{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Asynchronous Inference with OpenVINO™\n", "This notebook demonstrates how to use the [Async API](https://docs.openvino.ai/2024/openvino-workflow/running-inference/optimize-inference/general-optimizations.html) for asynchronous execution with OpenVINO.\n", "\n", "OpenVINO Runtime supports inference in either synchronous or asynchronous mode. The key advantage of the Async API is that when a device is busy with inference, the application can perform other tasks in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first.\n", "\n", "\n", "#### Table of contents:\n", "\n", "- [Imports](#Imports)\n", "- [Prepare model and data processing](#Prepare-model-and-data-processing)\n", " - [Download test model](#Download-test-model)\n", " - [Load the model](#Load-the-model)\n", " - [Create functions for data processing](#Create-functions-for-data-processing)\n", " - [Get the test video](#Get-the-test-video)\n", "- [How to improve the throughput of video processing](#How-to-improve-the-throughput-of-video-processing)\n", " - [Sync Mode (default)](#Sync-Mode-(default))\n", " - [Test performance in Sync Mode](#Test-performance-in-Sync-Mode)\n", " - [Async Mode](#Async-Mode)\n", " - [Test the performance in Async Mode](#Test-the-performance-in-Async-Mode)\n", " - [Compare the performance](#Compare-the-performance)\n", "- [`AsyncInferQueue`](#AsyncInferQueue)\n", " - [Setting Callback](#Setting-Callback)\n", " - [Test the performance with `AsyncInferQueue`](#Test-the-performance-with-AsyncInferQueue)\n", "\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Imports\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "Note: you may need to restart the kernel to use updated packages.\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ "import platform\n", "\n", "%pip install -q \"openvino>=2023.1.0\"\n", "%pip install -q opencv-python\n", "if platform.system() != \"windows\":\n", " %pip install -q \"matplotlib>=3.4\"\n", "else:\n", " %pip install -q \"matplotlib>=3.4,<3.7\"" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import cv2\n", "import time\n", "import numpy as np\n", "import openvino as ov\n", "from IPython import display\n", "import matplotlib.pyplot as plt\n", "\n", "# Fetch the notebook utils script from the openvino_notebooks repo\n", "import requests\n", "\n", "r = requests.get(\n", " url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n", ")\n", "open(\"notebook_utils.py\", \"w\").write(r.text)\n", "\n", "import notebook_utils as utils" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare model and data processing\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "### Download test model\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "We use a pre-trained model from OpenVINO's [Open Model Zoo](https://docs.openvino.ai/2024/documentation/legacy-features/model-zoo.html) to start the test. In this case, the model will be executed to detect the person in each frame of the video." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "################|| Downloading person-detection-0202 ||################\n", "\n", "========== Retrieving model/intel/person-detection-0202/FP16/person-detection-0202.xml from the cache\n", "\n", "========== Retrieving model/intel/person-detection-0202/FP16/person-detection-0202.bin from the cache\n", "\n" ] } ], "source": [ "# directory where model will be downloaded\n", "base_model_dir = \"model\"\n", "\n", "# model name as named in Open Model Zoo\n", "model_name = \"person-detection-0202\"\n", "precision = \"FP16\"\n", "model_path = f\"model/intel/{model_name}/{precision}/{model_name}.xml\"\n", "download_command = f\"omz_downloader \" f\"--name {model_name} \" f\"--precision {precision} \" f\"--output_dir {base_model_dir} \" f\"--cache_dir {base_model_dir}\"\n", "! $download_command" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Select inference device\n", "[back to top ⬆️](#Table-of-contents:)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c9c9f01fc0014058909e1d61e7bdd56d", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Dropdown(description='Device:', options=('CPU', 'AUTO'), value='CPU')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import ipywidgets as widgets\n", "\n", "core = ov.Core()\n", "device = widgets.Dropdown(\n", " options=core.available_devices + [\"AUTO\"],\n", " value=\"CPU\",\n", " description=\"Device:\",\n", " disabled=False,\n", ")\n", "\n", "device" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Load the model\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# initialize OpenVINO runtime\n", "core = ov.Core()\n", "\n", "# read the network and corresponding weights from file\n", "model = core.read_model(model=model_path)\n", "\n", "# compile the model for the CPU (you can choose manually CPU, GPU etc.)\n", "# or let the engine choose the best available device (AUTO)\n", "compiled_model = core.compile_model(model=model, device_name=device.value)\n", "\n", "# get input node\n", "input_layer_ir = model.input(0)\n", "N, C, H, W = input_layer_ir.shape\n", "shape = (H, W)" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Create functions for data processing\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def preprocess(image):\n", " \"\"\"\n", " Define the preprocess function for input data\n", "\n", " :param: image: the orignal input frame\n", " :returns:\n", " resized_image: the image processed\n", " \"\"\"\n", " resized_image = cv2.resize(image, shape)\n", " resized_image = cv2.cvtColor(np.array(resized_image), cv2.COLOR_BGR2RGB)\n", " resized_image = resized_image.transpose((2, 0, 1))\n", " resized_image = np.expand_dims(resized_image, axis=0).astype(np.float32)\n", " return resized_image\n", "\n", "\n", "def postprocess(result, image, fps):\n", " \"\"\"\n", " Define the postprocess function for output data\n", "\n", " :param: result: the inference results\n", " image: the orignal input frame\n", " fps: average throughput calculated for each frame\n", " :returns:\n", " image: the image with bounding box and fps message\n", " \"\"\"\n", " detections = result.reshape(-1, 7)\n", " for i, detection in enumerate(detections):\n", " _, image_id, confidence, xmin, ymin, xmax, ymax = detection\n", " if confidence > 0.5:\n", " xmin = int(max((xmin * image.shape[1]), 10))\n", " ymin = int(max((ymin * image.shape[0]), 10))\n", " xmax = int(min((xmax * image.shape[1]), image.shape[1] - 10))\n", " ymax = int(min((ymax * image.shape[0]), image.shape[0] - 10))\n", " cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)\n", " cv2.putText(\n", " image,\n", " str(round(fps, 2)) + \" fps\",\n", " (5, 20),\n", " cv2.FONT_HERSHEY_SIMPLEX,\n", " 0.7,\n", " (0, 255, 0),\n", " 3,\n", " )\n", " return image" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Get the test video\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "video_path = \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/CEO%20Pat%20Gelsinger%20on%20Leading%20Intel.mp4\"" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## How to improve the throughput of video processing\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "Below, we compare the performance of the synchronous and async-based approaches:\n", "\n", "### Sync Mode (default)\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "Let us see how video processing works with the default approach.
\n", "Using the synchronous approach, the frame is captured with OpenCV and then immediately processed:\n", "\n", "![drawing](https://user-images.githubusercontent.com/91237924/168452573-d354ea5b-7966-44e5-813d-f9053be4338a.png)\n", "\n", "```\n", "while(true) {\n", "// capture frame\n", "// populate CURRENT InferRequest\n", "// Infer CURRENT InferRequest\n", "//this call is synchronous\n", "// display CURRENT result\n", "}\n", "```\n", "```" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def sync_api(source, flip, fps, use_popup, skip_first_frames):\n", " \"\"\"\n", " Define the main function for video processing in sync mode\n", "\n", " :param: source: the video path or the ID of your webcam\n", " :returns:\n", " sync_fps: the inference throughput in sync mode\n", " \"\"\"\n", " frame_number = 0\n", " infer_request = compiled_model.create_infer_request()\n", " player = None\n", " try:\n", " # Create a video player\n", " player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n", " # Start capturing\n", " start_time = time.time()\n", " player.start()\n", " if use_popup:\n", " title = \"Press ESC to Exit\"\n", " cv2.namedWindow(title, cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", " while True:\n", " frame = player.next()\n", " if frame is None:\n", " print(\"Source ended\")\n", " break\n", " resized_frame = preprocess(frame)\n", " infer_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n", " # Start the inference request in synchronous mode\n", " infer_request.infer()\n", " res = infer_request.get_output_tensor(0).data\n", " stop_time = time.time()\n", " total_time = stop_time - start_time\n", " frame_number = frame_number + 1\n", " sync_fps = frame_number / total_time\n", " frame = postprocess(res, frame, sync_fps)\n", " # Display the results\n", " if use_popup:\n", " cv2.imshow(title, frame)\n", " key = cv2.waitKey(1)\n", " # escape = 27\n", " if key == 27:\n", " break\n", " else:\n", " # Encode numpy array to jpg\n", " _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n", " # Create IPython image\n", " i = display.Image(data=encoded_img)\n", " # Display the image in this notebook\n", " display.clear_output(wait=True)\n", " display.display(i)\n", " # ctrl-c\n", " except KeyboardInterrupt:\n", " print(\"Interrupted\")\n", " # Any different error\n", " except RuntimeError as e:\n", " print(e)\n", " finally:\n", " if use_popup:\n", " cv2.destroyAllWindows()\n", " if player is not None:\n", " # stop capturing\n", " player.stop()\n", " return sync_fps" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Test performance in Sync Mode\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Source ended\n", "average throuput in sync mode: 55.59 fps\n" ] } ], "source": [ "sync_fps = sync_api(source=video_path, flip=False, fps=30, use_popup=False, skip_first_frames=800)\n", "print(f\"average throuput in sync mode: {sync_fps:.2f} fps\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Async Mode\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "Let us see how the OpenVINO Async API can improve the overall frame rate of an application. The key advantage of the Async approach is as follows: while a device is busy with the inference, the application can do other things in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first.\n", "\n", "![drawing](https://user-images.githubusercontent.com/91237924/168452572-c2ff1c59-d470-4b85-b1f6-b6e1dac9540e.png)\n", "\n", "In the example below, inference is applied to the results of the video decoding. So it is possible to keep multiple infer requests, and while the current request is processed, the input frame for the next is being captured. This essentially hides the latency of capturing, so that the overall frame rate is rather determined only by the slowest part of the pipeline (decoding vs inference) and not by the sum of the stages.\n", "\n", "```\n", "while(true) {\n", "// capture frame\n", "// populate NEXT InferRequest\n", "// start NEXT InferRequest\n", "// this call is async and returns immediately\n", "// wait for the CURRENT InferRequest\n", "// display CURRENT result\n", "// swap CURRENT and NEXT InferRequests\n", "}\n", "```" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def async_api(source, flip, fps, use_popup, skip_first_frames):\n", " \"\"\"\n", " Define the main function for video processing in async mode\n", "\n", " :param: source: the video path or the ID of your webcam\n", " :returns:\n", " async_fps: the inference throughput in async mode\n", " \"\"\"\n", " frame_number = 0\n", " # Create 2 infer requests\n", " curr_request = compiled_model.create_infer_request()\n", " next_request = compiled_model.create_infer_request()\n", " player = None\n", " async_fps = 0\n", " try:\n", " # Create a video player\n", " player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n", " # Start capturing\n", " start_time = time.time()\n", " player.start()\n", " if use_popup:\n", " title = \"Press ESC to Exit\"\n", " cv2.namedWindow(title, cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n", " # Capture CURRENT frame\n", " frame = player.next()\n", " resized_frame = preprocess(frame)\n", " curr_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n", " # Start the CURRENT inference request\n", " curr_request.start_async()\n", " while True:\n", " # Capture NEXT frame\n", " next_frame = player.next()\n", " if next_frame is None:\n", " print(\"Source ended\")\n", " break\n", " resized_frame = preprocess(next_frame)\n", " next_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n", " # Start the NEXT inference request\n", " next_request.start_async()\n", " # Waiting for CURRENT inference result\n", " curr_request.wait()\n", " res = curr_request.get_output_tensor(0).data\n", " stop_time = time.time()\n", " total_time = stop_time - start_time\n", " frame_number = frame_number + 1\n", " async_fps = frame_number / total_time\n", " frame = postprocess(res, frame, async_fps)\n", " # Display the results\n", " if use_popup:\n", " cv2.imshow(title, frame)\n", " key = cv2.waitKey(1)\n", " # escape = 27\n", " if key == 27:\n", " break\n", " else:\n", " # Encode numpy array to jpg\n", " _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n", " # Create IPython image\n", " i = display.Image(data=encoded_img)\n", " # Display the image in this notebook\n", " display.clear_output(wait=True)\n", " display.display(i)\n", " # Swap CURRENT and NEXT frames\n", " frame = next_frame\n", " # Swap CURRENT and NEXT infer requests\n", " curr_request, next_request = next_request, curr_request\n", " # ctrl-c\n", " except KeyboardInterrupt:\n", " print(\"Interrupted\")\n", " # Any different error\n", " except RuntimeError as e:\n", " print(e)\n", " finally:\n", " if use_popup:\n", " cv2.destroyAllWindows()\n", " if player is not None:\n", " # stop capturing\n", " player.stop()\n", " return async_fps" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Test the performance in Async Mode\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Source ended\n", "average throuput in async mode: 75.17 fps\n" ] } ], "source": [ "async_fps = async_api(source=video_path, flip=False, fps=30, use_popup=False, skip_first_frames=800)\n", "print(f\"average throuput in async mode: {async_fps:.2f} fps\")" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Compare the performance\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "image/png": "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brcrWLCgSpUqJUnaunWr1e78/6pVq7rdLjQ0VKVLl07XHhcXpzNnzki6dLupO++80+3DeZ/mffv2uZ0/I61bt1bRokWVkpKiL774wmq/cOGCZsyYIcn13tFXKlmypMaPH6/9+/dr586dmjVrlp577jnr/bFixQo1bdpU//zzj626nO+9evXqWRdFc3rkkUfk7++vw4cP64cffnDpK1GihLp06aIzZ86oXr16atCggQYPHqy5c+dmWsMTTzwhSZoyZYpL+0cffSRJeuihh5Q/f36320ZGRloXtruSu/f8n3/+KUkqVKiQKlWqlGFNnihcuLBCQ0PTtTvfz5K99/y2bdskSYGBgRn+zN96663WFdud7/OEhAQdPnxYUsbv+cqVK8vPzy9d+6ZNmyRJvr6+uueeezJ8z2/evFmS/fc8ANyMCNIA4AFfX1/VqFFDPXr00Keffqpdu3apffv2kqTvv/9eK1eudBl/3333qUSJEtq6dat+++03q90Zbh5//PEMnysoKCjDPh+fS/+Mm/9/tWdJSkxMtGoMDg62uWdXl1E9zuBztf7La3WGIR8fH5dgciXnldIvD0/O/3cGK3fc9V0e/mJjYzN87N+/X5Ks0J1V/v7+evTRRyX9322uJGn+/Pk6fvy48ubNq86dO2dprvLly+vBBx/U66+/rm3btunll1+WdCno2LkF1vnz5/X5559LunS17CsVLlxY9957ryT395SeMmWKXn31VVWsWFGrVq3SuHHj1K5dOxUtWlQdOnRQfHx8um26deumgIAA/fzzz9q7d6+kS6+l848L/8Z7PiwsLMPtPHW193NWxrh7z2f2vvXz81PhwoVdxl/+3s9oW19fXxUqVChdu/M9n5KSkul7/tixY5Lsv+cB4GZEkAaAbBAaGqqpU6dav+RfGaT9/PysWx1NnjxZ0qVb2MTFxalQoUJWCM8OISEhkqTU1FSXlTBv5FyRTEtL09GjRzMcd+jQIZfxl///kSNHMtzOXZ/zjwsOh0MXL16UuXSaU4aPzG5llRHn93rdunX666+/JP1fqG7fvr1Hgc/X11fPP/+8atWqJSn9eywzc+bM0YkTJyRJzzzzjHUrs8sfc+fOlXTpD0FXfi/8/f01YMAAbd++Xfv27dOXX36p3r17K3/+/Prmm2901113pXuvFSpUSA888IDS0tKscD5r1iwlJiaq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AAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ODVQfrbb7/V3XffrUKFCilv3rwqV66cOnXqpH379rmMS0xMVExMjMLDwxUQEKCIiAgNGDBASUlJOVQ5AAAAACC38svpAtwxxqhPnz768MMPVaFCBT388MPKnz+/Dh48qGXLlmnPnj0qU6aMJCk5OVnR0dHauHGjWrRooU6dOmnDhg0aP368li1bpuXLlytv3rw5vEcAAAAAgNzCK4P0m2++qQ8//FBPPfWU3nzzTfn6+rr0X7x40fr/V199VRs3btSgQYM0duxYq33w4MEaN26cJk2apCFDhly32gEAAAAAuZvDGGNyuojLnT17VqVKlVKBAgW0bds2+fllnPWNMSpdurQSExN1+PBhBQUFWX3JyckqXry4ihYtqp07d2b5+RMTExUaGqqEhASFhIRc074AAHAjihj8fU6XAADIheLHts7pEjJlJwt63Yr0Tz/9pH/++Uc9evRQamqq5s2bp+3btyssLEx33XWXKlasaI2Ni4vTwYMH1bJlS5cQLUlBQUFq2LChFi5cqH379lmHggMAAAAAcC28LkivW7dOkuTr66uoqCht377d6vPx8VG/fv00fvx4SZeCtCRFRka6nSsyMlILFy5UXFxchkE6JSVFKSkp1teJiYnZsh8AAAAAgNzJ667affToUUnSxIkTFRoaqtWrV+v06dNavny5KlWqpAkTJui9996TJCUkJEiSQkND3c7lXI53jnNnzJgxCg0NtR6sXAMAAAAAMuN1QTotLU2SlCdPHs2ZM0d169ZVcHCwGjVqpK+++ko+Pj6aMGFCtj3fkCFDlJCQYD2uvLUWAAAAAACX87pDu52ry3Xq1FHJkiVd+qpVq6by5ctrx44dOnXqlDU2oxVn52HaGa1YS1JAQIACAgKyo3QAAAAAwE3A61akK1euLEkKCwtz2+9sP3v2rHVutPNc6Std7RxqAAAAAADs8roV6aZNm0qStmzZkq7vwoUL2rFjh4KCglSkSBEVL15cJUuWVGxsrJKTk9Pd/io2NlblypXjvGcAAAAAQLbxuhXpChUqqEWLFtqxY4cmT57s0jd27FidOnVK7du3l5+fnxwOh3r16qWkpCSNHj3aZezo0aOVlJSkxx9//HqW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rV+/Xi+//LJGjRqlW2+91e2YuLg4HTx4UC1btlRQUJBLX1BQkBo2bKiFCxdq3759KlOmzPUoGwAAAACQi3l0sbHrISUlRV27dlXNmjU1cODADMfFxcVJkiIjI932O9ud4wAAAAAAuBYerUg7HT58WN988422bt2q5ORkTZkyRZJ07Ngx7d69W9WrV1dgYKBHcw8bNkxxcXFat26dfH19MxyXkJAgSQoNDXXbHxIS4jLuSikpKUpJSbG+TkxM9KheAAAAAMDNweMV6XfffVflypXTM888o7ffftvlcO+jR4/q9ttvd3thsKxYsWKFxo8frxdffFHVqlXztMQsGTNmjEJDQ60Hh38DAAAAADLjUZCeP3++nnnmGVWvXl3z5s3Tk08+6dJftWpVRUVFac6cObbnvnjxorp166aoqCgNHjz4quOdK9EZrTg7V5gzWrEeMmSIEhISrMe+ffts1wwAAAAAuHl4dGj3a6+9prJly2rJkiUKCgrSunXr0o2pXr26fv31V9tzJyUlWecz58mTx+2Y22+/XZL07bffWhchy+gc6KudQx0QEKCAgADbdQIAAAAAbk4eBemNGzeqS5cu6a6SfblSpUrpyJEjtucOCAjQY4895rZv+fLliouL03333aciRYooIiJCkZGRKlmypGJjY5WcnOxSU3JysmJjY1WuXDkO2QYAAAAAZAuPgnRaWpr8/f0zHXP06FGPVnoDAwM1efJkt33du3dXXFychgwZogYNGljtvXr10qhRozR69GiNHTvWah89erSSkpL0/PPP264DAAAAAAB3PArSlStXzvSw7YsXL2r58uWqXr26x4XZMXDgQM2dO1fjxo3Thg0bVKtWLa1fv14//fST6tatq759+16XOgAAAAAAuZ9HFxt75JFHtGHDBo0cOTJdX2pqqv73v/9p165d6tq16zUXmBVBQUFatmyZ+vbtqy1btmjChAnaunWr+vfvr8WLF3t8Cy4AAAAAAK7kMMYYuxtduHBBLVq00PLly1WhQgXlzZtXmzdvVocOHbR27VrFx8erRYsW+vHHH+VwOP6Nuv81iYmJCg0NVUJCgnUPam/UcURUTpcAAMilVp8bk9MlAAByofixrXO6hEzZyYIerUj7+/tr4cKFGjx4sE6cOKG//vpLxhh9/fXXOnnypAYNGqR58+bdcCEaAAAAAICr8egcaenS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+vrq4Ycf1owZM7K7Hsv58+f1yiuvyBijEydOaPHixdq6dat69Oih5s2bS7oUpCUpMjLS7RyRkZFauHCh4uLiCNIAAAAAgGzhUZAOCQn514Pp+fPnNXLkSOtrh8Oh//3vfxozZozVlpCQIEkKDQ3NsM7Lx7mTkpKilJQU6+srb+MFAAAAAMDlPDpHul69etq0aVN21+IiODhYxhilpqZq3759eueddzR58mQ1adIkW8PumDFjFBoaaj1YuQYAAAAAZMajID1ixAj98ssv+vTTT7O7nnR8fHxUunRpPfnkk/rwww8VGxurl19+WdL/rURntOLsDNwZrVhL0pAhQ5SQkGA99u3bl817AAAAAADITTw6tPvnn39WkyZN1KNHD7311luqW7euihUrJofD4TLO4XCku5r2tWjRooUkaenSpZL+79xo57nSV7raOdSSFBAQoICAgGyrEQAAAACQu3kUpEeMGGH9/7p167Ru3Tq347I7SB88eFCS5O/vL+lSQC5ZsqRiY2OVnJzscuXu5ORkxcbGqly5chyuDQAAAADINh4F6SVLlmR3HZa///5bERERypcvn0v7mTNnFBMTI0m69957JV0K6r169dKoUaM0evRojR071ho/evRoJSUl6fnnn//XagUAAAAA3HyyFKTnzZunKlWqqFKlSpKk6Ojof62gWbNmaeLEibrzzjsVERGhkJAQHThwQD/++KNOnDihRo0aqV+/ftb4gQMHau7cuRo3bpw2bNigWrVqaf369frpp59Ut25d9e3b91+rFQAAAABw88nSxcbat2+vL7/80vq6fPnyevPNN/+Vgtq0aaOHH35Ye/fu1RdffKEJEyboxx9/VFRUlD744AP98ssvCgwMtMYHBQVp2bJl6tu3r7Zs2aIJEyZo69at6t+/vxYvXuwyFgAAAACAa5WlFWl/f39duHDB+jo+Pl6nTp36VwqqU6eO6tSpY2ub0NBQTZo0SZMmTfpXagIAAAAAwClLK9Jly5bVb7/9ptTUVKvtyit0AwAAAABwM8jSinTnzp01atQoFSxYUIUKFZIkTZo0SVOnTs10O4fDoZ07d157lQAAAAAAeIksBekXX3xRefPm1ffff6+DBw/K4XDIGCNjTKbbXa0fAAAAAIAbTZaCtJ+fnwYPHqzBgwdLknx8fNSvXz8NGzbsXy0OAAAAAABvk6VzpK80fPhwNWnSJJtLAQAAAADA+2VpRfpKw4cPz+46AAAAAAC4IXi0Ig0AAAAAwM2KIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGrwvSBw4c0Ouvv64WLVqobNmyypMnj4oXL64OHTpo1apVbrdJTExUTEyMwsPDFRAQoIiICA0YMEBJSUnXuXoAAAAAQG7ndUH6rbfeUr9+/bRr1y61aNFC/fv315133qm5c+fqjjvu0MyZM13GJycnKzo6WpMmTVKVKlXUr18/Va5cWePHj1ezZs107ty5HNoTAAAAAEBu5JfTBVypXr16Wrp0qaKjo13af/31VzVv3lxPPvmk2rVrp4CAAEnSq6++qo0bN2rQoEEaO3asNX7w4MEaN26cJk2apCFDhlzXfQAAAAAA5F4OY4zJ6SKyqmXLlvrpp5+0Zs0a1alTR8YYlS5dWomJiTp8+LCCgoKsscnJySpevLiKFi2qnTt3Zvk5EhMTFRoaqoSEBIWEhPwbu5EtOo6IyukSAAC51OpzY3K6BABALhQ/tnVOl5ApO1nQ6w7tzoy/v78kyc/v0kJ6XFycDh48qIYNG7qEaEkKCgpSw4YNtWvXLu3bt++61woAAAAAyJ1umCC9d+9eLVq0SCVKlFD16tUlXQrSkhQZGel2G2e7c5w7KSkpSkxMdHkAAAAAAJCRGyJIX7hwQV26dFFKSorGjRsnX19fSVJCQoIkKTQ01O12zuV45zh3xowZo9DQUOtRpkyZbK4eAAAAAJCbeH2QTktLU/fu3bV8+XI9/vjj6tKlS7bOP2TIECUkJFgPDgMHAAAAAGTG667afbm0tDT17NlTn3/+uR599FG9//77Lv3OleiMVpydh2lntGItSQEBAdYVwAEAAAAAuBqvDdJpaWnq0aOHPv30U3Xq1EnTpk2Tj4/rAvrVzoG+2jnUAAAAAADY5ZWHdl8eoh966CFNnz7dOi/6cpGRkSpZsqRiY2OVnJzs0pecnKzY2FiVK1eO854BAAAAANnG64K083DuTz/9VA8++KA+++wztyFakhwOh3r16qWkpCSNHj3apW/06NFKSkrS448/fj3KBgAAAADcJLzu0O5Ro0bpk08+UXBwsCpVqqSXXnop3Zh27dqpZs2akqSBAwdq7ty5GjdunDZs2KBatWpp/fr1+umnn1S3bl317dv3+u4AAAAAACBX87ogHR8fL0lKSkrSyy+/7HZMRESEFaSDgoK0bNkyjRgxQrNnz9aSJUtUokQJ9e/fX8OHD1dgYOB1qhwAAAAAcDNwGGNMThfhTRITExUaGqqEhATrPtTeqOOIqJwuAQCQS60+NyanSwAA5ELxY1vndAmZspMFve4caQAAAAAAvBlBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANnhlkP7ss8/Uu3dv1alTRwEBAXI4HJo2bVqG4xMTExUTE6Pw8HAFBAQoIiJCAwYMUFJS0vUrGgAAAABwU/DL6QLcefHFF7Vnzx4VLlxYJUqU0J49ezIcm5ycrOjoaG3cuFEtWrRQp06dtGHDBo0fP17Lli3T8uXLlTdv3utYPQAAAAAgN/PKFenJkycrPj5ex44dU58+fTId++qrr2rjxo0aNGiQFi5cqLFjx2rhwoUaNGiQ1qxZo0mTJl2nqgEAAAAANwOvDNJ33XWXwsPDrzrOGKPJkycrODhYQ4cOdekbOnSogoODNXny5H+rTAAAAADATcgrg3RWxcXF6eDBg2rYsKGCgoJc+oKCgtSwYUPt2rVL+/bty6EKAQAAAAC5zQ0fpCUpMjLSbb+z3TnOnZSUFCUmJro8AAAAAADIyA0dpBMSEiRJoaGhbvtDQkJcxrkzZswYhYaGWo8yZcpkf6EAAAAAgFzjhg7S2WHIkCFKSEiwHhwGDgAAAADIjFfe/iqrnCvRGa04Ow/TzmjFWpICAgIUEBCQ/cUBAAAAAHKlG3pF+mrnQF/tHGoAAAAAAOy64YN0yZIlFRsbq+TkZJe+5ORkxcbGqly5cpz3DAAAAADINjd0kHY4HOrVq5eSkpI0evRol77Ro0crKSlJjz/+eA5VBwAAAADIjbzyHOnJkyfrt99+kyT9+eefVtvSpUslSXfeead69eolSRo4cKDmzp2rcePGacOGDapVq5bWr1+vn376SXXr1lXfvn1zYhcAAAAAALmUVwbp3377TZ988olLW2xsrGJjY62vnUE6KChIy5Yt04gRIzR79mwtWbJEJUqUUP/+/TV8+HAFBgZe19oBAAAAALmbwxhjcroIb5KYmKjQ0FAlJCRY96H2Rh1HROV0CQCAXGr1uTE5XQIAIBeKH9s6p0vIlJ0seEOfIw0AAAAAwPVGkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALCBIA0AAAAAgA0EaQAAAAAAbCBIAwAAAABgA0EaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIANBGkAAAAAAGwgSAMAAAAAYANBGgAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ECQBgAAAADABoI0AAAAAAA2EKQBAAAAALAh1wTpNWvW6N5771VYWJiCgoLUoEEDzZo1K6fLAgAAAADkMn45XUB2WLJkiVq2bKm8efPq4YcfVv78+TV79mw99NBD2rdvn/r375/TJQIAAAAAcokbfkX64sWLevzxx+Xj46Ply5frww8/1IQJE7Rp0yZVqlRJzz//vPbs2ZPTZQIAAAAAcokbPkj/8ssv2rlzpzp37qyaNWta7aGhoXr++ed1/vx5ffLJJzlXIAAAAAAgV7nhg/TSpUslSS1atEjX17JlS0nSsmXLrmdJAAAAAIBc7IYP0nFxcZKkyMjIdH3FixdXcHCwNQYAAAAAgGt1w19sLCEhQdKlQ7ndCQkJsca4k5KSopSUlHTzJSYmZmOV2e9CSmpOlwAAyKXSUs7kdAkAgFzI2zOWsz5jzFXH3vBB+lqNGTNGI0eOTNdepkyZHKgGAABv0DGnCwAA5EKhr+d0BVlz+vTpDBdqnW74IO3cwYxWnRMTE1WgQIEMtx8yZIhiYmKsr9PS0nTy5EkVKlRIDocje4sFcN0lJiaqTJky2rdvn0JCQnK6HABALsJnDJC7GGN0+vRplSxZ8qpjb/gg7Tw3Oi4uTrVr13bpO3z4sJKSklSvXr0Mtw8ICFBAQIBLW1hYWLbXCSBnhYSE8EsOAOBfwWcMkHtcbSXa6Ya/2Fh0dLQk6aeffkrXt3DhQpcxAAAAAABcK4fJypnUXuzixYuqXLmyDhw4oJUrV1r3kk5ISFC9evUUHx+vbdu2KSIiIkfrBJAzEhMTFRoaqoSEBFYLAADZis8Y4OZ1wx/a7efnp8mTJ6tly5Zq3LixHn74YeXPn1+zZ8/Wnj17NH78eEI0cBMLCAjQ8OHD053CAQDAteIzBrh53fAr0k6rV6/W8OHD9fvvv+vChQuqXr26YmJi9NBDD+V0aQAAAACAXCTXBGkAAAAAAK6HG/5iYwAAAAAAXE8EaQDIIfHx8XI4HOrevXtOlwIAuEk4HA41adIkp8sAbngEaQDpJCcn65VXXlGtWrUUHBysgIAAlS5dWo0aNdKQIUO0c+fOnC4RAHAD6dmzpxwOhwoVKqSUlJScLgcArtkNf9VuANnr9OnTuvPOO/XHH3+oYsWKevTRR1WoUCEdP35cq1ev1tixY1WhQgVVqFAhp0sFANwATp8+rVmzZsnhcOjkyZOaM2cOF4MFcMMjSANw8frrr+uPP/5Qr1699OGHH8rhcLj07969m9UEAECWzZw5U8nJyYqJidHrr7+uKVOmEKQB3PA4tBuAixUrVkiSnn766XQhWpLKlSunKlWqSJLS0tIUHh6e6aF6jRs3lp+fn/bv3y9JmjZtmhwOh6ZNm6affvpJd9xxh/Lly6dChQqpW7duOnHihNt5Nm3apEceeUSlS5dWQECASpQooVatWmn+/PlX3afLz0XesmWL2rRpo7CwMBUoUECdOnXS8ePHrX1v3ry5QkJCVKBAAfXq1UvJyclu55w6darq16+v4OBgBQcHq379+po2bZrbsampqRo3bpwqVqyovHnzqmLFihozZozS0tIyrPno0aPq16+fKlasqICAABUuXFgdOnTQX3/9ddX9BQBvMmXKFPn5+WngwIFq2rSpFi9erD179rgdGxcXpx49eqhcuXIKCAhQwYIFVaNGDfXt21fOG83ceeed8vPz06FDh9zO0bVrVzkcDuvzbOnSpXI4HBoxYoTWrl2ru+++W/nz51doaKjat2+v+Ph4t/Ps2rVLTzzxhFVL0aJF1aRJkwz/rb+S81zkAwcOqHPnzipcuLDy58+v1q1ba9euXZKkLVu2qF27dipYsKDy58+vBx54QEeOHHE73/z589W0aVOFhoYqMDBQNWrU0MSJE3Xx4kW34ydPnqxq1aopb968KlOmjAYOHKhz585lWO/p06c1fPhwVa1aVYGBgQoLC1PLli3122+/ZWl/gZuOAYDLPProo0aSmTlzZpbGjxo1ykgyM2bMSNe3detWI8m0bt3aaps6daqRZNq3b2/y5MljOnToYPr372/q1q1rJJmGDRumm+frr782efLkMf7+/ub+++83Q4YMMY899pipVq2aadu27VVr3L17t5FkGjdubMLCwsxdd91l+vfvb6Kjo63n/PXXX01gYKC57777TP/+/U3t2rWNJNOjR4908/33v/81kkypUqXMs88+a5599llTqlQpI8k8++yz6cb37NnTSDLlypUzMTEx5qmnnjKFCxc2bdq0MZJMt27dXMbv2LHDlC5d2kgyLVq0MP379zddunQx+fLlM0FBQWblypVX3WcA8AabN282ksy9995rjDHmk08+MZLM8OHD0409cOCACQsLM/7+/qZdu3Zm0KBB5plnnjEtW7Y0/v7+5sKFC8YYYz799FMjybz88svp5vjnn39MYGCgqVq1qtW2ZMkSq4bAwEBz7733mv79+5tmzZoZSaZChQrm7NmzLvP8+uuvJiQkxDgcDtOqVSszePBg07t3b1OvXj1Ts2bNLO27JBMVFWUiIiJMgwYNTExMjPXvfvny5c2ff/5pwsLCTPPmzU3//v1NkyZNjCTTtGnTdHNNmDDBSDIFCxY0ffr0Mf379zeRkZFGkmnXrp1JS0tzGe/8bC5WrJh55plnTL9+/UzZsmWt54+OjnYZf+LECVO1alXrM7Fv376mZ8+eplChQsbPz898++23Wdpn4GZCkAbgYu7cuUaSyZ8/v+nfv79ZuHChOX78eIbjDxw4YPz8/EyTJk3S9f3vf/8zksycOXOsNmeQ9vPzM7/99pvVfvHiReuXiBUrVljthw8fNkFBQSYoKMisX78+3XPs27fvqvvkDNKSzOuvv261p6WlmXvvvddIMmFhYS51nj9/3kRFRRk/Pz9z+PBhq33ZsmVGkrnlllvMqVOnrPaTJ0+aSpUqGUlm+fLlVrvzF7gaNWqYpKQkq33//v2mcOHCboP0HXfcYXx9fc2CBQtc2rdt22by589vqlevftV9BgBvEBMTYySZL774whhjzOnTp01QUJApW7asSU1NdRn75ptvpvt32unEiRPW/589e9YULFjQlC9fPl2AfPvtt9PN4fx3WJL58ssvXcZ36dLFpT5jjDl37pwpVaqU8fHxMT/++GO6WrLyuWOMsZ6zX79+Lu1PPvmk9bmT0WfSunXrrPYdO3YYPz8/U7RoUbN3716XOu+8804jyXz66adWe1xcnPHz8zOlSpUyR44csdoTEhJM5cqV3Qbpzp07G0nmo48+cmk/cuSIKVOmjClSpEi6PzYANzuCNIB0JkyYYIKDg61fApx/sX/66afN9u3b041v3769cTgcJi4uzmo7f/68KVq0qClRooS1imDM/wXprl27ppvH2ffmm29abePGjTOSzLBhwzzeH2eQrlChQrpfupwrG+5WAJx/0f/ll1+sNufqsrsV+xkzZhhJpmfPnlZbjx49jCQze/bsdONHjx6dLkivX78+3RyXc/5S+ueff151vwEgJ50/f94UKVLEhISEuIQw55FPCxcudBnvDNIffPDBVefu16+fkWQWLVrk0n7bbbeZgIAAl+DtDNKNGzdON4+zLyYmxmqbOXNmhp9TdkgywcHBJjk52aV9+fLlV/1M+vjjj60252fRuHHj0j1HbGyskWSaNWtmtY0cOdJIMhMmTEg3fvr06emC9LFjx4yvr6/LHJdzfl/mz5+fpf0GbhZcbAxAOjExMXr88ce1YMEC/f7771q7dq1WrVqld955R1OmTNHMmTN13333WeN79+6tb7/9VpMnT9bYsWMlSfPmzdPRo0f1/PPPy88v/T81tWvXTtdWunRpSdKpU6esttWrV0uSWrRocc37FRUVle687xIlSkiSatasmW68s+/gwYNW24YNGyTJ7T04mzZtKknauHGj1bZp0yZJUqNGjdKNd9e2cuVKSdKRI0c0YsSIdP1bt261/lutWrV0/QDgLebOnatjx47pscceU968ea32rl276rPPPtOUKVNc/m3/z3/+oyFDhujpp5/W4sWL1apVK0VHR6t8+fLp5n7iiSc0adIkffTRR2revLkkad26ddqwYYM6d+6sggULptsmJz53IiMjlS9fPpc252dLZp9JWf3cuf3225U3b95r+txZs2aNUlNTlZKS4vZzJy4uTtKlz502bdqk6wduVgRpAG7lz59fDz74oB588EFJUkJCgp5//nm9++67euyxx3TgwAHlyZNH0qVfNsqVK6dPPvlEL730kvz8/DR58mQ5HA499thjbucPCQlJ1+YM3KmpqVZbQkKCJKlUqVLXvE+ZPWdmfRcuXLDaEhMT5ePjoyJFiqQbX6xYMTkcDiUmJlptCQkJ8vHxUeHChd2Ov9LJkyclSd9//72+//77DPclo4ugAYC3mDJliqRLwflyzZs3V6lSpTR37lydPHnSCr0RERFauXKlRowYoR9++EGzZs2SJFWpUkWjRo2yPo+cbdHR0ZozZ45OnDihQoUKafLkyZKkxx9/3G09N/LnjuT+M8PhcKhYsWI6cOCA1easv2jRounGZ/a5Exsbq9jY2Az3hc8dwBVX7QaQJaGhoXr77bcVHh6u48eP688//7T6HA6HnnjiCR0+fFjz58/Xvn379NNPP6l58+ZuVxLsCAsLkySXXxJyUkhIiNLS0nTs2LF0fUePHpUxxuWXo9DQUKWlpVlXBr+cuyuzOrd96623ZC6dfuP20a1bt2zcKwDIXs7PAUmKjo6Ww+GwHr6+vjpw4IBSUlL02WefuWxXrVo1ff311zp58qRWrFihYcOG6fDhw3rooYfShbw+ffooJSVFn376qc6cOaMvvvhCkZGRbldu7fDGzx3J/WeGMUZHjhxJ97kjXfpMulJmnzv9+/fP9HNn+PDh2bI/QG5BkAaQZQ6HQ0FBQW77evToIX9/f02ePFkff/yx0tLSMlwVsKNevXqSZP1CltNuu+02SZdup3IlZ9vlh4nXqFFDkvTrr7+mG++urX79+pL+7zZkAHAjmjZtmtLS0nTnnXfqscceS/dw/jHQuWp9JX9/fzVo0EAjR47Um2++KWOMvvvuO5cx999/v4oUKaLJkyfrq6++UkJCgnr16nXNtd9InzurVq3SuXPnrulzp27dui63CwOQNQRpAC4++OADrVmzxm3fnDlztGXLFoWFhaU7P7dYsWJq166dFixYoPfee0+FCxdWu3btrrmebt26KTg4WBMmTHA5B8zpeq8YOH/5GzlyZLpDuEeOHOkyRpK6dOkiSRo1apTLYXEHDhzQG2+8kW7+evXqqX79+vriiy80c+bMdP1paWlatmxZ9uwMAPwLjDGaOnWqHA6HPvnkE02ePDndY9q0abr99tv1xx9/aO3atZIuneN8+b+rTs5V1MvPs5akPHnyqHv37vr777/1/PPPy9/fX927d7/m+u+77z6VLl1an332mRYuXJiu/3p/7nTu3Fl+fn6aOHGiy7nT58+f16BBgyTJZb87d+4sX19fTZw40WVVOjExUS+99FK6+YsXL66OHTvq999/12uvvWbdr/tyq1at0pkzZ7Jxr4AbH+dIA3Dx448/qk+fPqpYsaIaNmyokiVLKjk5WRs2bNCvv/4qHx8fvfvuuwoICEi3bZ8+ffTVV1/pyJEj6t+/v3UO9bUoWrSoPv30Uz388MOqV6+e7rvvPlWuXFnHjx/XqlWrFBERoTlz5lzz82RV48aN9d///ldvvfWWqlWrpg4dOsgYo9mzZ2v//v169tln1bhxY2t806ZN1aNHD02dOlXVq1dX+/btlZKSopkzZ6pBgwbpVlgk6YsvvlDTpk318MMP6/XXX1etWrUUGBiovXv3asWKFTp27JjOnTt33fYZAOz45ZdftHv37gwvFObUo0cPrVixQlOmTFGdOnU0ffp0ffDBB2rcuLEqVKigkJAQ/f333/rhhx9UsGBB9ejRI90cvXv31vjx43Xw4EF16NDB7XnBdgUEBGjWrFlq1aqV7rnnHrVq1Uo1atRQYmKiNm7cqDNnzlgXALseKlSooHHjxql///6KiopSx44dFRQUpPnz52vbtm1q27atHn30UWt8xYoVNWzYMA0fPtwa7+fnp9mzZysqKkrbtm1L9xzvvvuutm3bpoEDB2r69Om6/fbbFRYWpn379mnt2rWKi4vToUOH0l04DbiZEaQBuBg3bpwaNmyon3/+WcuXL9ehQ4ckXbroSrdu3fTf//7X7ZVPpUuhsWzZstq7d2+2HF7n1L59e61atUpjxozRsmXLNG/ePBUuXFg1a9bMlsPH7XrzzTd122236b333tOHH34oSapatapGjRrl9he9jz76SJUqVdJHH32kt99+W6VLl1ZMTIw6duzoNkiXK1dOGzZs0MSJEzVnzhxNnTpVvr6+KlGihBo3bqwHHnjgX99HAPCU83Dtq60OP/TQQ3ruuef0xRdfaOLEierUqZPOnTun2NhYrV69WikpKSpdurSefPJJDRgwQGXLlk03R4UKFdSwYUP99ttv2fp5cPvtt2v9+vUaM2aMFi5cqEWLFqlAgQK69dZb1adPn2x7nqyKiYlRxYoVNXHiRH322Wc6f/68KlWqpAkTJujZZ59Nd/XvYcOGqWTJkpo0aZI++OADFS1aVA8//LBGjRrlNgwXLFhQv//+u95++23NnDlTM2bMUFpamooXL64aNWpo6NChbi+aCdzMHMbd8RsA4IFDhw6pbNmyuv3227V8+fKcLgcAkMudO3dOpUuXVnBwsHbt2iUfH85aBHB98K8NgGzz+uuv6+LFi3ryySdzuhQAwE1g6tSpOnHihHr37k2IBnBdsSIN4JokJCTovffe0549ezR58mRVqlRJf/zxh3x9fXO6NABALjV27FgdO3ZMH3zwgYKCgrR9+3brtk8AcD0QpAFck/j4eJUrV0558+ZVgwYN9P7776ty5co5XRYAIBdzOBzy9/dXjRo19NZbb6lBgwY5XRKAmwxBGgAAAAAAGziZBAAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQBAJrp37y6Hw6H4+PhrmiciIkIRERHZUlN2io+Pl8PhUPfu3bN13mnTpsnhcGjatGnZOi8AAN6AIA0AyPWcYbFVq1YZjlm6dKkcDof69OlzHSvDtXA4HGrSpInbPoI8AODf5JfTBQAA4M3GjBmjwYMHq1SpUjldyr+iVKlS2rJli0JDQ3O6FAAAbhgEaQAAMlGiRAmVKFEip8v41/j7+6tKlSo5XQYAADcUDu0GACATGZ0jffHiRY0ZM0YVKlRQ3rx5VbFiRY0ZM0a7du3K9JzjpKQkPffccypZsqQCAgIUFRWlr7/+2u3Y8+fPa+LEiapVq5aCgoKUP39+NWrUSPPmzcuwzl27dmnChAm69dZbFRAQcNVznzM6R/rQoUN67rnnFBkZqcDAQIWFhemWW25Rnz59lJCQkOmcV5o7d67q1aunfPnyqUiRIurZs6eOHDniduzu3bvVq1cvlS1bVgEBASpRooS6d++uPXv2WGOch+FL0rJly+RwOKzHtGnT1L17d/Xo0UOS1KNHD5f+y50+fVrDhw9X1apVrX1s2bKlfvvtt3R1NWnSRA6HQ+fOndOLL76oChUqyN/fXyNGjLD1WgAAcgdWpAEA8EDPnj01ffp0lS9fXk8//bRSUlI0adIkrVixIsNtLly4oBYtWuiff/5Rhw4ddObMGX355Zfq2LGjFixYoBYtWlhjU1JS1KpVKy1dulQ1a9bUY489pgsXLuj7779X27Zt9dZbb+mZZ55J9xz//e9/tXLlSrVu3Vr/+c9/VLRoUdv7dubMGTVs2FDx8fFq0aKF2rdvr/Pnz2v37t2aPn26/ve//2X5UPDZs2dr4cKFeuCBB3TXXXdp5cqVmjp1qn799VetXr1aBQoUsMauWrVKLVu2VHJystq0aaPIyEjFx8drxowZ+vHHH7VixQqVL19eERERGj58uEaOHKnw8HCXPwLUrFlTYWFhOnXqlObOnau2bduqZs2a6eo6efKkGjdurM2bN6thw4bq06ePEhMTNXfuXDVt2lRfffWV2rVrl267Dh06aNOmTWrVqpXCwsJUrlw5uy8vACA3MAAA5HK7d+82kkyFChXM8OHD3T66detmJJnevXu7bOts3717t9W2aNEiI8nUrFnTJCcnW+0HDx40xYoVM5JMt27dXOYJDw83kkzbtm1NSkpKurlatmzpMv755583kszQoUNNWlqa1Z6YmGjq1Klj8uTJYw4cOJCuztKlS5s9e/bYfm0ur3fevHlGkunbt2+68adPnzbnzp276rxTp041kowks2DBApe+wYMHG0nmmWeesdrOnz9vIiIiTP78+c369etdxv/666/G19fXtGnTxqVdkomOjs70+adOneq2v3PnzkaS+eijj1zajxw5YsqUKWOKFClizp49a7VHR0db3/MTJ05cbfcBALkcK9IAgJvGzp07NXLkyGue57PPPpMkDRs2TPny5bPaS5Qooeeee07PP/98httOmjRJefLksb5u3ry5wsPDtWbNGqstLS1N7733nipUqKCRI0e6HJKcP39+DRs2TPfdd5+++eabdKvSAwYMUNmyZa95HyUpMDAwXVtwcLCtOe666y61bNnSpe2FF17Q+++/r08//VRvvPGGfHx89N133yk+Pl6jRo3Sbbfd5jL+zjvvVNu2bTVnzhwlJiYqJCTE/s5c5vjx45o5c6aaNWum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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "width = 0.4\n", "fontsize = 14\n", "\n", "plt.rc(\"font\", size=fontsize)\n", "fig, ax = plt.subplots(1, 1, figsize=(10, 8))\n", "\n", "rects1 = ax.bar([0], sync_fps, width, color=\"#557f2d\")\n", "rects2 = ax.bar([width], async_fps, width)\n", "ax.set_ylabel(\"frames per second\")\n", "ax.set_xticks([0, width])\n", "ax.set_xticklabels([\"Sync mode\", \"Async mode\"])\n", "ax.set_xlabel(\"Higher is better\")\n", "\n", "fig.suptitle(\"Sync mode VS Async mode\")\n", "fig.tight_layout()\n", "\n", "plt.show()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## `AsyncInferQueue`\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "Asynchronous mode pipelines can be supported with the [`AsyncInferQueue`](https://docs.openvino.ai/2024/openvino-workflow/running-inference/integrate-openvino-with-your-application/python-api-exclusives.html#asyncinferqueue) wrapper class. This class automatically spawns the pool of `InferRequest` objects (also called “jobs”) and provides synchronization mechanisms to control the flow of the pipeline. It is a simpler way to manage the infer request queue in Asynchronous mode." ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Setting Callback\n", "[back to top ⬆️](#Table-of-contents:)\n", "\n", "When `callback` is set, any job that ends inference calls upon the Python function. The `callback` function must have two arguments: one is the request that calls the `callback`, which provides the `InferRequest` API; the other is called “user data”, which provides the possibility of passing runtime values." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def callback(infer_request, info) -> None:\n", " \"\"\"\n", " Define the callback function for postprocessing\n", "\n", " :param: infer_request: the infer_request object\n", " info: a tuple includes original frame and starts time\n", " :returns:\n", " None\n", " \"\"\"\n", " global frame_number\n", " global total_time\n", " global inferqueue_fps\n", " stop_time = time.time()\n", " frame, start_time = info\n", " total_time = stop_time - start_time\n", " frame_number = frame_number + 1\n", " inferqueue_fps = frame_number / total_time\n", "\n", " res = infer_request.get_output_tensor(0).data[0]\n", " frame = postprocess(res, frame, inferqueue_fps)\n", " # Encode numpy array to jpg\n", " _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n", " # Create IPython image\n", " i = display.Image(data=encoded_img)\n", " # Display the image in this notebook\n", " display.clear_output(wait=True)\n", " display.display(i)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def inferqueue(source, flip, fps, skip_first_frames) -> None:\n", " \"\"\"\n", " Define the main function for video processing with async infer queue\n", "\n", " :param: source: the video path or the ID of your webcam\n", " :retuns:\n", " None\n", " \"\"\"\n", " # Create infer requests queue\n", " infer_queue = ov.AsyncInferQueue(compiled_model, 2)\n", " infer_queue.set_callback(callback)\n", " player = None\n", " try:\n", " # Create a video player\n", " player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n", " # Start capturing\n", " start_time = time.time()\n", " player.start()\n", " while True:\n", " # Capture frame\n", " frame = player.next()\n", " if frame is None:\n", " print(\"Source ended\")\n", " break\n", " resized_frame = preprocess(frame)\n", " # Start the inference request with async infer queue\n", " infer_queue.start_async({input_layer_ir.any_name: resized_frame}, (frame, start_time))\n", " except KeyboardInterrupt:\n", " print(\"Interrupted\")\n", " # Any different error\n", " except RuntimeError as e:\n", " print(e)\n", " finally:\n", " infer_queue.wait_all()\n", " player.stop()" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "### Test the performance with `AsyncInferQueue`\n", "[back to top ⬆️](#Table-of-contents:)\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "average throughput in async mode with async infer queue: 103.81 fps\n" ] } ], "source": [ "frame_number = 0\n", "total_time = 0\n", "inferqueue(source=video_path, flip=False, fps=30, skip_first_frames=800)\n", "print(f\"average throughput in async mode with async infer queue: {inferqueue_fps:.2f} fps\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "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.8.10" }, "openvino_notebooks": { "imageUrl": "", "tags": { "categories": [ "API Overview" ], "libraries": [], "other": [], "tasks": [ "Object Detection" ] } }, "vscode": { "interpreter": { "hash": "08af04953a73b86b66cc089a637d3d397b0b73ad05ea59846f770cc21ccdacba" } }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "4ecf106c6ed841b8b894f447e66c41ba": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "DescriptionStyleModel", "state": { "description_width": "" } }, "54393aef1aee4b2a981c7b6ee75ed916": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", "state": {} }, "c9c9f01fc0014058909e1d61e7bdd56d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "DropdownModel", "state": { "_options_labels": [ "CPU", "AUTO" ], "description": "Device:", "index": 0, "layout": "IPY_MODEL_54393aef1aee4b2a981c7b6ee75ed916", "style": "IPY_MODEL_4ecf106c6ed841b8b894f447e66c41ba" } } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }