From 220c66258f76f10b8933126c87acddfd0636be8c Mon Sep 17 00:00:00 2001 From: Chintanippu Date: Wed, 31 Jul 2019 16:35:01 +0530 Subject: [PATCH] Extended Support of Smart Video Workshop for IoT Devcloud --- README.md | 58 +- object-detection/Devcloud/README.md | 5 + object-detection/Devcloud/ROIviewer.py | 139 ++ .../basic_end_to_end_object_detection.ipynb | 1351 +++++++++++++++++ object-detection/Devcloud/tutorial1.py | 250 +++ object-detection/Devcloud/tutorial1_job.sh | 31 + 6 files changed, 1805 insertions(+), 29 deletions(-) create mode 100644 object-detection/Devcloud/README.md create mode 100644 object-detection/Devcloud/ROIviewer.py create mode 100644 object-detection/Devcloud/basic_end_to_end_object_detection.ipynb create mode 100644 object-detection/Devcloud/tutorial1.py create mode 100644 object-detection/Devcloud/tutorial1_job.sh diff --git a/README.md b/README.md index cb18ea84..ff1c8467 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ -# Optimized Inference at the Edge with Intel® Tools and Technologies -This workshop will walk you through a computer vision workflow using the latest Intel® technologies and comprehensive toolkits including support for deep learning algorithms that help accelerate smart video applications. You will learn how to optimize and improve performance with and without external accelerators and utilize tools to help you identify the best hardware configuration for your needs. This workshop will also outline the various frameworks and topologies supported by Intel® accelerator tools. +# Optimized Inference at the Edge with Intel® Tools and Technologies +This workshop will walk you through a computer vision workflow using the latest Intel® technologies and comprehensive toolkits including support for deep learning algorithms that help accelerate smart video applications. You will learn how to optimize and improve performance with and without external accelerators and utilize tools to help you identify the best hardware configuration for your needs. This workshop will also outline the various frameworks and topologies supported by Intel® accelerator tools. ## How to Get Started - -> :warning: For the in-class training, the hardware and software setup part has already been done on the workshop hardware. In-class training participants should directly move to Workshop Agenda section. + +> :warning: For the in-class training, the hardware and software setup part has already been done on the workshop hardware. In-class training participants should directly move to Workshop Agenda section. In order to use this workshop content, you will need to setup your hardware and install the Intel® Distribution of OpenVINO™ toolkit for infering your computer vision application. ### 1. Hardware requirements The hardware requirements are mentioned in the System Requirement section of the [install guide](https://software.intel.com/en-us/articles/OpenVINO-Install-Linux) ### 2. Operating System -These labs have been validated on Ubuntu* 16.04 OS. +These labs have been validated on Ubuntu* 16.04 OS. ### 3. Software installation steps -#### a). Install Intel® Distribution of OpenVINO™ toolkit +#### a). Install Intel® Distribution of OpenVINO™ toolkit Use steps described in the [install guide](https://software.intel.com/en-us/articles/OpenVINO-Install-Linux) -to install the Intel® Distribution of OpenVINO™ toolkit, configure Model Optimizer, run the demos, additional steps to install Intel® Media SDK and OpenCL™ mentioned in the the guide. +to install the Intel® Distribution of OpenVINO™ toolkit, configure Model Optimizer, run the demos, additional steps to install Intel® Media SDK and OpenCL™ mentioned in the the guide. #### b). Install required packages sudo apt install git @@ -23,30 +23,30 @@ to install the Intel® Distribution of OpenVINO™ toolkit, configure Model Opti sudo apt install libgflags-dev sudo pip3 install opencv-python sudo pip3 install cogapp - + #### c). Run the demo scipts and compile samples -Delete $HOME/inference_engine_samples folder if it already exists. +Delete $HOME/inference_engine_samples folder if it already exists. rm -rf $HOME/inference_engine_samples - -Run demo scripts (any one of them or both if you want to both the demos) which will generate the folder $HOME/inference_engine_samples with the current Intel® Distribution of OpenVINO™ toolkit built. + +Run demo scripts (any one of them or both if you want to both the demos) which will generate the folder $HOME/inference_engine_samples with the current Intel® Distribution of OpenVINO™ toolkit built. cd /opt/intel/openvino/deployment_tools/demo ./demo_squeezenet_download_convert_run.sh ./demo_security_barrier_camera.sh - + sudo chown -R username.username $HOME/inference_engine_samples_build cd $HOME/inference_engine_samples_build make - + #### d). Download models using model downloader scripts in Intel® Distribution of OpenVINO™ toolkit installed folder - - Install python3 (version 3.5.2 or newer) + - Install python3 (version 3.5.2 or newer) - Install yaml and requests modules with command: sudo -E pip3 install pyyaml requests - + - Run model downloader script to download example deep learning models - + cd /opt/intel/openvino/deployment_tools/tools/model_downloader sudo python3 downloader.py --name mobilenet-ssd,ssd300,ssd512,squeezenet1.1,face-detection-retail-0004,face-detection-retail-0004-fp16,age-gender-recognition-retail-0013,age-gender-recognition-retail-0013-fp16,head-pose-estimation-adas-0001,head-pose-estimation-adas-0001-fp16,emotions-recognition-retail-0003,emotions-recognition-retail-0003-fp16,facial-landmarks-35-adas-0002,facial-landmarks-35-adas-0002-fp16 @@ -96,10 +96,10 @@ sudo chown username.username -R /opt/intel/workshop/ 7. It opens in default browser, locate the required jupyter notebook (.ipynb) file and double click on it to open and run. -> :warning: This workshop content has been validated with Intel® Distribution of OpenVINO™ toolkit version R1 (openvino_toolkit_2019.1.094). +> :warning: This workshop content has been validated with Intel® Distribution of OpenVINO™ toolkit version R1 (openvino_toolkit_2019.1.094). + - ## Workshop Agenda * **Smart Video/Computer Vision Tools Overview** - Slides - [Introduction to Smart Video Tools](./presentations/01-Introduction-to-Intel-Smart-Video-Tools.pdf) @@ -107,11 +107,11 @@ sudo chown username.username -R /opt/intel/workshop/ * **Training a Deep Learning Model** - Slides - [Training a Deep Learning Model](./presentations/DL_training_model.pdf) - Lab - Training a Deep Learning Model [[Default](./dl-model-training/README.md)] [[Python](./dl-model-training/Python/Deep_Learning_Tutorial.ipynb)] - + * **Basic End to End Object Detection Inference Example** - Slides - [Basic End to End Object Detection Example](./presentations/02-03_Basic-End-to-End-Object-Detection-Example.pdf) - Lab Setup - [Lab Setup Instructions](./Lab_setup.md) - - Lab - Basic End to End Object Detection Example [[C++](./object-detection/README.md)] [[Python](./object-detection/Python/basic_end_to_end_object_detection.ipynb)] + - Lab - Basic End to End Object Detection Example [[C++](./object-detection/README.md)] [[Python](./object-detection/Python/basic_end_to_end_object_detection.ipynb)] [[Devcloud](./object-detection/Devcloud/basic_end_to_end_object_detection.ipynb)] - Lab - Tensor Flow example [[C++](./advanced-video-analytics/tensor_flow.md)] [[Python](./object-detection/Python/Tensor_Flow_example.ipynb)] - Lab - [Object Detection with YOLOv3* model](./object-detection/README_yolov3.md) @@ -120,15 +120,15 @@ sudo chown username.username -R /opt/intel/workshop/ * **HW Acceleration with Intel® Movidius™ Neural Compute Stick** - Lab - HW Acceleration with Intel® Movidius™ Neural Compute Stick [[C++](./HW-Acceleration-with-Movidious-NCS/README.md)] [[Python](./HW-Acceleration-with-Movidious-NCS/Python/HW_Acceleration_with_Movidius_NCS.ipynb)] - + * **FPGA Inference Accelerator** - Slides - [HW Acceleration with Intel® FPGA](./presentations/FPGA.pdf) -* **Optimization Tools and Techniques** +* **Optimization Tools and Techniques** - Slides - [Optimization Tools and Techniques](./presentations/04-05_Optimization_and_advanced_analytics.pdf) - Lab 1 - Optimization Tools and Techniques [[C++](./optimization-tools-and-techniques/README.md)] [[Python](./optimization-tools-and-techniques/Python/optimization_tools_and_techniques.ipynb)] - Lab 2- [Intel® VTune™ Amplifier tutorial](./optimization-tools-and-techniques/README_VTune.md) - + * **Advanced Video Analytics** - Lab - Multiple models usage example [[C++](./advanced-video-analytics/multiple_models.md)] [[Python](./advanced-video-analytics/Python/advanced_video_analytics.ipynb)] > #### Disclaimer -> Intel and the Intel logo are trademarks of Intel Corporation or its subsidiaries in the U.S. and/or other countries. - +> Intel and the Intel logo are trademarks of Intel Corporation or its subsidiaries in the U.S. and/or other countries. + > *Other names and brands may be claimed as the property of others diff --git a/object-detection/Devcloud/README.md b/object-detection/Devcloud/README.md new file mode 100644 index 00000000..de0b87dd --- /dev/null +++ b/object-detection/Devcloud/README.md @@ -0,0 +1,5 @@ +## Extend the support of Smart Video Workshop for IoT Devcloud +### Lab - Basic End to End Object Detection Example +1. Steps to run the Lab - Basic End to End Object Detection on Dev Cloud +- Download the basic_end_to_end_object_detection.ipynb file and replace it in $HOME/Reference-samples/smart-video-workshop/object-detection/Python/ folder with the existing file. +- Download the updated tutorial1.py, ROIviewer.py files and replace it in $HOME/Reference-samples/smart-video-workshop/object-detection/Python/ folder with existing python files. diff --git a/object-detection/Devcloud/ROIviewer.py b/object-detection/Devcloud/ROIviewer.py new file mode 100644 index 00000000..4bd3e7b2 --- /dev/null +++ b/object-detection/Devcloud/ROIviewer.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import sys +import os +from argparse import ArgumentParser +import cv2 +import logging as log +import struct +import collections + + + +def build_argparser(): + parser = ArgumentParser() + parser.add_argument("-i", "--input", + help="Path to video file or image. 'cam' for capturing video stream from camera", required=True, + type=str) + parser.add_argument("-l", "--labels", help="Labels mapping file", required=True, type=str) + parser.add_argument("--ROIfile",help="Path to ROI file.",default="ROIs.txt",type=str) + parser.add_argument("-b", help="Batch size", default=0, type=int) + parser.add_argument('-o', '--output_dir', + help='Location to store the results of the processing', + default=None, + required=True, + type=str) + return parser + +class ROI_data_type: + framenum="" + labelnum="" + confidence="" + xmin="" + ymin="" + xmax="" + ymax="" + +def main(): + log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.INFO, stream=sys.stdout) + args = build_argparser().parse_args() + batch=args.b + ROIs = collections.deque() + assert os.path.isfile(args.ROIfile), "Specified ROIs.txt file doesn't exist" + + fin=open("ROIs.txt",'r') + for l in fin: + R=ROI_data_type() + batchnum,R.framenum,R.labelnum,R.confidence,R.xmin,R.ymin,R.xmax,R.ymax=l.split() + if int(batchnum)==batch: + ROIs.append(R) + + if args.input == 'cam': + input_stream = 0 + else: + input_stream = args.input + assert os.path.isfile(args.input), "Specified input file doesn't exist" + + # print("opening", args.input," batchnum ",args.b,"\n") + + cap = cv2.VideoCapture(input_stream) + width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + fps = int(cap.get(cv2.CAP_PROP_FPS)) + out = cv2.VideoWriter(os.path.join(args.output_dir, "cars_output.mp4"),0x00000021,fps,(width,height)) + + if not cap.isOpened(): + print("could not open input video file") + framenum=0 + if len(ROIs)>1: + R=ROIs[0] + else: + print("empty ROI file"); + if args.labels: + with open(args.labels, 'r') as f: + labels_map = [x.strip() for x in f] + else: + labels_map = None + + while True: + ret, frame = cap.read() + if not ret: + break + ncols=cap.get(3) + nrows=cap.get(4) + while int(R.framenum)1: + ROIs.popleft() + R=ROIs[0]; + else: + break + while int(R.framenum)==framenum: + xmin = int(float(R.xmin) * float(ncols)) + ymin = int(float(R.ymin) * float(nrows)) + xmax = int(float(R.xmax) * float(ncols)) + ymax = int(float(R.ymax) * float(nrows)) + + class_id=int(float(R.labelnum)+1) + cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (0, 255, 0),4,16,0) + + if len(labels_map)==0: + templabel=int(float(R.labelnum))+":"+int(R.confidence*100.0) + print(templabel) + else: + templabel=str(labels_map[int(float(R.labelnum))])+":"+str(int(float(R.confidence)*100.0)) + + cv2.rectangle(frame, (xmin, ymin+32), (xmax, ymin), (155, 155, 155),-1,0) + cv2.putText(frame, templabel, (xmin, ymin+24), cv2.FONT_HERSHEY_COMPLEX, 1.1, (0, 0, 0),3) + + if len(ROIs)>1: + ROIs.popleft() + R=ROIs[0] + else: + break + time = (1/20) + out.write(frame) + #cv2.imshow("Detection Results", frame) + if cv2.waitKey(30)>=0: + break + if len(ROIs)<=1: + break + framenum+=1 + cap.release() + +main() + diff --git a/object-detection/Devcloud/basic_end_to_end_object_detection.ipynb b/object-detection/Devcloud/basic_end_to_end_object_detection.ipynb new file mode 100644 index 00000000..62854777 --- /dev/null +++ b/object-detection/Devcloud/basic_end_to_end_object_detection.ipynb @@ -0,0 +1,1351 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "# Object detection with Intel® Distribution of OpenVINO™ toolkit\n", + "\n", + "This tutorial uses a Single Shot MultiBox Detector (SSD) on a trained mobilenet-ssd* model to walk you through the basic steps of using two key components of the Intel® Distribution of OpenVINO™ toolkit: Model Optimizer and Inference Engine.\n", + "\n", + "Model Optimizer is a cross-platform command-line tool that takes pre-trained deep learning models and optimizes them for performance/space using conservative topology transformations. It performs static model analysis and adjusts deep learning models for optimal execution on end-point target devices.\n", + "\n", + "Inference is the process of using a trained neural network to interpret data such as images. This lab feeds a short video of cars, frame-by-frame, to the Inference Engine which subsequently utilizes an optimized trained neural network to detect cars.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 0: Set Up\n", + "\n", + "### 0.1: Import Dependicies\n", + "\n", + "Execute the following cell to import Python dependencies needed for displaying the results in this notebook (tip: select the cell and use Ctrl+Enter to execute the cell)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import HTML\n", + "import matplotlib.pyplot as plt\n", + "import os\n", + "import time\n", + "import sys \n", + "from pathlib import Path\n", + "sys.path.insert(0, str(Path().resolve().parent.parent))\n", + "sys.path.insert(0,os.path.join(os.environ['HOME'],'Reference-samples/iot-devcloud/demoTools/'))\n", + "from demoutils import *\n", + "from openvino.inference_engine import IEPlugin, IENetwork\n", + "import cv2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 0.2 Build the OpenVINO Samples\n", + "\n", + "Execute the following cell to build the samples." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-- Looking for C++ include unistd.h\n", + "-- Looking for C++ include unistd.h - found\n", + "-- Looking for C++ include stdint.h\n", + "-- Looking for C++ include stdint.h - found\n", + "-- Looking for C++ include sys/types.h\n", + "-- Looking for C++ include sys/types.h - found\n", + "-- Looking for C++ include fnmatch.h\n", + "-- Looking for C++ include fnmatch.h - found\n", + "-- Looking for C++ include stddef.h\n", + "-- Looking for C++ include stddef.h - found\n", + "-- Check size of uint32_t\n", + "-- Check size of uint32_t - done\n", + "-- Looking for strtoll\n", + "-- Looking for strtoll - found\n", + "-- Found InferenceEngine: /opt/intel/openvino_2019.1.094/deployment_tools/inference_engine/lib/intel64/libinference_engine.so (Required is at least version \"1.6\") \n", + "-- Performing Test HAVE_CPUID_INFO\n", + "-- Performing Test HAVE_CPUID_INFO - Success\n", + "-- Host CPU features:\n", + "-- 3DNOW not supported\n", + "-- 3DNOWEXT not supported\n", + "-- ABM not supported\n", + "-- ADX supported\n", + "-- AES supported\n", + "-- AVX supported\n", + "-- AVX2 supported\n", + "-- AVX512CD supported\n", + "-- AVX512F supported\n", + "-- AVX512ER not supported\n", + "-- AVX512PF not supported\n", + "-- BMI1 supported\n", + "-- BMI2 supported\n", + "-- CLFSH supported\n", + "-- CMPXCHG16B supported\n", + "-- CX8 supported\n", + "-- ERMS supported\n", + "-- F16C supported\n", + "-- FMA supported\n", + "-- FSGSBASE supported\n", + "-- FXSR supported\n", + "-- HLE supported\n", + "-- INVPCID supported\n", + "-- LAHF supported\n", + "-- LZCNT supported\n", + "-- MMX supported\n", + "-- MMXEXT not supported\n", + "-- MONITOR supported\n", + "-- MOVBE supported\n", + "-- MSR supported\n", + "-- OSXSAVE supported\n", + "-- PCLMULQDQ supported\n", + "-- POPCNT supported\n", + "-- PREFETCHWT1 not supported\n", + "-- RDRAND supported\n", + "-- RDSEED supported\n", + "-- RDTSCP supported\n", + "-- RTM supported\n", + "-- SEP supported\n", + "-- SHA not supported\n", + "-- SSE supported\n", + "-- SSE2 supported\n", + "-- SSE3 supported\n", + "-- SSE4.1 supported\n", + "-- SSE4.2 supported\n", + "-- SSE4a not supported\n", + "-- SSSE3 supported\n", + "-- SYSCALL supported\n", + "-- TBM not supported\n", + "-- XOP not supported\n", + "-- XSAVE supported\n", + "-- TBB include: /opt/intel/openvino_2019.1.094/deployment_tools/inference_engine/external/tbb/include\n", + "-- TBB Release lib: /opt/intel/openvino_2019.1.094/deployment_tools/inference_engine/external/tbb/lib/libtbb.so\n", + "-- TBB Debug lib: /opt/intel/openvino_2019.1.094/deployment_tools/inference_engine/external/tbb/lib/libtbb_debug.so\n", + "-- Looking for pthread.h\n", + "-- Looking for pthread.h - found\n", + "-- Looking for pthread_create\n", + "-- Looking for pthread_create - not found\n", + "-- Looking for pthread_create in pthreads\n", + "-- Looking for pthread_create in pthreads - not found\n", + "-- Looking for pthread_create in pthread\n", + "-- Looking for pthread_create in pthread - found\n", + "-- Found Threads: TRUE \n", + "-- Configuring done\n", + "-- Generating done\n", + "-- Build files have been written to: /home/u28679/inference_engine_samples_build\n", + "[ 1%] Built target hello_classification\n", + "[ 2%] Built target hello_autoresize_classification\n", + "[ 4%] Built target hello_request_classification\n", + "[ 10%] Built target format_reader\n", + "[ 10%] Built target gflags_nothreads_static\n", + "[ 33%] Built target ie_cpu_extension\n", + "[ 35%] Built target end2end_video_analytics_opencv\n", + "[ 35%] Built target lenet_network_graph_builder\n", + "[ 36%] Built target security_barrier_camera_demo\n", + "[ 39%] Built target object_detection_demo\n", + "[ 41%] Built target human_pose_estimation_demo\n", + "[ 42%] Built target benchmark_app\n", + "[ 42%] Built target object_detection_demo_ssd_async\n", + "[ 48%] Built target validation_app\n", + "[ 50%] Built target crossroad_camera_demo\n", + "[ 50%] Built target speech_sample\n", + "[ 55%] Built target common\n", + "[ 57%] Built target object_detection_sample_ssd\n", + "[ 58%] Built target segmentation_demo\n", + "[ 68%] Built target calibration_tool\n", + "[ 68%] Built target interactive_face_detection_demo\n", + "[ 69%] Built target end2end_video_analytics_ie\n", + "[ 70%] Built target mask_rcnn_demo\n", + "[ 76%] Built target pedestrian_tracker_demo\n", + "[ 77%] Built target perfcheck\n", + "[ 78%] Built target hello_shape_infer_ssd\n", + "[ 81%] Built target classification_sample\n", + "[ 81%] Built target style_transfer_sample\n", + "[ 82%] Built target object_detection_demo_yolov3_async\n", + "[ 83%] Built target classification_sample_async\n", + "[ 87%] Built target text_detection_demo\n", + "[ 94%] Built target multi-channel-face-detection-demo\n", + "[ 95%] Built target smart_classroom_demo\n", + "[ 96%] Built target super_resolution_demo\n", + "[100%] Built target multi-channel-human-pose-estimation-demo\n", + "\n", + "Build completed, you can find binaries for all samples in the /home/u28679/inference_engine_samples_build/intel64/Release subfolder.\n", + "\n" + ] + } + ], + "source": [ + "! /opt/intel/openvino/deployment_tools/inference_engine/samples/build_samples.sh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 0.3 Run model downloader script to download example deep learning models" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "###############|| Downloading topologies ||###############\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.prototxt\n", + "... 100%, 28 KB, 60595 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.caffemodel\n", + "... 100%, 22605 KB, 22457 KB/s, 1 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/ssd/300/caffe/ssd300.prototxt\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/ssd/300/caffe/ssd300.caffemodel\n", + "... 100%, 95497 KB, 26735 KB/s, 3 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/ssd/512/caffe/ssd512.prototxt\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/ssd/512/caffe/ssd512.caffemodel\n", + "... 100%, 98624 KB, 27072 KB/s, 3 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/classification/squeezenet/1.1/caffe/squeezenet1.1.prototxt\n", + "... 100%, 9 KB, 37544 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/classification/squeezenet/1.1/caffe/squeezenet1.1.caffemodel\n", + "... 100%, 4834 KB, 24947 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.xml\n", + "... 100%, 47 KB, 2043 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004.bin\n", + "... 100%, 2297 KB, 25024 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.xml\n", + "... 100%, 47 KB, 19804 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_detection/face/sqnet1.0modif-ssd/0004/dldt/face-detection-retail-0004-fp16.bin\n", + "... 100%, 1148 KB, 30517 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013.xml\n", + "... 100%, 14 KB, 45024 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013.bin\n", + "... 100%, 8351 KB, 28837 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013-fp16.xml\n", + "... 100%, 14 KB, 17749 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/age_gender/dldt/age-gender-recognition-retail-0013-fp16.bin\n", + "... 100%, 4175 KB, 28739 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001.xml\n", + "... 100%, 17 KB, 42357 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001.bin\n", + "... 100%, 7466 KB, 29127 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001-fp16.xml\n", + "... 100%, 17 KB, 37907 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/headpose/vanilla_cnn/dldt/head-pose-estimation-adas-0001-fp16.bin\n", + "... 100%, 3733 KB, 29363 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003.xml\n", + "... 100%, 19 KB, 855 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003.bin\n", + "... 100%, 9697 KB, 28813 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003-fp16.xml\n", + "... 100%, 19 KB, 42664 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Retail/object_attributes/emotions_recognition/0003/dldt/emotions-recognition-retail-0003-fp16.bin\n", + "... 100%, 4848 KB, 28982 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/facial_landmarks/custom-35-facial-landmarks/dldt/facial-landmarks-35-adas-0002.xml\n", + "... 100%, 108 KB, 2290 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/facial_landmarks/custom-35-facial-landmarks/dldt/facial-landmarks-35-adas-0002.bin\n", + "... 100%, 17950 KB, 28869 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/facial_landmarks/custom-35-facial-landmarks/dldt/facial-landmarks-35-adas-0002-fp16.xml\n", + "... 100%, 108 KB, 2326 KB/s, 0 seconds passed\n", + "\n", + "========= Downloading /home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/Transportation/object_attributes/facial_landmarks/custom-35-facial-landmarks/dldt/facial-landmarks-35-adas-0002-fp16.bin\n", + "... 100%, 8975 KB, 28801 KB/s, 0 seconds passed\n", + "\n", + "\n", + "###############|| Post processing ||###############\n", + "\n", + "========= Deleting \"save_output_param\" from ssd300.prototxt =========\n", + "========= Deleting \"save_output_param\" from ssd512.prototxt =========\n", + "========= Changing input dimensions in squeezenet1.1.prototxt =========\n" + ] + } + ], + "source": [ + "! python3 /opt/intel/openvino/deployment_tools/tools/model_downloader/downloader.py --name mobilenet-ssd,ssd300,ssd512,squeezenet1.1,face-detection-retail-0004,face-detection-retail-0004-fp16,age-gender-recognition-retail-0013,age-gender-recognition-retail-0013-fp16,head-pose-estimation-adas-0001,head-pose-estimation-adas-0001-fp16,emotions-recognition-retail-0003,emotions-recognition-retail-0003-fp16,facial-landmarks-35-adas-0002,facial-landmarks-35-adas-0002-fp16" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + "## Step 1: Optimize a deep-learning model using the Model Optimizer (MO)\n", + "\n", + "In this section, you will use the Model Optimizer to convert a trained model to two Intermediate Representation (IR) files (one .bin and one .xml). The Inference Engine requires this model conversion so that it can use the IR as input and achieve optimum performance on Intel hardware.\n", + "\n", + "\n", + "### 1.1 Create a directory to store IR files" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "! mkdir -p $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32 \n", + "! mkdir -p $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " \n", + "### 1.2 Run the Model Optimizer on the pretrained Caffe* model. This step generates one .xml file and one .bin file and places both files in the tutorial samples directory (located here: /object-detection/)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model Optimizer arguments:\n", + "Common parameters:\n", + "\t- Path to the Input Model: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.caffemodel\n", + "\t- Path for generated IR: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32\n", + "\t- IR output name: \tmobilenet-ssd\n", + "\t- Log level: \tERROR\n", + "\t- Batch: \tNot specified, inherited from the model\n", + "\t- Input layers: \tNot specified, inherited from the model\n", + "\t- Output layers: \tNot specified, inherited from the model\n", + "\t- Input shapes: \tNot specified, inherited from the model\n", + "\t- Mean values: \t[127,127,127]!\n", + "\t- Scale values: \tNot specified\n", + "\t- Scale factor: \t256.0\n", + "\t- Precision of IR: \tFP32\n", + "\t- Enable fusing: \tTrue\n", + "\t- Enable grouped convolutions fusing: \tTrue\n", + "\t- Move mean values to preprocess section: \tFalse\n", + "\t- Reverse input channels: \tFalse\n", + "Caffe specific parameters:\n", + "\t- Enable resnet optimization: \tTrue\n", + "\t- Path to the Input prototxt: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.prototxt\n", + "\t- Path to CustomLayersMapping.xml: \tDefault\n", + "\t- Path to a mean file: \tNot specified\n", + "\t- Offsets for a mean file: \tNot specified\n", + "Model Optimizer version: \t2019.1.0-341-gc9b66a2\n", + "\n", + "[ SUCCESS ] Generated IR model.\n", + "[ SUCCESS ] XML file: /home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32/mobilenet-ssd.xml\n", + "[ SUCCESS ] BIN file: /home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32/mobilenet-ssd.bin\n", + "[ SUCCESS ] Total execution time: 4.47 seconds. \n" + ] + } + ], + "source": [ + "! python3 /opt/intel/openvino/deployment_tools/model_optimizer/mo_caffe.py --input_model $HOME/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.caffemodel -o $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32 --scale 256 --mean_values [127,127,127]! " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model Optimizer arguments:\n", + "Common parameters:\n", + "\t- Path to the Input Model: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.caffemodel\n", + "\t- Path for generated IR: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16\n", + "\t- IR output name: \tmobilenet-ssd\n", + "\t- Log level: \tERROR\n", + "\t- Batch: \tNot specified, inherited from the model\n", + "\t- Input layers: \tNot specified, inherited from the model\n", + "\t- Output layers: \tNot specified, inherited from the model\n", + "\t- Input shapes: \tNot specified, inherited from the model\n", + "\t- Mean values: \t[127,127,127]\n", + "\t- Scale values: \tNot specified\n", + "\t- Scale factor: \t256.0\n", + "\t- Precision of IR: \tFP16\n", + "\t- Enable fusing: \tTrue\n", + "\t- Enable grouped convolutions fusing: \tTrue\n", + "\t- Move mean values to preprocess section: \tFalse\n", + "\t- Reverse input channels: \tFalse\n", + "Caffe specific parameters:\n", + "\t- Enable resnet optimization: \tTrue\n", + "\t- Path to the Input prototxt: \t/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.prototxt\n", + "\t- Path to CustomLayersMapping.xml: \tDefault\n", + "\t- Path to a mean file: \tNot specified\n", + "\t- Offsets for a mean file: \tNot specified\n", + "Model Optimizer version: \t2019.1.0-341-gc9b66a2\n", + "\n", + "[ SUCCESS ] Generated IR model.\n", + "[ SUCCESS ] XML file: /home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16/mobilenet-ssd.xml\n", + "[ SUCCESS ] BIN file: /home/u28679/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16/mobilenet-ssd.bin\n", + "[ SUCCESS ] Total execution time: 4.37 seconds. \n" + ] + } + ], + "source": [ + "! cd /opt/intel/openvino/deployment_tools/model_optimizer && python3 mo_caffe.py --input_model $HOME/Reference-samples/smart-video-workshop/object-detection/Python/object_detection/common/mobilenet-ssd/caffe/mobilenet-ssd.caffemodel -o $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16 --scale 256 --mean_values [127,127,127] --data_type FP16\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + " Note: Although this tutorial uses Single Shot MultiBox Detector (SSD) on a trained mobilenet-ssd* model, the Inference Engine is compatible with other neural network architectures, such as AlexNet*, GoogleNet*, MxNet* etc.\n", + "\n", + "\n", + "The Model Optimizer converts a pretrained Caffe* model to make it compatible with the Intel Inference Engine and optimizes it for Intel® architecture. These are the files you would include with your C++ application to apply inference to visual data.\n", + "\n", + " Note: If you continue to train or make changes to the Caffe* model, you would then need to re-run the Model Optimizer on the updated model.\n", + "\n", + "### 1.3 Navigate to the tutorial sample model directory and verify creation of the optimized model files (the IR files)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mobilenet-ssd.bin mobilenet-ssd.mapping mobilenet-ssd.xml\r\n" + ] + } + ], + "source": [ + "! cd $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP32 && ls" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mobilenet-ssd.bin mobilenet-ssd.mapping mobilenet-ssd.xml\r\n" + ] + } + ], + "source": [ + "! cd $HOME/Reference-samples/smart-video-workshop/object-detection/mobilenet-ssd/FP16 && ls" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Source your environmental variables" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[setupvars.sh] OpenVINO environment initialized\r\n" + ] + } + ], + "source": [ + "! /opt/intel/openvino/bin/setupvars.sh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + "#### Download the test video file to the object-detection folder.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Execute the following cell to download the test video file " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--2019-07-31 02:48:53-- https://pixabay.com/en/videos/download/video-1900_source.mp4\n", + "Resolving pixabay.com (pixabay.com)... 104.18.141.87, 104.18.82.97, 2606:4700::6812:8d57, ...\n", + "Connecting to pixabay.com (pixabay.com)|104.18.141.87|:443... connected.\n", + "HTTP request sent, awaiting response... 301 Moved Permanently\n", + "Location: /videos/download/video-1900_source.mp4 [following]\n", + "--2019-07-31 02:48:53-- https://pixabay.com/videos/download/video-1900_source.mp4\n", + "Reusing existing connection to pixabay.com:443.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://player.vimeo.com/play/465539722?s=151662242_1564603862_17bfd4e016f887474843f09977d21f5c&loc=external&context=Vimeo%5CController%5CApi%5CResources%5CVideoController.&download=1&filename=Cars%2B-%2B1900source.mp4 [following]\n", + "--2019-07-31 02:48:53-- https://player.vimeo.com/play/465539722?s=151662242_1564603862_17bfd4e016f887474843f09977d21f5c&loc=external&context=Vimeo%5CController%5CApi%5CResources%5CVideoController.&download=1&filename=Cars%2B-%2B1900source.mp4\n", + "Resolving player.vimeo.com (player.vimeo.com)... 151.101.188.217\n", + "Connecting to player.vimeo.com (player.vimeo.com)|151.101.188.217|:443... connected.\n", + "HTTP request sent, awaiting response... 302 Found\n", + "Location: https://gcs-vimeo.akamaized.net/exp=1564580933~acl=%2A%2F465539722%2A~hmac=38934eeb9b590c5e0f3cc0ab7a3c7215a06ba4ca72eb8837b604c94a3e43f9d7/vimeo-prod-src-cl-us-legacy/videos/465539722?download=1&filename=Cars+-+1900.mp4&source=1 [following]\n", + "--2019-07-31 02:48:53-- https://gcs-vimeo.akamaized.net/exp=1564580933~acl=%2A%2F465539722%2A~hmac=38934eeb9b590c5e0f3cc0ab7a3c7215a06ba4ca72eb8837b604c94a3e43f9d7/vimeo-prod-src-cl-us-legacy/videos/465539722?download=1&filename=Cars+-+1900.mp4&source=1\n", + "Resolving gcs-vimeo.akamaized.net (gcs-vimeo.akamaized.net)... 23.215.102.35, 23.215.102.64\n", + "Connecting to gcs-vimeo.akamaized.net (gcs-vimeo.akamaized.net)|23.215.102.35|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 47043073 (45M) [video/mp4]\n", + "Saving to: ‘/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/video-1900_source.mp4’\n", + "\n", + "video-1900_source.m 100%[===================>] 44.86M 27.8MB/s in 1.6s \n", + "\n", + "2019-07-31 02:48:55 (27.8 MB/s) - ‘/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/video-1900_source.mp4’ saved [47043073/47043073]\n", + "\n" + ] + } + ], + "source": [ + "! wget 'https://pixabay.com/en/videos/download/video-1900_source.mp4' -P $HOME/Reference-samples/smart-video-workshop/object-detection/Python/ " + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "! mv video-1900_source.mp4 cars_1900.mp4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Input Video\n", + "\n", + "Execute the following cell to create a symlink and view the input video." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ln: '/home/u28679/Reference-samples/smart-video-workshop/object-detection/Python/cars_1900.mp4' and './cars_1900.mp4' are the same file\r\n" + ] + }, + { + "data": { + "text/html": [ + "

Cars video

\n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "!ln -sf $HOME/Reference-samples/smart-video-workshop/object-detection/Python/cars_1900.mp4 \n", + "videoHTML('Cars video', ['cars_1900.mp4'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Inference on a video\n", + "\n", + "The Python code takes in command line arguments for video, model and so on.\n", + "\n", + "**Command line arguments options and how they are interpreted in the application source code**\n", + "\n", + "```\n", + "SAMPLEPATH=$PBS_O_WORKDIR\n", + "python3 tutorial1.py -m ${SAMPLEPATH}/../mobilenet-ssd/$3/mobilenet-ssd.xml \\\n", + " -i ${INPUT_FILE} \\\n", + " -o ${RESULTS_PATH} \\\n", + " -d ${DEVICE} \\\n", + " -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_sse4.so\n", + "\n", + "```\n", + "##### The description of the arguments used in the argument parser is the command line executable equivalent.\n", + "* -m location of the **mobilenet-ssd** pre-trained model which has been pre-processed using the **model optimizer**.\n", + " There is automated support built in this argument to support both FP32 and FP16 models targeting different hardware\n", + " (**Note** we are using mobilenet-ssd in this example. However, OpenVINO's Inference Engine is compatible with other neural network architectures such as AlexNet*, GoogleNet*, SqueezeNet* etc.,) \n", + "* -i location of the input video stream\n", + "* -o location where the output file with inference needs to be stored (results/[device])\n", + "* -d type of Hardware Acceleration (CPU, GPU, MYRIAD, HDDL or HETERO:FPGA,CPU)\n", + "* -l absolute path to the shared library and is currently optimized for core/xeon (/opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_sse4.so)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.1: Create a Job File\n", + "\n", + "All the code up to this point has been executed within the Jupyter Notebook instance running on a development node based on an Intel® Xeon® Scalable Processor, where the Notebook is allocated to a single core. To run inference on the entire video, you need more compute power. Run the workload on several DevCloud's edge compute nodes and then send work to the edge compute nodes by submitting jobs into a queue. For each job, specify the type of the edge compute server that must be allocated for the job." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To pass the specific variables to the Python code, we use the following arguments:\n", + "\n", + "* `-m`      location of the optimized **MobileNet-SSD** model's XML\n", + "* `-i`      location of the input video\n", + "* `-o`      output directory\n", + "* `-d`      hardware device type (CPU, GPU, MYRIAD)\n", + "* `-l`      path to the CPU extension library" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The job file will be executed directly on the edge compute node." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting tutorial1_job.sh\n" + ] + } + ], + "source": [ + "%%writefile tutorial1_job.sh\n", + "ME=`basename $0`\n", + "\n", + "# The default path for the job is your home directory, so we change directory to where the files are.\n", + "cd $PBS_O_WORKDIR\n", + "DEVICE=$2\n", + "FP_MODEL=$3\n", + "INPUT_FILE=$4\n", + "RESULTS_BASE=$1\n", + "\n", + "\n", + "NN_MODEL=\"mobilenet-ssd.xml\"\n", + "RESULTS_PATH=\"${RESULTS_BASE}\"\n", + "mkdir -p $RESULTS_PATH\n", + "echo \"$ME is using results path $RESULTS_PATH\"\n", + "\n", + "if [ \"$DEVICE\" = \"HETERO:FPGA,CPU\" ]; then\n", + " # Environment variables and compilation for edge compute nodes with FPGAs\n", + " export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/altera/aocl-pro-rte/aclrte-linux64/\n", + " # Environment variables and compilation for edge compute nodes with FPGAs\n", + " source /opt/fpga_support_files/setup_env.sh\n", + " aocl program acl0 /opt/intel/openvino/bitstreams/a10_vision_design_bitstreams/2019R1_PL1_FP11_MobileNet_Clamp.aocx\n", + "fi\n", + " \n", + "# Running the object detection code\n", + "SAMPLEPATH=$PBS_O_WORKDIR\n", + "! python3 tutorial1.py -m ${SAMPLEPATH}/../mobilenet-ssd/${FP_MODEL}/${NN_MODEL} \\\n", + " -i $INPUT_FILE \\\n", + " -o $RESULTS_PATH \\\n", + " -d $DEVICE \\\n", + " -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_avx2.so" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.2: Understand how jobs are submitted into the queue\n", + "\n", + "Now that we have the job script, we can submit the jobs to edge compute nodes. In the IoT DevCloud, you can do this using the `qsub` command.\n", + "We can submit people_counter to several different types of edge compute nodes simultaneously or just one node at a time.\n", + "\n", + "There are three options of `qsub` command that we use for this:\n", + "- `-l` : this option let us select the number and the type of nodes using `nodes={node_count}:{property}`. \n", + "- `-F` : this option let us send arguments to the bash script. \n", + "- `-N` : this option let us name the job so that it is easier to distinguish between them.\n", + "\n", + "The `-F` flag is used to pass arguments to the job script.\n", + "The [tutorial1_job.sh](tutorial1_job.sh) takes in 4 arguments:\n", + "1. the path to the directory for the output video and performance stats\n", + "2. targeted device (e.g. CPU, GPU and MYRIAD.\n", + "3. the floating precision to use for inference\n", + "4. location of the input video stream\n", + "\n", + "The job scheduler uses the contents of `-F` flag as the argument to the job script.\n", + "\n", + "If you are curious to see the available types of nodes on the IoT DevCloud, run the following optional cell." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 35 idc001skl,compnode,iei,tank-870,intel-core,i5-6500te,skylake,intel-hd-530,ram8gb,1gbe\r\n", + " 15 idc002mx8,compnode,iei,tank-870,intel-core,i5-6500te,skylake,intel-hd-530,ram8gb,net1gbe,hddl-r,iei-mustang-v100-mx8\r\n", + " 18 idc003a10,compnode,iei,tank-870,intel-core,i5-6500te,skylake,intel-hd-530,ram8gb,net1gbe,hddl-f,iei-mustang-f100-a10\r\n", + " 23 idc004nc2,compnode,iei,tank-870,intel-core,i5-6500te,skylake,intel-hd-530,ram8gb,net1gbe,ncs,intel-ncs2\r\n", + " 10 idc006kbl,compnode,iei,tank-870,intel-core,i5-7500t,kaby-lake,intel-hd-630,ram8gb,net1gbe\r\n", + " 16 idc007xv5,compnode,iei,tank-870,intel-xeon,e3-1268l-v5,skylake,intel-hd-p530,ram32gb,net1gbe\r\n", + " 15 idc008u2g,compnode,up-squared,grove,intel-atom,e3950,apollo-lake,intel-hd-505,ram4gb,net1gbe,ncs,intel-ncs2\r\n", + " 1 idc009jkl,compnode,jwip,intel-core,i5-7500,kaby-lake,intel-hd-630,ram8gb,net1gbe\r\n", + " 1 idc010jal,compnode,jwip,intel-atom,e3950,apollo-lake,intel-hd-505,ram4gb,net1gbe\r\n" + ] + } + ], + "source": [ + "!pbsnodes | grep compnode | awk '{print $3}' | sort | uniq -c" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, the properties describe the node, and number on the left is the number of available nodes of that architecture.\n", + "\n", + "**Note**: If you want to use your own video, change the environment variable 'VIDEO' in the following cell from \"cars_1900.mp4\" to the full path of your uploaded video." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"VIDEO\"] = 'cars_1900.mp4'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.3: Job queue submission\n", + "\n", + "Each of the cells below will submit a job to different edge compute nodes.\n", + "The output of the cell is the `JobID` of your job, which you can use to track progress of a job.\n", + "\n", + "**Note** You can submit all jobs at once or follow one at a time. \n", + "\n", + "After submission, they will go into a queue and run as soon as the requested compute resources become available. \n", + "(tip: **shift+enter** will run the cell and automatically move you to the next cell. So you can hit **shift+enter** multiple times to quickly run multiple cells)\n", + "\n", + "#### Submitting to an edge compute node with an Intel® CPU\n", + "In the cell below, submit a job to IEI \n", + " Tank* 870-Q170 edge node with an Intel® Core™ i5-6500TE processor. The inference workload will run on the CPU." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "38638.c003\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1c1d7bab074a4e668014eb7ded5ecf74", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(FloatProgress(value=0.0, bar_style='info', description='Inference', style=ProgressStyle(descrip…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#Submit job to the queue\n", + "job_id_core = !qsub tutorial1_job.sh -l nodes=1:idc001skl:i5-6500te -F \"results/Core CPU FP32 $VIDEO \" -N obj_det_core\n", + "print(job_id_core[0]) \n", + "#Progress indicators\n", + "if job_id_core:\n", + " progressIndicator('results/Core', 'i_progress.txt', \"Inference\", 0, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***Wait!***\n", + "\n", + "Please wait for 1-2 minutes after running the above cell for inferencing.\n", + "\n", + "#### Execute the ROIViewer Sample\n", + "\n", + "For simplicity of the code and to put more focus on the performance number, video rendering with rectangle boxes for detected objects has been separated from tutorial1.py\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "! python3 ROIviewer.py -i $HOME/Reference-samples/smart-video-workshop/object-detection/Python/cars_1900.mp4 -l $HOME/Reference-samples/smart-video-workshop/object-detection/pascal_voc_classes.txt -o results/Core " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Submitting to an edge compute node with Intel® Core CPU and using the onboard Intel® GPU\n", + "In the following cell, we submit a job to IEI \n", + " Tank* 870-Q170 edge node with an Intel® Core i5-6500TE. The inference workload will run on the Intel® HD Graphics 530 card integrated with the CPU." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "38639.c003\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "352f89afb7e447cbb7537da4e989ebd8", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(FloatProgress(value=0.0, bar_style='info', description='Inference', style=ProgressStyle(descrip…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#Submit job to the queue\n", + "job_id_gpu = !qsub tutorial1_job.sh -l nodes=1:idc001skl:intel-hd-530 -F \" results/GPU GPU FP32 $VIDEO\" -N obj_det_gpu \n", + "print(job_id_gpu[0])\n", + "#Progress indicators\n", + "if job_id_gpu:\n", + " progressIndicator('results/GPU', 'i_progress.txt', \"Inference\", 0, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***Wait!***\n", + "\n", + "\n", + "Please wait for 1-2 minutes after running the above cell for inferencing.\n", + "\n", + "#### Execute the ROIViewer Sample\n", + "\n", + "For simplicity of the code and to put more focus on the performance number, video rendering with rectangle boxes for detected objects has been separated from tutorial1.py" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "! python3 ROIviewer.py -i $HOME/Reference-samples/smart-video-workshop/object-detection/Python/cars_1900.mp4 -l $HOME/Reference-samples/smart-video-workshop/object-detection/pascal_voc_classes.txt -o results/GPU " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Submitting to an edge compute node with Intel® NCS 2 (Neural Compute Stick 2)\n", + "In the following cell, we submit a job to IEI \n", + " Tank 870-Q170 edge node with an Intel Core i5-6500te CPU. The inference workload will run on an Intel Neural Compute Stick 2 installed in this node." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "38640.c003\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "83247476c5294dafb4a661861a3e0fc6", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(FloatProgress(value=0.0, bar_style='info', description='Inference', style=ProgressStyle(descrip…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#Submit job to the queue\n", + "job_id_ncs2 = !qsub tutorial1_job.sh -l nodes=1:idc004nc2:intel-ncs2 -F \"results/NCS2 MYRIAD FP16 $VIDEO \" -N obj_det_ncs2\n", + "print(job_id_ncs2[0]) \n", + "#Progress indicators\n", + "if job_id_ncs2:\n", + " progressIndicator('results/NCS2', 'i_progress.txt', \"Inference\", 0, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***Wait!***\n", + "\n", + "\n", + "Please wait for 1-2 minutes after running the above cell for inferencing.\n", + "\n", + "#### Execute the ROIViewer Sample\n", + "\n", + "For simplicity of the code and to put more focus on the performance number, video rendering with rectangle boxes for detected objects has been separated from tutorial1.py" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "! python3 ROIviewer.py -i $HOME/Reference-samples/smart-video-workshop/object-detection/Python/cars_1900.mp4 -l $HOME/Reference-samples/smart-video-workshop/object-detection/pascal_voc_classes.txt -o results/NCS2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.4 Check the Progress\n", + "\n", + "Check the progress of the jobs. `Q` status stands for `queued`, `R` for `running`. How long a job is being queued is dependent on number of the users. It should take up to 5 minutes for a job to run. If the job is no longer listed, it's done. " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f5328ed7659340c2bf5e900e34b0c530", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Output(layout=Layout(border='1px solid gray', width='100%'))" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f3c8a6d0509f4dc4abefa65c8edeaa75", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Button(description='Stop', style=ButtonStyle())" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "liveQstat()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You should see the jobs you have submitted (referenced by `Job ID` that gets displayed right after you submit the job in step 3.3).\n", + "There should also be an extra job in the queue \"jupyterhub\": this job runs your current Jupyter Notebook session.\n", + "\n", + "The 'S' column shows the current status. \n", + "- If it is in Q state, it is in the queue waiting for available resources. \n", + "- If it is in R state, it is running. \n", + "- If the job is no longer listed, it means it is completed.\n", + "\n", + "**Note**: Time spent in the queue depends on the number of users accessing the edge nodes. Once these jobs begin to run, they should take from 1 to 5 minutes to complete." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are the parameters used in the above cells to run the application:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: tutorial1.py [-h] -m MODEL -i INPUT [-l CPU_EXTENSION] [-pp PLUGIN_DIR]\r\n", + " [-d DEVICE] [--labels LABELS] [-pt PROB_THRESHOLD]\r\n", + " [-fr FR] [-b B] -o OUTPUT_DIR\r\n", + "\r\n", + "optional arguments:\r\n", + " -h, --help show this help message and exit\r\n", + " -m MODEL, --model MODEL\r\n", + " Path to an .xml file with a trained model.\r\n", + " -i INPUT, --input INPUT\r\n", + " Path to video file or image. 'cam' for capturing video\r\n", + " stream from camera\r\n", + " -l CPU_EXTENSION, --cpu_extension CPU_EXTENSION\r\n", + " MKLDNN (CPU)-targeted custom layers.Absolute path to a\r\n", + " shared library with the kernels impl.\r\n", + " -pp PLUGIN_DIR, --plugin_dir PLUGIN_DIR\r\n", + " Path to a plugin folder\r\n", + " -d DEVICE, --device DEVICE\r\n", + " Specify the target device to infer on; CPU, GPU, FPGA\r\n", + " or MYRIAD is acceptable. Demo will look for a suitable\r\n", + " plugin for device specified (CPU by default)\r\n", + " --labels LABELS Labels mapping file\r\n", + " -pt PROB_THRESHOLD, --prob_threshold PROB_THRESHOLD\r\n", + " Probability threshold for detections filtering\r\n", + " -fr FR maximum frames to process\r\n", + " -b B Batch size\r\n", + " -o OUTPUT_DIR, --output_dir OUTPUT_DIR\r\n", + " Location to store the results of the processing\r\n" + ] + } + ], + "source": [ + "! python3 tutorial1.py -h" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: ROIviewer.py [-h] -i INPUT -l LABELS [--ROIfile ROIFILE] [-b B] -o\r\n", + " OUTPUT_DIR\r\n", + "\r\n", + "optional arguments:\r\n", + " -h, --help show this help message and exit\r\n", + " -i INPUT, --input INPUT\r\n", + " Path to video file or image. 'cam' for capturing video\r\n", + " stream from camera\r\n", + " -l LABELS, --labels LABELS\r\n", + " Labels mapping file\r\n", + " --ROIfile ROIFILE Path to ROI file.\r\n", + " -b B Batch size\r\n", + " -o OUTPUT_DIR, --output_dir OUTPUT_DIR\r\n", + " Location to store the results of the processing\r\n" + ] + } + ], + "source": [ + "! python3 ROIviewer.py -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "## Step 4: View Results\n", + "\n", + "Once the jobs are completed, the queue system outputs the `stdout` and `stderr` streams of each job into files with names\n", + "`obj_det_{type}.o{JobID}` and `obj_det_{type}.e{JobID}`. Here, obj_det_{type} corresponds to the `-N` option of qsub. For example, `core` for Core CPU target.\n", + "\n", + "You can find the output video files inside the `results` directory. We wrote a short utility script that will display these videos within the notebook. See `demoutils.py` if you are interested in understanding further how the results are displayed in notebook. \n", + "\n", + "`obj_det_{type}.e{JobID}`\n", + "\n", + "(here, obj_det_{type} corresponds to the `-N` option of qsub).\n", + "\n", + "However, for this case, we may be more interested in the output video files. They are stored in mp4 format inside the `results/` directory.\n", + "We wrote a short utility script that will display these videos with in the notebook.\n", + "Run the cells below to display them.\n", + "See `demoutils.py` if you are interested in understanding further how the results are displayed in notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

IEI Tank (Intel Core CPU)

\n", + "

256 \n", + " frames processed in 2.52 \n", + " seconds

\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "videoHTML('IEI Tank (Intel Core CPU)', \n", + " ['results/Core/cars_output.mp4'], \n", + " 'results/Core/stats.txt')" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

IEI Intel GPU (Intel Core + Onboard GPU)

\n", + "

256 \n", + " frames processed in 2.19 \n", + " seconds

\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "videoHTML('IEI Intel GPU (Intel Core + Onboard GPU)', \n", + " ['results/GPU/cars_output.mp4'], \n", + " 'results/GPU/stats.txt')" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

IEI Tank + Intel CPU + Intel NCS2

\n", + "

256 \n", + " frames processed in 3.86 \n", + " seconds

\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "videoHTML('IEI Tank + Intel CPU + Intel NCS2',\n", + " ['results/NCS2/cars_output.mp4'], \n", + " 'results/NCS2/stats.txt')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 5: Performance Comparison\n", + "\n", + "The running time of each inference task is recorded in `stats_*job_id*_*architectute*.txt` in `results` folder, where the *architecture* corresponds to the architecture of the target edge compute node. Run the cell below to plot the results of all jobs side-by-side. Lower values for processing time mean better performance. Keep in mind that some architectures are optimized for the highest performance, others for low power or other metrics." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA48AAAIpCAYAAAACWw1IAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzs3XmYnFWZ9/HvTYAiUAiEdAiE3WVkJxJkCQIyChEhogiRVUAbHH2VZZRFEJFNB3FEBVTaBRERRAWJyCZGFokzhIBIBBQJBoRAMGEplmLJef94qjOVSnWeh6Squ9L9/VxXXdV1zqnqu7qbJr8+5zknUkpIkiRJkrQ4yw10AZIkSZKkzmd4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyT1k4jYJiIujoi/RcQLEZHqbo8MdH1SK0TErg0/2ykiLh7ouiRJS8/wKGlIiIjDmvyD9pF+/PyHAv8DfBR4C7Byf31uDbyI2LDJz1/R2+oDXb+ai4jTlvB7enWT12r2O6rx9lpE/Csi/hQR34uI90ZEFKhzREQcHRG/iYhHa3+8qkbE7Ij4S0TcGhHfjogjI2LbiFixPV8xScu65Qe6AEka7CJiBHABMGyga5G0TBsGjKjdtgQ+BvwhIg5JKc1s9oSI2A/4LrBGk+61ajeAd9W1vw+4vlVFSxo8DI+S1H7vBcoNbS8DVwNP1h7P7deKpPZ5DPhGQ9v/DkQhQ8R44PcRsX1K6Yn6joiYCFyOK80ktYjhUZLab6MmbWenlM7o90rUaX4IPJczptofhbRKSukh4JiBrmMA/RP4ec6YPxd8reeBH9Q9XhfYBRjZMG594BzgkN6GiFgBOJ9Fg+NzwO+B2bXHo4AtgDcXrEnSEGZ4lKT2W6lJ26P9XoU60ekppUcGugi11EMppVaF57mNr1VbBv8bYLuGsZMi4pMppedrj3cC1msY81vgAymlFxs/UUSMAfYmWwqbWlG8pMHHZQySVKePjU1+X+tbOyLOiYj7axtOPBsRd0REd0Qs8vu09/nAF5t8qh82fI7Tmjw/ImJiRPyw9jnnRcQrtU0ufh8RJ+RtphIRjzS+n1r7myLi1Ii4OyKeGaAaShFxbET8sVbDi7XNO86IiDct7jXrXvsdEfG12mvMrm0CMq9W61UR8cmIWGcxz185Io6KiF9GxMyIeD4iXoqIWbXnHxoRHfWH1ljMbqYR8daIuCAi/h4RL0fE3Ii4OSI+XOB1l4uIj0XEbbWvYSUiZkTE2bXA0uf3smh9DeMubjJu11rfvpFt7vJk7Xs6KyJ+EBGFZsdqX4cvR8Qf6n4unqm9nwsiYpsir9OJUkpzgc826VoB2Lbu8RZNxlzYLDjWXvefKaXvpJS2JQuZkrSIjvofoiR1qoj4AHAJ0Bhqdqjd3hsRk1JKLfmLfURsBvyU5v8A7N3kYhfgpIg4MqX0szfw2psAN7DorER/1vA24NfAWxu6NgFOAfaNiF1SSnP6eP5awPeAvZp0rwisDrwd2IdsWd5pTV7jQ2QbiTQuAYTsa7Ne7fknR8SHU0pFlxoOiIj4BHAeUKprLgG7AbtFxLkppc/18dxVgGtqY+ttWrsdHtn1c+1WjohfA+9vaF8POBz4cETskVKa2uzJEbES8HXgSBb9A/mKwGpk7+eTtUD7Hymll1tYf3/5Ux/to+o+brzOGmAscFXei6eUXl+SoiQNfs48SlK+LYBfsGhwrLcf2XKvpRYR48iO9WgW2hqtBlwREUe8gU9xI/nBsd013MaiwbHeJmQhoFlt6wLTaB4cC4mI/yD7njYLjo3eBtwREVsu6efrB+8Dvs3CwbHRZyPiPX30Xc6iwbHeaOA6mu/Y2UrfZ9HgWG9V4CeRXc+3kMiOl7gB+ATF/n1zGHBtp80sF9TXbP9LdR83+8PLKRHxq9qM+sZtqEvSILcs/sKUpP42onY/F7iW7HqgD7HoX/Y/TTYb1qt3x8ntWfT6pJuAv9Q9/iNkyyjJdmFdpWH8v4CbgWfJAt32Df0XRsTUlNL9Bd7PurX7e8kC4krAO3o7+6mGUcCrZIHkCbLws37DmEkRcVxK6am62oIs9K3Lop4AbiH7Po0i24Vy7cZBtSWL32ry/L+SfT3mk10vVr9Esgz8IiI2Sym9UuD9FXVqRCxuw5xbUkq5M0X834zT42RHLJSBD5ItZaz3aRqWJEbEJJoH8X/WXqtEFujWLFDH0up9H38g21RmGxZeignZBlR7segM2n8BOze0zQemAH8HuoAJwPC6/t2AL9B8afmSektEnJcz5qsppX8uxefoaxnyw3Uf/57sd1X9OZABTKzdiIi5wJ1k/938KqVU/ztJkhaVUvLmzZu3QX8jm2VIDbdHmozbsMm4RBb0uurGbUsWfhrHrdHkNU9rMu6wPur8bJOxk4GVG8Yd0WTcpU1e75Em4+YDRzYZW+7HGirAO+vGjAQeajJun4bX2reP789XgBUbxi5HFvIPaWj/dZPnnwJE3ZjlyXa5bBz38SX8+evr5yrvdl6T19q1j7FTgFXqxu3TZMy8Jq/3xybjbqv/fpPNPP692ectWN/FTcZd3Mf7+FTdmGFkfyxY7NcFGEO2K239mKeBsQ3j1ic7SqR+3PM0+e+24Pf1tCX8vm69JL+jau/zaOCFJmNnNXnNy95gXbdR99+lN2/evDXeXLYqScWclOquv0sp3Uk2S9Vow6X8PPs1PH4dOCo1bHKRUvoB8LeGsfs0W87XxCUppYsaG1NKlX6soSeltODsv5TS02RLJxs1HnPSWBvA5JTSialhRjClND+l9MuU0o972yJiVWCPhuffn1I6M6WU6p77GlmgbLR/87fTEY5JKb3Q+yCldDXZ7GG91SNiwdLTiFiTRWfFAT5b//1OKc0G+uNombtTShfUfd7XyZbjNmr8uZhIdk1jva+mlO6ub0gpzQIubBhXJpv57kQbNGxO9BjZda0rNxl7WpO2o8iWqRe1E3B77XpgSVqE4VGS8j1HNvPW6Ikmbasu6SeJiGFky/TqDQP+2WRXysSi1wyuAhS5Lu+HHVDDpU3ainw9mwWd7xT4fL22YdFLNjbp4701W1a4wxv4XP3pvpRSs01U8r6mWzfp/2dKqdkfRvLOLmyFnzRpW9Kfi6/08X09q8nYTv2+FnV+7Y85C0nZsR0TgIPIlqcWsQLwg8jZRVnS0GR4lKR8s1JK85u0N9ulcWl+r65JFtSWxugCY+7tgBoeadJW5Ou5VpMxRa6x7DUqf8hilWs7k7bKRimlWMyt6HmBj/TRnvc1bbZh0KxmL1SbmX6mYD1L6pEmbUV+Lpb2+1rkZ7aoW3K+p5FSuqdFn+svwKSU0qf7GpAyl6WU3km2UdahZH9wuZdsqWozq5Et+ZakhbhhjiTl+1cf7Z24nX2z7fkbtTsA5NaQUmr2Ne3Er2czZbJrzjpJK39GX1pMX19ho1UG6ueiyH83A+F5smtve71GthLiMeDO9AaPj0kpPQb8uHbrPfLmE2QbBkXD8E7eXVjSADE8SlLn+BfZP5TrZ/5eJjuLsKi/5g2ov7ZvoGpYCk8BGzS0bQLMfAPPb/QQ2S66RTU9ZH0Z9XSTtmY72faeBdnuozqWVLPv6zUU/7l4I7PX/WnuG5h9fsNSSk8CX4qINwOHNHQPb/IUSUOc4VGSOkRK6fWImM7CRxOsBHw3FTj+IiKGpaU83LsTasjxRxYNj58AflPw+dPJZm/q//+3MtkGMa/lPbkf3l9/a7Z88q0RsWFK6ZGG9n37oZ4l9b/ARxvaHkgpnZD3xEH4PQUWnNU6OqX06wLDm4XvvmazJQ1hXvMoSZ2l2aYkV0TEhs0GR8TwiNgrIn7KortILss19OXKJm17R8SXG3d5jcz7I2LBjEpK6TmyMzbrrQNcHBFvavYJI6IrIg6LiFuAg5ey/o5SWz7cuDlOABdERGlBQ7a88dT+rO0Nuobs6Jx6x0bE/rWzQRcREVtFxOnAP9pe3cDYEJgcEfdGxDERsciZpwAR8XayDXUa3dXO4iQtm5x5lKTOcgHZOW7r1LVtATwUEbeTLcN7kWz54NuAzchmBgF+NIhq6MsvgWnAuIb2E4GPRsQUYB7ZRjA7kJ3r96WGsacCu7Pw0tyDgA/UAmLvLqsjgU3J3mPvH1v73Kl2CZ0aEc/ljLms/liTNvg6ix6TsifwQET8FigBe9G5S1ZJKT0WEd8GPlPXvAJwBXBmRNwNzCHbDXhdsuv5lnaTnWXFFmTf469HxN/JNsqZQ/bzvzGwM4tukvUv3tgRH5KGCMOjJHWQlNILEbEPtQPf67qGAbvUboO+hr6klFJE7AtMZeFwC7A2cGCB15gWEZ8hC8n1ysD7W1JocYcXGHMP2bLMtkgpXRERB5MFxHobAh+ve/w42XVwnRoijwfGAu9qaH8rix4pM1S9uXbL85+1Yz4kaSEuW5WkDpNSupPs3Lo3sp3/PBZdfrhM19CX2kHv44Drl+I1LgQ+SPNrvfryd+BvS/o5O9xHgN8tpv9RsvMCG3eZrbatojcopVQlm1G+gOI7tM4Hftu2ogbW42S7sr4RzwMfSym1ewWBpGWUM4+S1IFSSjMi4h1k/xjeF9geGAO8CXgFmEsWZO4CbgZ+l1J6ZbDVsJjangDeV9sU5CBgR2AjYHWyJbWzgQfIrm+8uo/XuDoiricLThPIAmkX2Wzri8CTtdf4I3BTm5eODqjabPN7yWZCDwc2J1v2+QjZUuGvkQXHroanzunHMnOllF4G/l9EnAscRrYk8+1ks6XDyI65+AdwH3ALcF3tZ2nQSSndAawXEVsBOwHvJFuCvQHZOY4rkf2cPw38mSxEX5ZSarYDryQBEIvfsV2SJAki4gMsGsSvTSk1LneVJA1SzjxKkjTERcQBZNd8/iyl9GyT/u2A7zR56jXtrk2S1DmceZQkaYiLiFOAM8iOu5hOtlz3eWBVYCtg6yZPexjYpL+WKkuSBp4zj5IkqdcKZBslbZcz7lngQwZHSRpa3G1VkiS9EVOB7VJKfxroQiRJ/ctlq5IkDXER0XvG5XuBLYG1yHZWXQ54BphJdtbkz1JKfxioOiVJA2vIh8eRI0emDTfccKDLkCRJkqQBcddddz2dUmo8jmkRQ/6axw033JBp06YNdBmSJEmSNCAi4h9FxnnNoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknL1e3iMiHUj4lsRMTUiXoyIFBEbNowZFxEXRcQDtTGzIuInEbFRk9dbLiJOiohHIuLliPhTROzbX+9HkqRGt956KxMnTmTMmDFEBBdffPGCvldffZUTTjiBLbfcklVWWYW1116bAw88kFmzZi30GtVqlU9/+tOMHDmSVVZZhYkTJ/LYY4/18zuRJOn/DMTM41uA/YF5wG19jPkIsBnwTeB9wInAO4BpEbFew9gzgNOA82tj/whcGRF7trxySZIKqFQqbL755nzjG99g+PDhC/W9+OKLTJ8+nZNPPpnp06fzq1/9ikcffZQJEybw2muvLRh3zDHH8Itf/IKf/vSn3HbbbTz33HPstddevP766/39diRJAiBSSv37CSOWSynNr338caAH2Cil9EjdmK6U0pyG520AzATOTCmdWmsbBTwKfCWl9MW6sTcDXSmlLfPqGTduXJo2bdrSvzFJkpool8ucf/75HHbYYX2O+ctf/sJmm23GvffeyxZbbMGzzz5LV1cXP/zhDznooIMAePTRR9lggw247rrr2GOPPfqpeknSUBARd6WUxuWN6/eZx97gmDNmTpO2fwBzgDF1zXsAKwKXNgy/FNii2TJXSZI6zXPPPQfAGmusAcBdd93Fq6++yu67775gzHrrrccmm2zCHXfcMSA1SpK0zGyYExGbAKOA++uaNwOqwEMNw2fU7jfth9IkSVpir7zyCv/5n//J3nvvzbrrrgvA7NmzGTZsGCNHjlxo7FprrcXs2bMHokxJklh+oAsoIiKWB75DNvP4/bquEcAzadG1t3Pr+iVJ6kivvfYaBx98MM888wzXXHPNQJcjSdJiLSszj+cDOwIHp5TmLe2LRcSRETEtIqbNmbPICllJktrutdde44ADDuDee+/l5ptvZs0111zQN3r0aF5//XWefvrphZ7z5JNPMnr06P4uVZIkYBkIjxHxFeBI4IiU0o0N3fOA1SMiGtp7Zxzn0kRK6aKU0riU0riurq7WFixJUo5XX32VSZMmce+99zJlypRFAuE222zDCiuswE033bSg7bHHHuP+++9nxx137O9yJUkCOnzZakScDJwAfDql9OMmQ2YAJeDNLHzdY++1jn9pb4WSJC2qUqnw0EPZ/5bmz5/PrFmzuOeeexgxYgTrrLMO++23H3feeSeTJ08mIhZcx7jaaqsxfPhwVlttNT72sY9x/PHHM2rUKNZcc02OO+44ttxyS97znvcM5FuTJA1hHTvzGBGfAc4ETk4pnd/HsOuBV4GDGtoPBu5LKc1sY4mSJDU1bdo0xo4dy9ixY3nppZf44he/yNixYzn11FN57LHH+NWvfsXjjz/ONttsw9prr73gdsUVVyx4jfPOO48PfvCDTJo0ifHjx1Mul5k8eTLDhg0bwHcmSRrK+v2cR4CI+HDtw38HPgF8kmwznDkppVsi4iPAZcANwJcanv5cSukvda/1FeAY4PPAdGAScBQwMaX067xaPOdRkiRJ0lBW9JzHgVq2emXD4wtr97cAuwITgKjdT2gY2zum18lABTgaGA08COxfJDhKkiRJkooZkJnHTuLMoyRJkqShrOjMY8de8yhJkiRJ6hyGR0mSJElSLsOjJEmSJCmX4VGSJEmSlGugdluVJLVBqacy0CVIg161uzzQJUjSgHDmUZIkSZKUy/AoSZIkScpleJQkSZIk5TI8SpIkSZJyGR4lSZIkSbkMj5IkSZKkXIZHSZIkSVIuw6MkSZIkKZfhUZIkSZKUy/AoSZIkScpleJQkSZIk5TI8SpIkSZJyGR4lSZIkSbkMj5IkSZKkXIZHSZIkSVIuw6MkSZIkKZfhUZIkSZKUy/AoSZIkScpleJQkSZIk5TI8SpIkSZJyGR4lSZIkSbkMj5IkSZKkXIZHSZIkSVIuw6MkSZIkKZfhUZIkSZKUy/AoSZIkScpleJQkSZIk5TI8SpIkSZJyGR4lSZIkSbkMj5IkSZKkXIZHSZIkSVIuw6MkSZIkKZfhUZIkSZKUy/AoSZIkScpleJT6cOuttzJx4kTGjBlDRHDxxRcv1J9S4rTTTmOdddZh+PDh7LrrrsyYMWOhMfPmzeOQQw5htdVWY7XVVuOQQw7hmWee6cd3IUmSJLWG4VHqQ6VSYfPNN+cb3/gGw4cPX6T/nHPO4Wtf+xrf+ta3uPPOOxk1ahTvfe97ef755xeMOfDAA5k+fTrXX389119/PdOnT+eQQw7pz7chSZIktcTyA12A1Kn23HNP9txzTwAOO+ywhfpSSpx33nmceOKJ7LvvvgD86Ec/YtSoUVx22WUcddRR3H///Vx//fXcfvvt7LDDDgB897vf5V3vehcPPvgg//Zv/9av70eSJElaGs48Sktg5syZzJ49m913331B2/Dhw9l555254447AJg6dSrlcpkdd9xxwZjx48ezyiqrLBgjSZIkLSsMj9ISmD17NgBrrbXWQu1rrbXWgr7Zs2fT1dVFRCzojwhGjRq1YIwkSZK0rDA8SpIkSZJyGR6lJTB69GgAnnzyyYXan3zyyQV9o0ePZs6cOaSUFvSnlHjqqacWjJEkSZKWFYZHaQlstNFGjB49mptuumlB28svv8xtt9224BrHHXbYgUqlwtSpUxeMmTp1Ki+88MJC10FKkiRJywJ3W5X6UKlUeOihhwCYP38+s2bN4p577mHEiBGsv/76HHPMMZx99tm8/e1v521vextnnnkm5XKZAw88EIBNNtmECRMmcNRRR3HRRRcBcNRRR7HXXnu506okSZKWOc48Sn2YNm0aY8eOZezYsbz00kt88YtfZOzYsZx66qkAHH/88Rx77LF86lOfYty4cTzxxBPceOONrLrqqgte47LLLmOrrbZijz32YI899mCrrbbixz/+8UC9JUmSJGmJRf31WEPRuHHj0rRp0wa6DElqiVJPZaBLkAa9and5oEuQpJaKiLtSSuPyxjnzKEmSJEnKZXiUJEmSJOUyPEqSJEmSchkeJUmSJEm5DI+SJEmSpFz9fs5jRKwLnACMA7YChgMbpZQeaRi3EnAGcDCwOnAPcEJK6daGccvVXu8oYDTwIHB6SukX7X0n7eWOiVJ7uVuiJEnSGzMQM49vAfYH5gG3LWbc94Fu4FRgL+AJ4IaI2Lph3BnAacD5wPuAPwJXRsSerS1bkiRJkoaufp95BG5NKa0FEBEfB3ZvHBARWwEHAkeklH5Ya7sFmAGcDkystY0CPgt8JaV0bu3pUyLiLcBXgN+0+b1IkiRJ0pDQ7zOPKaX5BYZNBF4Frqh73mvA5cAeEVGqNe8BrAhc2vD8S4EtImKjpa9YkiRJktSpG+ZsBsxMKb3Y0D6DLCy+pW5cFXioyTiATdtWoSRJkiQNIZ0aHkeQXRPZaG5df+/9MymllDNOkiRJkrQUOjU8tlVEHBkR0yJi2pw5cwa6HEmSJEnqeJ0aHucBazRp751JnFs3bvWIiJxxC0kpXZRSGpdSGtfV1bXUxUqSJEnSYNep4XEGsFFErNzQvinwCv93jeMMoAS8uck4gL+0rUJJkiRJGkI6NTxOBlYA9uttiIjlgUnAjSmlaq35erJdWQ9qeP7BwH0ppZn9UKskSZIkDXoDcc4jEfHh2ofb1O7fFxFzgDkppVtSSndHxBXAeRGxAjAT+A9gI+qCYkrpqYj4b+CkiHgemE4WMHejdhakJEmSJGnpDUh4BK5seHxh7f4WYNfax4cDZwFnAqsDfwImpJSmNzz3ZKACHA2MBh4E9k8p/br1ZUuSJEnS0DQg4TGl1LjBTbMxLwHH1W6LG/c6WcA8szXVSZIkSZIadeo1j5IkSZKkDmJ4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQpl+FRkiRJkpTL8ChJkiRJymV4lCRJkiTlMjxKkiRJknIZHiVJkiRJuQyPkiRJkqRchkdJkiRJUi7DoyRJkiQp1/J9dZR6Kge+kReqdpcvW/pyJEmSJEmdqM/wCFza8DjV7qNJG4DhUZIkSZIGqcWFx7fWfbwOWZi8AbgceBJYCzgA2B04qF0FSpIkSZIGXp/hsdpd/nvvx6WeyleBn1W7y5+rGzID+F2pp3IucBxwe9uqlCRJkiQNqKIb5ryXbNaxmeuB97SmHEmSJElSJyoaHl8B3tFH3zbAq60pR5IkSZLUiRZ3zWO9K4EvlXoqr9Y+7r3mcX/gNODidhQnSZIkSeoMRcPjccBqwLm1W70rav2SJEmSpEGqUHisdpdfBA4o9VROB7YH1gaeAP5Y7S7f38b6JEmSJEkdoOjMIwC1oGhYlCRJkqQh5g2Fx1JPZSSwPrBSY1+1u3xHq4qSJEmSJHWWQuGx1FNZG7gE2K1JdwAJGNbCuiRJkiRJHaTozOO3yY7q+DzwZ6DatookSZIkSR2naHjcBTim2l3+UTuLkSRJkiR1puUKjnuJbHdVSZIkSdIQVDQ8fh84qJ2FSJIkSZI6V9Flq48AB5V6KjcCvwHmNg6odpcvaWFdRMR44IvA1sBw4G/A+SmlH9SNWQk4AzgYWB24BzghpXRrK2uRJEmSpKGuaHjsqd1vCLynSX8i2421JSJiS+C3wB+BbuBF4MPA9yOilFL6dm3o94H3A58DHgY+BdwQETuklO5pVT2SJEmSNNQVDY9vbWsVi/oI2dEfe6eUKrW2m2qh8lDg2xGxFXAgcERK6YcAEXELMAM4HZjYzzVLkiRJ0qBVKDxWu8t/b3chDVYEXiXbqKfes8AatY8n1sZc0duZUnotIi4HTqzNUHqkiCRJkiS1QNGZRwBKPZW3kx3bMYLsusdbqt3lB9pQ18XAfwDfjIizyJat7gf8O3BIbcxmwMyU0osNz51BFj7fUvtYkiRJkrSUCoXHUk9leeB7ZMEt6rpSqadyCfDxanf59VYVlVK6LyJ2Ba4CPllrfhX4RErp8trjEcC8Jk+fW9ffVEQcCRwJsP7667eiZEmSJEka1Ioe1fEFsqM6Tie7/nHV2v3ptfZTWllURLwV+AXZzOHeZJv0fAf4TkQs9ZEhKaWLUkrjUkrjurq6lvblJEmSJGnQK7ps9VDgrGp3+Ut1bX8HvlTqqQTwUeBLTZ+5ZM4mm2ncK6X0aq3t5ohYE/hGRPyUbNZxgybP7Z1xXOQ4EUmSJEnSkik687gOcHsffbcDY1pTzgJbAH+qC469/hdYExhFNiu5UUSs3DBmU+AV4KEW1yRJkiRJQ1bR8PgEsEMffdsDj7emnAVmA1tHxIoN7dsBL5PNKk4GViDbSAeAiFgemATc6E6rkiRJktQ6RZetXgacUuqpvAb8hCxMjiY7j/EU4NwW13U+cCUwOSIuJDuyYyJwAPD1lNIrwN0RcQVwXkSsAMwk26F1I7LrMCVJkiRJLVI0PH6R7OiLs4Az69qDLOSd1sqiUko/j4g9gRPIdnldieway08B360benhdTasDfwImpJQT23StAAAgAElEQVSmt7IeSZIkSRrqCoXHanf5VWD/Uk9lK2BnFj7n8d52FJZSug64LmfMS8BxtZskSZIkqU2KzjwCUO0u/4lsdk+SJEmSNIQU2jCn1FM5tNRT+UIffV8o9VQOaW1ZkiRJkqROUnS31eOAZ/vomwcc25pyJEmSJEmdqGh4fAtwXx99M2r9kiRJkqRBqmh4fB0Y2UffSLJdVyVJkiRJg1TR8Pi/wJF99B0F3NmaciRJkiRJnajobqtnAzeVeip/IDt38Z/AGODjwDuBPdpTniRJkiSpExSaeax2l6cAk4D1gO8D19fu1wX2r3aXf9e2CiVJkiRJA67oslWq3eVfABsAWwC7ApsDG1a7y1e1pzRJkiRJUqcoumwVgGp3OZHtripJkiRJGkIKh8dST2VL4BRgZ2AEsH21uzy91FM5E7i12l2+sU01SpIkSZIGWKFlq6Weyo7A/wBbAb8EhjW8xidaX5okSZIkqVMUvebxv4CbgU2Az7DwuY7TgG1aXJckSZIkqYMUDY/bABdUu8vzgdTQ9zSwVkurkiRJkiR1lKLhsQoM76NvNPBsa8qRJEmSJHWiouHxduAzpZ5K/fjeGcgjgCktrUqSJEmS1FGK7rZ6KlmAvBu4kiw4HlzqqZwDbA+8sz3lSZIkSZI6QaGZx2p3+W5gV+AZ4DSyDXOOAVYC3l3tLt/fpvokSZIkSR2g8DmP1e7yncAupZ7KysBIYF61u/x82yqTJEmSJHWMotc8LlDtLr9Y7S7PAoaVeipblXoqK7ShLkmSJElSBykUHks9lZNKPZWz6h7vBPwDmA78rdRTeUub6pMkSZIkdYCiM4+HkoXFXucA9wEfBv4FnN7iuiRJkiRJHaRoeFwX+BtAqacyEtgOOKXaXb4K+DKwS3vKkyRJkoauJ554go9+9KN0dXWx0korsemmm3LLLbcsNOavf/0rH/rQh1h99dVZeeWVecc73sH997ufpVqv6IY5rwO91zbuDLwM/KH2+ClgRIvrkiRJkoa0Z555hvHjx7PTTjtx7bXX0tXVxcMPP8yoUaMWjJk5cybjx4/n0EMP5Xe/+x2rr746DzzwAOVyeQAr12BVNDz+BTig1FO5DTgcuLXaXX6l1rceMKcdxUmSJElD1TnnnMPaa6/NJZdcsqBto402WmjMySefzO67787Xvva1BW0bb7xxv9WooaXostUzgAOBCrA72TWPvSaQbZwjSZIkqUWuvvpqtttuOyZNmsSoUaPYeuutOf/880kpATB//nwmT57MpptuyoQJE+jq6mLbbbfliiuuGODKNVgVCo/V7vJ1wGZkAXKLand5Sl33VBYOk5IkSZKW0sMPP8yFF17IxhtvzA033MDRRx/NiSeeyAUXXADAU089RaVS4eyzz2b33Xfnpptu4oADDuCggw7i2muvHeDqNRhF718uhqpx48aladOmDXQZiyj1VAa6BGlQq3YPzmtB/N0htd9g/f2hzrPiiisybtw47rjjjgVtn//857nqqqu4//77efzxxxkzZgwHHHAAl1122YIxBx54IPPmzeO6664biLK1DIqIu1JK4/LGFV22KkmSJKkfrb322my66aYLtW2yySbMmjULgJEjR7L88ssvdozUSoZHSZIkqQONHz+eBx98cKG2v/71r2ywwQZANjO57bbbLnaM1EpFd1uVJEmS1I+OPfZYdtxxR8466ywmTZrE3XffzTe/+U3OPvvsBWOOP/549t9/f971rnex2267MWXKFC6//HKuvvrqAaxcg5Uzj5IkSVIH2nbbbbn66qv52c9+xuabb87JJ5/MGWecwSc/+ckFY/bZZx8uuugizj33XLbYYgu+9a1vcckll/D+979/ACvXYJW7YU6pp7IicBZwRbW73Hk7yywlN8yRhqbBuuGFvzuk9husvz8kDV0t2zCn2l1+BfgUsHIrCpMkSZIkLXuKLlu9h+ycR0mSJEnSEFQ0PH4WOL7UU5nQzmIkSZIkSZ2p6G6rPwFGANeWeipV4Emg/mLJVO0uv7nVxUmSJEmSOkPR8PgHFg6LkiRJkqQhpFB4rHaXD253IZIkSZKk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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + " arch_list = [('core', 'Core', 'Intel Core\\ni5-6500TE\\nCPU'),\n", + " ('gpu', 'GPU', ' Intel Core\\ni5-6500TE\\nGPU'),\n", + " ('ncs2', 'NCS2', 'Intel\\nNCS2')]\n", + "\n", + "stats_list = []\n", + "for arch, dir_, a_name in arch_list:\n", + " if 'job_id_'+arch in vars():\n", + " stats_list.append(('results/{}/stats.txt'.format(dir_), a_name))\n", + " else:\n", + " stats_list.append(('placeholder'+arch, a_name))\n", + "\n", + "summaryPlot(stats_list, 'Architecture', 'Time, seconds', 'Inference Engine Processing Time', 'time' )\n", + "summaryPlot(stats_list, 'Architecture', 'Frames per second', 'Inference Engine FPS', 'fps' )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (Ubuntu)", + "language": "python", + "name": "c003-python_3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/object-detection/Devcloud/tutorial1.py b/object-detection/Devcloud/tutorial1.py new file mode 100644 index 00000000..c735135a --- /dev/null +++ b/object-detection/Devcloud/tutorial1.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +""" + Copyright (c) 2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from __future__ import print_function +import sys +import os +from argparse import ArgumentParser +import cv2 +import time +import logging as log +from openvino.inference_engine import IENetwork, IEPlugin +from enum import Enum +import collections +import sys +from pathlib import Path +sys.path.insert(0, str(Path().resolve().parent.parent)) +sys.path.insert(0,os.path.join(os.environ['HOME'],'Reference-samples/iot-devcloud/demoTools/')) +from demoutils import * + + +class output_mode_type(Enum): + CLASSIFICATION_MODE=1 + SSD_MODE=2 + + +def build_argparser(): + parser = ArgumentParser() + parser.add_argument("-m", "--model", help="Path to an .xml file with a trained model.", required=True, type=str) + parser.add_argument("-i", "--input", + help="Path to video file or image. 'cam' for capturing video stream from camera", required=True, + type=str) + parser.add_argument("-l", "--cpu_extension", + help="MKLDNN (CPU)-targeted custom layers.Absolute path to a shared library with the kernels " + "impl.", type=str, default=None) + parser.add_argument("-pp", "--plugin_dir", help="Path to a plugin folder", type=str, default=None) + parser.add_argument("-d", "--device", + help="Specify the target device to infer on; CPU, GPU, FPGA or MYRIAD is acceptable. Demo " + "will look for a suitable plugin for device specified (CPU by default)", default="CPU", + type=str) + parser.add_argument("--labels", help="Labels mapping file", default=None, type=str) + parser.add_argument("-pt", "--prob_threshold", help="Probability threshold for detections filtering", + default=0.5, type=float) + parser.add_argument("-fr", help="maximum frames to process", default=256, type=int) + parser.add_argument("-b", help="Batch size", default=1, type=int) + parser.add_argument('-o', '--output_dir', + help='Location to store the results of the processing', + default=None, + required=True, + type=str) + return parser + + +def main(): + log.basicConfig(format="[ %(levelname)s ] %(message)s", level=log.INFO, stream=sys.stdout) + args = build_argparser().parse_args() + model_xml = args.model + model_bin = os.path.splitext(model_xml)[0] + ".bin" + + preprocess_times = collections.deque() + infer_times = collections.deque() + postprocess_times = collections.deque() + + ROIfile=open("ROIs.txt","w"); # output stored here, view with ROIviewer + + # Plugin initialization for specified device and load extensions library if specified + log.info("Initializing plugin for {} device...".format(args.device)) + plugin = IEPlugin(device=args.device, plugin_dirs=args.plugin_dir) + if args.cpu_extension and 'CPU' in args.device: + plugin.add_cpu_extension(args.cpu_extension) + + # Read IR + log.info("Reading IR...") + net = IENetwork(model=model_xml, weights=model_bin) + + if plugin.device == "CPU": + supported_layers = plugin.get_supported_layers(net) + not_supported_layers = [l for l in net.layers.keys() if l not in supported_layers] + if len(not_supported_layers) != 0: + log.error("Following layers are not supported by the plugin for specified device {}:\n {}". + format(plugin.device, ', '.join(not_supported_layers))) + log.error("Please try to specify cpu extensions library path in demo's command line parameters using -l " + "or --cpu_extension command line argument") + sys.exit(1) + + #Set Batch Size + net.batch_size = args.b + batchSize = net.batch_size + frameLimit = args.fr + assert len(net.inputs.keys()) == 1, "Demo supports only single input topologies" + assert len(net.outputs) == 1, "Demo supports only single output topologies" + input_blob = next(iter(net.inputs)) + out_blob = next(iter(net.outputs)) + log.info("Loading IR to the plugin...") + exec_net = plugin.load(network=net, num_requests=2) + infer_file = os.path.join(args.output_dir, 'i_progress.txt') + + # Read and pre-process input image + n, c, h, w = net.inputs[input_blob].shape + output_dims=net.outputs[out_blob].shape + infer_width=w; + infer_height=h; + num_channels=c; + channel_size=infer_width*infer_height + full_image_size=channel_size*num_channels + + print("inputdims=",w,h,c,n) + print("outputdims=",output_dims[3],output_dims[2],output_dims[1],output_dims[0]) + if int(output_dims[3])>1 : + print("SSD Mode") + output_mode=output_mode_type.SSD_MODE + else: + print("Single Classification Mode") + output_mode=CLASSIFICATION_MODE + output_data_size=int(output_dims[2])*int(output_dims[1])*int(output_dims[0]) + del net + if args.input == 'cam': + input_stream = 0 + else: + input_stream = args.input + assert os.path.isfile(args.input), "Specified input file doesn't exist" + if args.labels: + with open(args.labels, 'r') as f: + labels_map = [x.strip() for x in f] + else: + labels_map = None + + cap = cv2.VideoCapture(input_stream) + cur_request_id = 0 + next_request_id = 1 + + is_async_mode =True + if (is_async_mode == True): + log.info("Starting inference in async mode...") + else : + log.info("Starting inference in sync mode...") + + render_time = 0 + + framenum = 0 + process_more_frames=True + frames_in_output=batchSize + + while process_more_frames: + time1 = time.time() + for mb in range(0 , batchSize): + ret, frame = cap.read() + if not ret or (framenum >= frameLimit): + process_more_frames=False + frames_in_output=mb + + if (not process_more_frames): + break + + # convert image to blob + # Fill input tensor with planes. First b channel, then g and r channels + in_frame = cv2.resize(frame, (w, h)) + in_frame = in_frame.transpose((2, 0, 1)) # Change data layout from HWC to CHW + + + time2 = time.time() + diffPreProcess = time2 - time1 + if process_more_frames: + preprocess_times.append(diffPreProcess*1000) + + # Main sync point: + # in the truly Async mode we start the NEXT infer request, while waiting for the CURRENT to complete + # in the regular mode we start the CURRENT request and immediately wait for it's completion + inf_start = time.time() + if is_async_mode: + exec_net.start_async(request_id=next_request_id, inputs={input_blob: in_frame}) + else: + exec_net.start_async(request_id=cur_request_id, inputs={input_blob: in_frame}) + if exec_net.requests[cur_request_id].wait(-1) == 0: + inf_end = time.time() + + det_time = inf_end - inf_start + infer_times.append(det_time*1000) + progressUpdate(infer_file, (sum(infer_times)/1000), framenum+1, 256) + time1 = time.time() + + for mb in range(0 , batchSize): + if (framenum >= frameLimit): + process_more_frames=False; + break; + + # Parse detection results of the current request + res = exec_net.requests[cur_request_id].outputs[out_blob] + for obj in res[0][0]: + # Write into ROIs.txt only objects when probability more than specified threshold + if obj[2] > args.prob_threshold: + confidence=obj[2] + locallabel = obj[1] - 1 + print(str(0),str(framenum),str(locallabel),str(confidence),str(obj[3]),str(obj[4]),str(obj[5]),str(obj[6]), file=ROIfile) + + + sys.stdout.write("\rframenum:"+str(framenum + 1)) + sys.stdout.flush() + render_start = time.time() + framenum = framenum+1 + time2 = time.time() + diffPostProcess = time2 - time1 + postprocess_times.append(diffPostProcess*1000) + + if is_async_mode: + cur_request_id, next_request_id = next_request_id, cur_request_id + + + print("\n") + preprocesstime=0 + inferencetime=0 + postprocesstime=0 + + for obj in preprocess_times: + preprocesstime+=obj + for obj in infer_times: + inferencetime+=obj + for obj in postprocess_times: + postprocesstime+=obj + + + print("Preprocess: {:.2f} ms/frame".format(preprocesstime/(len(preprocess_times)*batchSize))) + print("Inference: {:.2f} ms/frame ".format(inferencetime/(len(infer_times)*batchSize))) + print("Postprocess: {:.2f} ms/frame".format(postprocesstime/(len(postprocess_times)*batchSize))) + + + with open(os.path.join(args.output_dir, 'stats.txt'), 'w') as f: + f.write('{:.3g} \n'.format(inferencetime/(batchSize*1000))) + f.write('{} \n'.format(framenum)) + + del exec_net + del plugin + + +if __name__ == '__main__': + sys.exit(main() or 0) + diff --git a/object-detection/Devcloud/tutorial1_job.sh b/object-detection/Devcloud/tutorial1_job.sh new file mode 100644 index 00000000..e12e0752 --- /dev/null +++ b/object-detection/Devcloud/tutorial1_job.sh @@ -0,0 +1,31 @@ +ME=`basename $0` + +# The default path for the job is your home directory, so we change directory to where the files are. +cd $PBS_O_WORKDIR +DEVICE=$2 +FP_MODEL=$3 +INPUT_FILE=$4 +RESULTS_BASE=$1 + + +NN_MODEL="mobilenet-ssd.xml" +RESULTS_PATH="${RESULTS_BASE}" +mkdir -p $RESULTS_PATH +echo "$ME is using results path $RESULTS_PATH" + +if [ "$DEVICE" = "HETERO:FPGA,CPU" ]; then + # Environment variables and compilation for edge compute nodes with FPGAs + export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/altera/aocl-pro-rte/aclrte-linux64/ + # Environment variables and compilation for edge compute nodes with FPGAs + source /opt/fpga_support_files/setup_env.sh + aocl program acl0 /opt/intel/openvino/bitstreams/a10_vision_design_bitstreams/2019R1_PL1_FP11_MobileNet_Clamp.aocx +fi + +# Running the object detection code +SAMPLEPATH=$PBS_O_WORKDIR +! python3 tutorial1.py -m ${SAMPLEPATH}/../mobilenet-ssd/${FP_MODEL}/${NN_MODEL} \ + -i $INPUT_FILE \ + -o $RESULTS_PATH \ + -d $DEVICE \ + -l /opt/intel/openvino/deployment_tools/inference_engine/lib/intel64/libcpu_extension_avx2.so +