{"id":329286,"date":"2023-08-21T13:00:00","date_gmt":"2023-08-21T13:00:00","guid":{"rendered":"https:\/\/pyimagesearch.com\/?p=40241"},"modified":"2023-08-21T13:00:00","modified_gmt":"2023-08-21T13:00:00","slug":"people-counter-on-oak","status":"publish","type":"post","link":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/2023\/08\/21\/people-counter-on-oak\/","title":{"rendered":"People Counter on OAK"},"content":{"rendered":"<p class=\"syndicated-attribution\"><meta name= \\\"keywords \\\" content= \\\"\u96fb\u5b50\u8a08\u7b97\u6a5f, \u6559\u80b2, IT \u96fb\u8166\u73ed,\u96fb\u8166\u88dc\u7fd2\uff0c \u96fb\u8166\u73ed\uff0c \u5bb6\u6559\uff0c \u79c1\u4eba\u8001\u5e2b\uff0c \u8cc7\u8a0a\u6280\u8853\uff0c \u7a0b\u5e8f\u8a2d\u8a08\uff0c \u96fb\u5b50\u8a08\u7b97\u6a5f\uff0c \u904a\u6232\uff0c \u860b\u679c\uff0c \u96fb\u5f71\uff0c \u8a08\u7b97\u6a5f\uff0c\u7de8\u78bc\uff0c Java\uff0c C\/C++\uff0c JavaScript\uff0c PHP\uff0c HTML\uff0c CSS\uff0c MySQL\uff0c mobile\uff0c Android\uff0c \u52d5\u6f2b\uff0c Python\uff0c teacher\uff0c \u88dc\u7fd2\uff0c \u96fb\u8166\u88dc\u7fd2 \u8cc7\u8a0a, \u7535\u5b50\u8ba1\u7b97\u673a, IT ,Game, apple, movie, Computer,student,Java,\u6559\u80b2, ,\u5b66\u751f, \u5b66\u4e60, learn, \u6559\u5b66,  Android, apple,anime, animation, \u4fe1\u606f\u6280\u672f, \u7a0b\u5e8f\u8bbe\u8ba1, \u79fb\u52a8\u7535\u8bdd, \u8cc7\u8a0a\u79d1\u6280,Game, Jeu, Juego,Call Of Duty ,\u4f7f\u547d\u53ec\u559a , \u6e38\u620f, \u7535\u5b50\u6e38\u620f,, \u591a\u4eba\u7535\u5b50\u6e38\u620f, \u7f51\u7edc\u6e38\u620f\uff0conline\uff0conline game, \u624b\u673a\u6e38\u620f, mobile \\\"><\/p>\n<p><script src=\"https:\/\/fast.wistia.com\/embed\/medias\/rrnv0u6oos.jsonp\" async=\"\"><\/script><script src=\"https:\/\/fast.wistia.com\/assets\/external\/E-v1.js\" async=\"\"><\/script><\/p>\n<div class=\"wistia_responsive_padding\" style=\"padding:56.25% 0 0 0;position:relative;\">\n<div class=\"wistia_responsive_wrapper\" style=\"height:100%;left:0;position:absolute;top:0;width:100%;\">\n<div class=\"wistia_embed wistia_async_rrnv0u6oos seo=true videoFoam=true\" style=\"height:100%;position:relative;width:100%\">\n<div class=\"wistia_swatch\" style=\"height:100%;left:0;opacity:0;overflow:hidden;position:absolute;top:0;transition:opacity 200ms;width:100%;\"><img decoding=\"async\" src=\"https:\/\/fast.wistia.com\/embed\/medias\/rrnv0u6oos\/swatch\" style=\"filter:blur(5px);height:100%;object-fit:contain;width:100%;\" alt=\"\" aria-hidden=\"true\" onload=\"this.parentNode.style.opacity=1;\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"TOC\"\/>\n<div class=\"yoast-breadcrumbs\"><span><span><a href=\"https:\/\/pyimagesearch.com\/\">Home<\/a><\/span><\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<h2><strong>Table of Contents<\/strong><\/h2>\n<div class=\"toc\">\n<ul>\n<li id=\"TOCh2BPTitle\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h2BPTitle\">People Counter on OAK<\/a><\/li>\n<ul>\n<li id=\"TOCh3Introduction\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Introduction\">Introduction<\/a><\/li>\n<li id=\"TOCh3Development\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Development\">Configuring Your Development Environment<\/a><\/li>\n<li id=\"TOCh3Help\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Help\">Need Help Configuring Your Development Environment?<\/a><\/li>\n<li id=\"TOCh3Structure\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Structure\">Project Structure<\/a><\/li>\n<li id=\"TOCh3Script\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Script\">What Is a Script Node in DepthAI?<\/a><\/li>\n<li id=\"TOCh3Configuring\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Configuring\">Configuring the Prerequisites<\/a><\/li>\n<li id=\"TOCh3Defining\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Defining\">Defining the Utilities<\/a><\/li>\n<ul>\n<li id=\"TOCh4Imports\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4Imports\">Setting Up Imports<\/a><\/li>\n<li id=\"TOCh4VideoDetectionPipeline\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4VideoDetectionPipeline\">Video Detection Pipeline<\/a><\/li>\n<li id=\"TOCh4TrackerPipeline\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4TrackerPipeline\">Tracker Pipeline<\/a><\/li>\n<li id=\"TOCh4Helper\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4Helper\">Helper Functions<\/a><\/li>\n<li id=\"TOCh4ObjectTrackerLogic\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4ObjectTrackerLogic\">Object Tracker Logic<\/a><\/li>\n<\/ul>\n<li id=\"TOCh3Driver\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Driver\">People Counting: Python Driver Script<\/a><\/li>\n<ul>\n<li id=\"TOCh4Results\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h4Results\">Results<\/a><\/li>\n<\/ul>\n<\/ul>\n<li id=\"TOCh2Summary\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h2Summary\">Summary<\/a><\/li>\n<ul>\n<li id=\"TOCh3Citation\"><a rel=\"noopener\"  href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#h3Citation\">Citation Information<\/a><\/li>\n<\/ul>\n<\/ul>\n<\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h2BPTitle\"\/>\n<h2><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh2BPTitle\"><strong>People Counter on OAK<\/strong><\/a><\/h2>\n<p>In this tutorial, you will learn how to construct a people-counting system on an OAK device in conjunction with DepthAI and Python. This people-counting application aims to tally the number of individuals moving \u201cup\u201d or \u201cdown\u201d within a video sequence.<\/p>\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-large is-resized\"><a href=\"https:\/\/pyimagesearch.com\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-scaled.webp\"  rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-1024x538.webp?lossy=2&#038;strip=1&#038;webp=1\" alt=\"\" class=\"wp-image-41055\" width=\"700\" height=\"368\" srcset=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch.webp?size=126x66&amp;lossy=2&amp;strip=1&amp;webp=1 126w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-300x158.webp?lossy=2&amp;strip=1&amp;webp=1 300w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch.webp?size=378x199&amp;lossy=2&amp;strip=1&amp;webp=1 378w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch.webp?size=504x265&amp;lossy=2&amp;strip=1&amp;webp=1 504w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch.webp?size=630x331&amp;lossy=2&amp;strip=1&amp;webp=1 630w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-768x403.webp?lossy=2&amp;strip=1&amp;webp=1 768w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-1024x538.webp?lossy=2&amp;strip=1&amp;webp=1 1024w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/08\/oak-people-counter-pyimagesearch-scaled.webp?lossy=2&amp;strip=1&amp;webp=1 1080w\" sizes=\"(max-width: 630px) 100vw, 630px\" \/><\/a><\/figure>\n<\/div>\n<p>In counting people, our first step involves detecting individuals in a specific frame, followed by the implementation of tracking on the OAK device. For the detection phase, we will harness the capabilities of a pre-trained YOLOv8 nano model and employ a tracker within the device. <\/p>\n<p>Upon the conclusion of this tutorial, you will understand the procedures involved in developing and deploying a people-counting application on the OAK platform.<\/p>\n<p>This lesson is the 3rd in our series on <strong>OAK 102<\/strong>:<\/p>\n<ol>\n<li><a href=\"https:\/\/pyimg.co\/9qcei\"  rel=\"noreferrer noopener\"><em>Training the YOLOv8 Object Detector for OAK-D<\/em><\/a><em> <\/em><\/li>\n<li><a href=\"https:\/\/pyimg.co\/92by6\"  rel=\"noreferrer noopener\"><em>Hand Gesture Recognition with YOLOv8 on OAK-D in Near Real-Time<\/em><\/a><em> <\/em><\/li>\n<li><a href=\"https:\/\/pyimg.co\/pi5v4\"  rel=\"noreferrer noopener\"><strong><em>People Counter on OAK<\/em><\/strong><\/a><strong> (this tutorial)<\/strong><\/li>\n<\/ol>\n<p><strong>To learn how to implement and run people counting on OAK, <\/strong><strong><em>just keep reading.<\/em><\/strong><\/p>\n<p><\/p>\n<div id=\"pyi-source-code-block\" class=\"source-code-wrap\">\n<div class=\"gpd-source-code\">\n<div class=\"gpd-source-code-content\">\n        <img decoding=\"async\" src=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2020\/01\/source-code-icon.png?lossy=2&#038;strip=1&#038;webp=1\" alt=\"\"><\/p>\n<h4>Looking for the source code to this post?<\/h4>\n<p>                    <a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#download-the-code\" class=\"pyis-cta-modal-open-modal\">Jump Right To The Downloads Section <svg class=\"svg-icon arrow-right\" width=\"12\" height=\"12\" aria-hidden=\"true\" role=\"img\" focusable=\"false\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M6.8125 0.1875C6.875 0.125 6.96875 0.09375 7.09375 0.09375C7.1875 0.09375 7.28125 0.125 7.34375 0.1875L13.875 6.75C13.9375 6.8125 14 6.90625 14 7C14 7.125 13.9375 7.1875 13.875 7.25L7.34375 13.8125C7.28125 13.875 7.1875 13.9062 7.09375 13.9062C6.96875 13.9062 6.875 13.875 6.8125 13.8125L6.1875 13.1875C6.125 13.125 6.09375 13.0625 6.09375 12.9375C6.09375 12.8438 6.125 12.75 6.1875 12.6562L11.0312 7.8125H0.375C0.25 7.8125 0.15625 7.78125 0.09375 7.71875C0.03125 7.65625 0 7.5625 0 7.4375V6.5625C0 6.46875 0.03125 6.375 0.09375 6.3125C0.15625 6.25 0.25 6.1875 0.375 6.1875H11.0312L6.1875 1.34375C6.125 1.28125 6.09375 1.1875 6.09375 1.0625C6.09375 0.96875 6.125 0.875 6.1875 0.8125L6.8125 0.1875Z\" fill=\"#169FE6\"><\/path><\/svg><\/a>\n            <\/div>\n<\/div>\n<\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h2BPTitle\"\/>\n<h2><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOC\"><strong>People Counter on OAK<\/strong><\/a><\/h2>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Introduction\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Introduction\"><strong>Introduction<\/strong><\/a><\/h3>\n<p>People counting is a cutting-edge application within computer vision, focusing on accurately determining the number of individuals in a particular area or moving in specific directions, such as \u201centering\u201d or \u201cexiting.\u201d It is immensely useful across various fields (e.g., retail analytics, smart building management, and public safety).<\/p>\n<p>While counting people, techniques like object detection and tracking are deployed to scrutinize and make sense of the visuals of human movement within a frame or sequence of frames. Notably, this application is greatly enhanced by utilizing edge devices like OAK, which combines advanced computer vision and artificial intelligence (AI) capabilities.<\/p>\n<p>Imagine being able to analyze foot traffic in a retail store to optimize product placement or efficiently managing building occupancy for energy savings and safety \u2014 the applications are manifold.<\/p>\n<p><strong>Figure 1<\/strong> illustrates an example frame from a video sequence where individuals are accurately counted as they move in different directions. The annotations displaying the individual counts of people moving \u201cup\u201d or \u201cdown\u201d are shown in real-time.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><a href=\"https:\/\/lh5.googleusercontent.com\/FO1cu6ZxJNyv3d60D90XkC0_iq5ByF1Mug51tdHPJZZ4hoMo2cjOWA1Gh8oEAoSPXrduZuERiBZ9mPKB5cSUj0nEf1Fjqly1zFsJSArpDb29KznZp9X2cBH1aN3dc0Cu1YhgZwkznL1wWA01d-u9Sds\"  rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/FO1cu6ZxJNyv3d60D90XkC0_iq5ByF1Mug51tdHPJZZ4hoMo2cjOWA1Gh8oEAoSPXrduZuERiBZ9mPKB5cSUj0nEf1Fjqly1zFsJSArpDb29KznZp9X2cBH1aN3dc0Cu1YhgZwkznL1wWA01d-u9Sds\" alt=\"\" width=\"665\" height=\"500\"\/><\/a><figcaption><strong>Figure 1:<\/strong> Sample frame from a video sequence with people counting annotations (source: image by the author).<\/figcaption><\/figure>\n<\/div>\n<p>For those enthusiastic about exploring the potential of OAK devices and the array of computer vision applications they can facilitate, we suggest browsing through the <a href=\"https:\/\/pyimagesearch.com\/2022\/11\/28\/introduction-to-opencv-ai-kit-oak\/\"  rel=\"noreferrer noopener\">Introduction to OpenCV AI Kit (OAK)<\/a> tutorial on PyImageSearch, particularly the <a href=\"https:\/\/pyimagesearch.com\/2022\/11\/28\/introduction-to-opencv-ai-kit-oak\/#h3Applications\"  rel=\"noreferrer noopener\">Applications on OAK<\/a> section.<\/p>\n<p>If you\u2019ve been following our OAK series, you may recall that in <a href=\"https:\/\/pyimg.co\/92by6\"  rel=\"noreferrer noopener\">our previous post<\/a>, we delved into gesture recognition and how it can be applied to identify and interpret human gestures by deploying on an OAK device. Today, we are shifting gears to explore another fascinating application: people counting.<\/p>\n<p>Let\u2019s not delay any further but dive straight into this engaging tutorial to unravel the procedure for building, deploying, and running a people counting application on the OAK platform. But before that, let\u2019s quickly discuss the topics we will cover to implement people counting on OAK.<\/p>\n<p>In the first segment of this tutorial, we will address the essential Python packages necessary for building our people counter on OAK. The primary focus will be on DepthAI and OpenCV, as they are the cornerstone libraries for this application.<\/p>\n<p>Next, we&#8217;ll set up the foundational elements for today\u2019s project. Among these, a crucial component of DepthAI, known as the Script node, will be discussed. The Script node is instrumental in enabling the implementation and execution of custom tracking logic directly on the OAK device.<\/p>\n<p>Subsequently, we will lay out the utilities required for the smooth operation of the people counting application on OAK. This includes the construction of video detection and tracker pipeline, as well as the creation of helper functions. Moreover, we will establish the object tracker logic, designed to operate within the Script node.<\/p>\n<p>As we approach the culmination of this tutorial, we will define the primary Python driver script that integrates all the utilities and logic that have been developed. This script will be the driving force behind the successful execution of the application.<\/p>\n<p>Lastly, we will evaluate the fruits of our labor by analyzing the results of employing the people counting application on OAK to real video footage. Through this, you will witness firsthand the effectiveness and practicality of the people counter in action.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Development\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Development\"><strong>Configuring Your Development Environment<\/strong><\/a><\/h3>\n<p>To follow this guide, you need to have <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">depthai<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">opencv<\/code> libraries installed on your system.<\/p>\n<p>Luckily, all these libraries are pip-installable:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"1\">$ pip install depthai==2.21.2.0\n$ pip install opencv-python==4.6.0<\/pre>\n<p><strong>If you need help configuring your development environment for OpenCV, we <em>highly recommend<\/em> that you read our <\/strong><a href=\"https:\/\/pyimagesearch.com\/2018\/09\/19\/pip-install-opencv\/\"  rel=\"noreferrer noopener\"><strong><em>pip install OpenCV<\/em> guide<\/strong><\/a> \u2014 it will have you up and running in minutes.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Help\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Help\"><strong>Need Help Configuring Your Development Environment?<\/strong><\/a><\/h3>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/pyimagesearch.com\/pyimagesearch-university\/\"  rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"334\" src=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2021\/05\/pyimagesearch_plus_jupyter.png?lossy=2&#038;strip=1&#038;webp=1\" alt=\"Need help configuring your dev environment? Want access to pre-configured Jupyter Notebooks running on Google Colab? Be sure to join PyImageSearch University \u2014 you\u2019ll be up and running with this tutorial in minutes.\" class=\"wp-image-19836\" srcset=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2021\/05\/pyimagesearch_plus_jupyter.png?size=126x84&amp;lossy=2&amp;strip=1&amp;webp=1 126w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2021\/05\/pyimagesearch_plus_jupyter-300x200.png?lossy=2&amp;strip=1&amp;webp=1 300w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2021\/05\/pyimagesearch_plus_jupyter.png?size=378x253&amp;lossy=2&amp;strip=1&amp;webp=1 378w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2021\/05\/pyimagesearch_plus_jupyter.png?lossy=2&amp;strip=1&amp;webp=1 500w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/a><figcaption><strong>Figure 2: <\/strong>Need help configuring your dev environment? Want access to pre-configured Jupyter Notebooks running on Google Colab? Be sure to join <a href=\"https:\/\/pyimagesearch.com\/pyimagesearch-university\/\"  rel=\"noreferrer noopener\">PyImageSearch University<\/a> \u2014 you\u2019ll be up and running with this tutorial in minutes.<\/figcaption><\/figure>\n<\/div>\n<p>All that said, are you:<\/p>\n<ul>\n<li>Short on time?<\/li>\n<li>Learning on your employer\u2019s administratively locked system?<\/li>\n<li>Wanting to skip the hassle of fighting with the command line, package managers, and virtual environments?<\/li>\n<li><strong>Ready to run the code on your Windows, macOS, or Linux system <\/strong><strong><em>now<\/em><\/strong><strong>?<\/strong><\/li>\n<\/ul>\n<p>Then join <a href=\"https:\/\/pyimagesearch.com\/pyimagesearch-university\/\"  rel=\"noreferrer noopener\">PyImageSearch University<\/a> today!<\/p>\n<p><strong>Gain access to Jupyter Notebooks for this tutorial and other PyImageSearch guides that are <\/strong><strong><em>pre-configured<\/em><\/strong><strong> to run on Google Colab\u2019s ecosystem right in your web browser!<\/strong> No installation required.<\/p>\n<p>And best of all, these Jupyter Notebooks will run on Windows, macOS, and Linux!<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Structure\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Structure\"><strong>Project Structure<\/strong><\/a><\/h3>\n<p>We first need to review our project directory structure.<\/p>\n<p>Start by accessing this tutorial\u2019s <strong><em>\u201cDownloads\u201d<\/em><\/strong> section to retrieve the source code and example images.<\/p>\n<p>From there, take a look at the directory structure:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"2\">$ tree .\n.\n\u251c\u2500\u2500 main.py\n\u251c\u2500\u2500 output\n\u2502   \u2514\u2500\u2500 tracking_result_long.mp4\n\u251c\u2500\u2500 pyimagesearch\n\u2502   \u251c\u2500\u2500 __init__.py\n\u2502   \u251c\u2500\u2500 config.py\n\u2502   \u251c\u2500\u2500 track.py\n\u2502   \u2514\u2500\u2500 utils.py\n\u251c\u2500\u2500 videos\n\u2502   \u251c\u2500\u2500 example_01.mp4\n\u2502   \u2514\u2500\u2500 example_02.mp4\n\u2514\u2500\u2500 yolov8-model\n    \u2514\u2500\u2500 yolov8n\n        \u251c\u2500\u2500 yolov8n.blob\n        \u2514\u2500\u2500 yolov8n.json\n\n6 directories, 10 files<\/pre>\n<p>In the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pyimagesearch<\/code> directory, we have the following:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">__init__.py<\/code>: A special Python file that allows the directory to be treated as a package<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config.py<\/code>: The configuration file for the object tracking task<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils.py<\/code>: The utilities for running the people counter on OAK (e.g., creating video and tracker pipelines and a few other helper functions)<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">track.py<\/code>:  A script that keeps track of the movement of objects across the screen in four directions and communicates with OAK to send information on the count data<\/li>\n<\/ul>\n<p>In the core directory, we have the following:<\/p>\n<ul>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">main.py<\/code>: The main Python driver script to run people counting on OAK leveraging the pipelines and other utilities from <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">utils.py<\/code><\/li>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8-model<\/code>: Houses YOLOv8 variants model files<\/li>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8n<\/code>: A subdirectory inside the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8-model<\/code> directory\n<ul>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8n.blob<\/code>: A file inside the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8n<\/code> directory containing the weights of the YOLOv8 model.<\/li>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8n.json<\/code>: A JSON file inside the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">yolov8n<\/code> directory containing the configuration or metadata of the YOLOv8 model.<\/li>\n<\/ul>\n<\/li>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">videos<\/code>: Contains a few test video files, which the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">main.py<\/code> script will use to run people counting on OAK<\/li>\n<li><code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">output<\/code>: Houses people counting result video files <\/li>\n<\/ul>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Script\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Script\"><strong>What Is a Script Node in DepthAI?<\/strong><\/a><\/h3>\n<p>In today&#8217;s tutorial, we will use the Script node to execute people counting on the OAK device. Up to this point in our OAK series, we haven\u2019t delved into the Script node, so let\u2019s take a moment to understand its functionality.<\/p>\n<p>In the context of the DepthAI library, a Script node (<strong>Figure 3<\/strong>) is a special node within the DepthAI pipeline that allows you to execute custom Python scripts onboard the device. It&#8217;s part of the pipeline that can be used for additional processing using Python code. <\/p>\n<p>If you\u2019re not acquainted with DepthAI pipelines, then be sure to look at <a href=\"https:\/\/pyimagesearch.com\/2022\/12\/19\/oak-d-understanding-and-running-neural-network-inference-with-depthai-api\/\"  rel=\"noreferrer noopener\">this tutorial<\/a>.<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter is-resized\"><a href=\"https:\/\/lh5.googleusercontent.com\/6f4PDktURUr99leGVELoufpM0iLJA5nIXDg8bwFG-fMCRI1GS1S04M5e94TsF_f6QWQIQ9y2QatDluJI1k_od6Df5KEt3_1PeTUeu6oTOYk4xMt1nox4dakwRvUS5zfxDE1dM7HPxqs-_qKlDL_dpUM\"  rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/lh5.googleusercontent.com\/6f4PDktURUr99leGVELoufpM0iLJA5nIXDg8bwFG-fMCRI1GS1S04M5e94TsF_f6QWQIQ9y2QatDluJI1k_od6Df5KEt3_1PeTUeu6oTOYk4xMt1nox4dakwRvUS5zfxDE1dM7HPxqs-_qKlDL_dpUM\" alt=\"\" width=\"700\" height=\"315\"\/><\/a><figcaption><strong>Figure 3: <\/strong>Script Node in DepthAI (<a href=\"https:\/\/docs.luxonis.com\/projects\/api\/en\/latest\/components\/nodes\/script\/\"  rel=\"noreferrer noopener\">source<\/a>).<\/figcaption><\/figure>\n<\/div>\n<p>Following are some of the key points about the Script node:<\/p>\n<ul>\n<li><strong>Custom Processing:<\/strong> It is often used for custom data processing or algorithms not covered by the built-in nodes. For example, a Script node could perform mathematical operations, data filtering, or custom logic.<\/li>\n<\/ul>\n<ul>\n<li><strong>Python Code:<\/strong> The Script node runs Python code. You can create your own custom Python script and load it into the Script node. For instance, in this tutorial, we will write a custom Python script named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">track.py<\/code>, which will then be loaded into a Script node.<\/li>\n<\/ul>\n<ul>\n<li><strong>Interfacing with Other Nodes:<\/strong> The Script node can take input from other pipeline nodes and provide output to other nodes. For example, in this tutorial, we would <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">link<\/code> the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ObjectTracker<\/code> node output with the Script node.<\/li>\n<\/ul>\n<p>The Script node in DepthAI is very useful for incorporating custom logic and processing within the DepthAI pipeline, leveraging the flexibility of Python scripting. However, as the scripts run on the device itself, it&#8217;s important to be mindful of the resource constraints and optimize the scripts accordingly.<\/p>\n<p>If you are still interested in learning more about the Script node, you can check the Luxonis documentation <a href=\"https:\/\/docs.luxonis.com\/projects\/api\/en\/latest\/components\/nodes\/script\/\"  rel=\"noreferrer noopener\">here<\/a>.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Configuring\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Configuring\"><strong>Configuring the Prerequisites<\/strong><\/a><\/h3>\n<p>Before we start our implementation, let\u2019s review our project\u2019s configuration. For that, we will move on to the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config.py<\/code> script located in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pyimagesearch<\/code> directory.<\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config.py<\/code> script sets up the necessary paths for the YOLOv8n models, their configurations, test video, and output directories for the resulting video. It also defines the camera preview dimensions and tracking threshold.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"3\"># import necessary packages\nimport os\n\n# path to the model, test data directory, and results\nYOLOV8N_MODEL = os.path.join(\n    \"yolov8-model\", \"yolov8n\", \"yolov8n.blob\"\n)  # path to yolov8 model blob\nYOLOV8N_CONFIG = os.path.join(\n    \"yolov8-model\", \"yolov8n\", \"yolov8n.json\"\n)  # path to yolov8 model configuration<\/pre>\n<p>We start by importing the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">os<\/code> module on <strong>Line 2<\/strong>. <\/p>\n<p>On <strong>Lines 4-10<\/strong>, we set up two variables, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">YOLOV8N_MODEL<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">YOLOV8N_CONFIG<\/code>, to store the file paths of the YOLOv8 model&#8217;s blob file and configuration file, respectively. To accomplish this, we leverage <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">os.path.join<\/code> to construct the file paths. <\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"12\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"4\">INPUT_VIDEO_LONG = os.path.join(\"videos\", \"example_02.mp4\")  # path to long input video\nINPUT_VIDEO_SHORT = os.path.join(\n    \"videos\", \"example_01.mp4\"\n)  # path to short input video\nOUTPUT_VIDEO_LONG = os.path.join(\n    \"output\", \"tracking_result_long.mp4\"\n)  # path to long output video\nOUTPUT_VIDEO_SHORT = os.path.join(\n    \"output\", \"tracking_result_short.mp4\"\n)  # path to short output video<\/pre>\n<p>On <strong>Lines 12-21<\/strong>, we set the input and output video paths for the short and long video files:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">INPUT_VIDEO_LONG<\/code>: set to the file path of a longer input video<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">INPUT_VIDEO_SHORT<\/code>: set to the file path of a shorter input video<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">OUTPUT_VIDEO_LONG<\/code>: set to the file path where the inference result of the longer input video will be saved<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">OUTPUT_VIDEO_SHORT<\/code>: set to the file path where the inference result of the shorter input video will be saved<\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"23\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"5\"># minimum distance the person has to move (across the x\/y axis) to be considered a real movement\nDISTANCE_THRESHOLD = 0.25\n\n# define camera preview dimensions same as yolov8 model input size\nCAMERA_PREV_DIM = (640, 640)<\/pre>\n<p>On <strong>Line 24<\/strong>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">DISTANCE_THRESHOLD<\/code> is set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0.25<\/code>. This constant represents the minimum distance an object (a person) must move across the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code> or <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y<\/code> axis to be considered as having moved. <\/p>\n<p>This would help filter out small movements that should not be considered actual.<\/p>\n<p>Lastly, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">CAMERA_PREV_DIM<\/code> is assigned the tuple <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">(640, 640)<\/code>, which has been utilized in some of our earlier tutorials in the OAK series, so we assume you are already familiar with it.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Defining\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Defining\"><strong>Defining the Utilities<\/strong><\/a><\/h3>\n<p>Now that the configuration is defined, we can outline the utilities needed for creating video detection and object tracker pipelines with DepthAI, as well as some helper functions. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils.py<\/code> script defines several functions:<\/p>\n<ul>\n<li>Assists in creating a pipeline for object detection in videos using the YOLOv8 model<\/li>\n<li>Sets up a pipeline for tracking people by taking the output from the detection pipeline and adding a Script node for the tracking logic<\/li>\n<li>Defines a handful of helper functions for loading configuration files, annotating camera frames, and normalizing predictions<\/li>\n<\/ul>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4Imports\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4Imports\"><strong>Setting Up Imports<\/strong><\/a><\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"6\"># import the necessary packages\nimport json\nimport logging\nfrom pathlib import Path\n\nimport cv2\nimport depthai as dai\nimport numpy as np\n\nfrom pyimagesearch import config\n\n# set up logging configuration\nlogging.basicConfig(\n    level=logging.INFO, format=\"%(asctime)s - %(levelname)s - %(message)s\"\n)<\/pre>\n<p>On <strong>Lines 2-10<\/strong>, necessary packages and modules are imported:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">json<\/code>: to work with JSON data<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">logging<\/code>: module to log messages<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">Path<\/code> from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pathlib<\/code>: for handling file paths in an object-oriented way<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cv2<\/code>: for reading videos, processing images, and annotating frames<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">depthai<\/code>: is the most important module for working on interacting with OAK and building pipelines for it<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">numpy<\/code>: for array related operations<\/li>\n<li>custom <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config<\/code>: module from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pyimagesearch<\/code> for importing configuration constants<\/li>\n<\/ul>\n<p>Logging is configured on <strong>Lines 13-15<\/strong> using <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">logging.basicConfig<\/code> to display logs when required. The parameters such as logging <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">level<\/code> is set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">INFO<\/code>, meaning log messages with severity <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">INFO<\/code> and higher will be displayed. And the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">format<\/code> of the log messages to indicate the timestamp, log level, and the actual log message.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4VideoDetectionPipeline\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4VideoDetectionPipeline\"><strong>Video Detection Pipeline<\/strong><\/a><\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"18\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"7\">def video_detection_pipeline(config_path, model_path):\n    # initialize a depthai pipeline\n    pipeline = dai.Pipeline()\n    logging.info(\"Initialized DepthAI pipeline\")\n\n    # load model config file and fetch nn_config parameters\n    configPath = Path(config_path)\n    model_config = load_config(configPath)\n    nnConfig = model_config.get(\"nn_config\", {})<\/pre>\n<p>On <strong>Line 18<\/strong>, we define the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_detection_pipeline<\/code> function which <\/p>\n<ul>\n<li>initializes a DepthAI pipeline on <strong>Line 20 <\/strong><\/li>\n<li>loads some configuration parameters using the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">load_config<\/code> method for a YOLOv8n model and fetches <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn_config<\/code> from the JSON configuration file on<strong> Lines 24-26<\/strong><\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"28\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"8\">    # using nnConfig extract metadata like classes, iou and confidence threshold, number of coordinates\n    metadata = nnConfig.get(\"NN_specific_metadata\", {})\n    classes = metadata.get(\"classes\", {})\n    coordinates = metadata.get(\"coordinates\", {})\n    anchors = metadata.get(\"anchors\", {})\n    anchorMasks = metadata.get(\"anchor_masks\", {})\n    iouThreshold = metadata.get(\"iou_threshold\", {})\n    confidenceThreshold = metadata.get(\"confidence_threshold\", {})<\/pre>\n<p>In this continuation of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_detection_pipeline<\/code> function, the code snippet above retrieves <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">metadata<\/code> and other configuration parameters from the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nnConfig<\/code> dictionary. This dictionary holds configurations specific to the neural network, established on <strong>Lines 30-35<\/strong>.<\/p>\n<p>The following shell block demonstrates what the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn_config<\/code> looks like and what values it holds. From <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn_config<\/code>, we extract the key <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">NN_specific_metadata<\/code>, which contains the configuration necessary for object detection, and straightforwardly assigns each of these parameters to their respective variables (e.g., <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">classes<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">coordinates<\/code>, etc.).<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"shell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"9\">\"nn_config\": {\n        \"output_format\": \"detection\",\n        \"NN_family\": \"YOLO\",\n        \"input_size\": \"640x640\",\n        \"NN_specific_metadata\": {\n            \"classes\": 80,\n            \"coordinates\": 4,\n            \"anchors\": [],\n            \"anchor_masks\": {},\n            \"iou_threshold\": 0.5,\n            \"confidence_threshold\": 0.5\n        }<\/pre>\n<p>For instance, in the shell block mentioned above, the parameter <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">classes<\/code> is set to <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">80<\/code> because the pre-trained YOLOv8 model we are using was trained on the <a href=\"https:\/\/public.roboflow.com\/object-detection\/microsoft-coco-subset\"  rel=\"noreferrer noopener\">MS COCO dataset<\/a>, which comprises 80 classes.<\/p>\n<p>How would you like immediate access to 120K+ images curated and labeled with object bounding boxes  to train, explore, and experiment with &#8230; for free. Head over to <a href=\"https:\/\/universe.roboflow.com\/isl\/az-6mqow?ref=pyimagesearch\"  rel=\"noreferrer noopener\">Roboflow<\/a> and get a free account to grab these hand gesture images. <\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"37\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"10\">    # configure inputs for depthai pipeline\n    # since this pipeline is dealing with images an XLinkIn node is created\n    videoIN = pipeline.createXLinkIn()\n    # create a Yolo detection node\n    detectionNetwork = pipeline.create(dai.node.YoloDetectionNetwork)\n    # create a XLinkOut node for fetching the neural network outputs to host\n    nnOut = pipeline.create(dai.node.XLinkOut)\n\n    # set stream names used in queue to fetch data when the pipeline is started\n    nnOut.setStreamName(\"nn\")\n    videoIN.setStreamName(\"video_input\")<\/pre>\n<p>On <strong>Line 39<\/strong>, an <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">XLinkIn<\/code> node is instantiated within the pipeline and is assigned to the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">videoIN<\/code>. This node is the conduit for data transmission from the host system (e.g., a computer executing the script) into the DepthAI pipeline. Typically, it is employed for transmitting images or video frames into the pipeline for further processing. In this particular scenario, it is utilized for sending video frames.<\/p>\n<p>Subsequently, on <strong>Line 41<\/strong>, a node named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">YoloDetectionNetwork<\/code> is instantiated within the pipeline. This node executes the YOLO object detection algorithm on the inputted video frames.<\/p>\n<p>On <strong>Line 43<\/strong>, an <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">XLinkOut<\/code> node is instantiated within the pipeline. The variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nnOut<\/code> will transmit the data processed by the neural network from the DepthAI pipeline back to the host system.<\/p>\n<p>On <strong>Lines 46 and 47<\/strong>, the stream names for the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nnOut<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">videoIN<\/code> nodes are configured as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_input<\/code>, respectively. These names facilitate retrieving data from these particular output streams at the host end.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"49\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"111\">    # network specific settings - parameters read from config file\n    # confidence and iou threshold, classes, coordinates are set\n    # most important the model .blob file is used to load weights\n    detectionNetwork.setConfidenceThreshold(confidenceThreshold)\n    detectionNetwork.setNumClasses(classes)\n    detectionNetwork.setCoordinateSize(coordinates)\n    detectionNetwork.setAnchors(anchors)\n    detectionNetwork.setAnchorMasks(anchorMasks)\n    detectionNetwork.setIouThreshold(iouThreshold)\n    detectionNetwork.setBlobPath(model_path)\n    detectionNetwork.setNumInferenceThreads(2)\n    detectionNetwork.input.setBlocking(False)<\/pre>\n<p>The code on <strong>Lines 52-60<\/strong> configures the YOLO detection network node within the DepthAI pipeline with various parameters such as:<\/p>\n<ul>\n<li>Setting up the confidence threshold for the detection. Detections with a confidence score below this threshold will be discarded.<\/li>\n<li>The number of object classes the YOLO network has been trained to detect.<\/li>\n<li>The bounding box coordinate size (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">xmin<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ymin<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">xmax<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ymax<\/code>).<\/li>\n<li>Sets the anchor boxes, which are used in YOLO for predicting the bounding boxes of objects.<\/li>\n<li>Sets the anchor masks. These are used in YOLO to divide the prediction layer outputs among multiple scales.<\/li>\n<li>Sets the Intersection over Union (IoU) threshold for non-maximum suppression. This is used to determine which boxes should be kept vs discarded.<\/li>\n<li>The path to the model file (in blob format). This file contains the weights and architecture of the trained model.<\/li>\n<li>The number of threads to be used for running inferences. This can be tuned for performance.<\/li>\n<li>The input to non-blocking mode. In non-blocking mode, if the input queue is full, the node will not wait for it to free up space and will continue processing. This can be useful to keep data flowing through the pipeline without stalling.<\/li>\n<\/ul>\n<p>It&#8217;s important to note that both the anchors and anchor masks are left empty following the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn_config<\/code> dictionary that was previously observed.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"62\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"11\">    # linking the nodes - image node output is linked to detection node\n    # detection network node output is linked to XLinkOut input\n    videoIN.out.link(detectionNetwork.input)\n    detectionNetwork.out.link(nnOut.input)\n\n    # return the pipeline to the calling function\n    logging.info(\"DepthAI pipeline created\")\n    return detectionNetwork, pipeline<\/pre>\n<p>In the final part of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_detection_pipeline<\/code> method, we establish the data flow within the DepthAI pipeline by linking the nodes in the correct order.<\/p>\n<p>On<strong> Line 64<\/strong>, the output of the XLinkIn node (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">videoIN<\/code>) is linked to the input of the YOLO detection network node (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code>). The XLinkIn node receives video frames from the host into the pipeline. This line essentially sends the image to the detection network for inference.<\/p>\n<p>Then, the output of the YOLO detection network node (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code>) is linked to the input of the XLinkOut node (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nnOut<\/code>). This line returns the processed output (detections) to the host.<\/p>\n<p>Finally, on <strong>Lines 68 and 69<\/strong>, a log message is printed to indicate that the pipeline has been successfully created, and the function returns <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pipeline<\/code> to the calling function. <\/p>\n<p>In this instance, in conjunction with the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pipeline<\/code> object, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code> node is also returned because it will be connected to the object tracker node within the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">object_tracker_pipeline<\/code> method. This connection is essential for further processing and tracking of the detected objects.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4TrackerPipeline\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4TrackerPipeline\"><strong>Tracker Pipeline<\/strong><\/a><\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"72\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"12\">def object_tracker_pipeline(pipeline, detectionNetwork):\n    # create and configure the object tracker\n    objectTracker = pipeline.create(dai.node.ObjectTracker)\n    objectTracker.setDetectionLabelsToTrack([0])  # Track people\n    objectTracker.setTrackerType(dai.TrackerType.ZERO_TERM_COLOR_HISTOGRAM)\n    objectTracker.setTrackerIdAssignmentPolicy(\n        dai.TrackerIdAssignmentPolicy.SMALLEST_ID\n    )<\/pre>\n<p>The function <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">object_tracker_pipeline<\/code> on <strong>Line 72<\/strong> takes two arguments: <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pipeline<\/code> (the DepthAI pipeline) and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code> (the YOLO detection network node). <\/p>\n<p>Within this function, on <strong>Lines 74-79<\/strong>, an object tracker node is created and configured to track people (or objects with label <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code>) using a color histogram-based tracking algorithm and a specific ID assignment policy:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker = pipeline.create(dai.node.ObjectTracker)<\/code>: Creates an object tracker node within the DepthAI pipeline.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker.setDetectionLabelsToTrack([0])<\/code>: Configures the object tracker to only track objects with label <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code>. In object detection models, different object classes have various labels (e.g., <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">person<\/code> might be labeled as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">car<\/code> as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">1<\/code>, etc.), so this line configures the tracker to track persons.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker.setTrackerType(dai.TrackerType.ZERO_TERM_COLOR_HISTOGRAM)<\/code>: Sets the type of tracker to use. Here, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ZERO_TERM_COLOR_HISTOGRAM<\/code> is chosen, which is a specific algorithm for object tracking based on color histograms.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker.setTrackerIdAssignmentPolicy(dai.TrackerIdAssignmentPolicy.SMALLEST_ID)<\/code>: Sets the ID assignment policy for the tracker. In this case, it assigns the smallest available ID to the new objects being tracked.<\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"81\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"13\">    # link detection networks outputs to the object tracker\n    detectionNetwork.passthrough.link(objectTracker.inputTrackerFrame)\n    detectionNetwork.passthrough.link(objectTracker.inputDetectionFrame)\n    detectionNetwork.out.link(objectTracker.inputDetections)<\/pre>\n<p>Here, we connect the detection network&#8217;s output (both the raw frames and the processed detections) to the object tracker node. This allows the object tracker to use both the raw video frames and the detection results to track objects in a video stream.<\/p>\n<p>Let\u2019s dissect each of these lines to comprehend their respective functions:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork.passthrough.link(objectTracker.inputTrackerFrame)<\/code>: This links the raw frames (unprocessed by the neural network) from the detection network to the input tracker frame of the object tracker. This means that the object tracker will have access to the original video frames.<\/li>\n<\/ul>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork.passthrough.link(objectTracker.inputDetectionFrame)<\/code>: This links the same raw frames from the detection network to the input detection frame of the object tracker. Often, object trackers use additional information from the original image to improve tracking accuracy.<\/li>\n<\/ul>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork.out.link(objectTracker.inputDetections)<\/code>: This links the processed output from the detection network (bounding boxes, class labels, etc.) to the input detections of the object tracker. This means the object tracker will use the output of the detection network to keep track of the objects as they move across frames.<\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"86\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"14\">    script = pipeline.create(dai.node.Script)\n    objectTracker.out.link(script.inputs[\"tracklets\"])\n\n    with open(\"pyimagesearch\/track.py\", \"r\") as f:\n        s = f.read()\n        s = s.replace(\"THRESH_DIST_DELTA\", str(config.DISTANCE_THRESHOLD))\n        script.setScript(s)<\/pre>\n<p>This is where things become more interesting as we introduce a Script node into the DepthAI pipeline and link the object tracker\u2019s output to the Script node&#8217;s input. Additionally, we load a Python script from a file, modify it, and set it as the script for the Script node. Let\u2019s understand it step-by-step:<\/p>\n<p>On <strong>Line 86<\/strong>, a Script node is created in the DepthAI pipeline. Then in the next line, the output of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker<\/code> node (which contains tracking information of objects) is linked to the input of the Script node. The name <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklets<\/code> refers to the specific input in the Script node receiving the tracking information.<\/p>\n<p>Then on <strong>Lines 89-92<\/strong>, <\/p>\n<ul>\n<li>Open a file named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">script.py<\/code> located in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pyimagesearch<\/code> directory for reading and read the entire content of the file into a string variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">s<\/code>. <\/li>\n<li>We replace the placeholder text <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">THRESH_DIST_DELTA<\/code> within the script with the actual distance threshold value from the configuration (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config.DISTANCE_THRESHOLD<\/code>). It parameterizes the script with a specific threshold value.<\/li>\n<li>Finally, set the modified script as the script for the Script node within the DepthAI pipeline.<\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"94\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"15\">    # send tracklets to the host\n    trackerOut = pipeline.create(dai.node.XLinkOut)\n    trackerOut.setStreamName(\"out\")\n    script.outputs[\"out\"].link(trackerOut.input)<\/pre>\n<p>Next, we add an XLinkOut node to the DepthAI pipeline to send the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklets<\/code> processed by the Script node back to the host.<\/p>\n<p><strong>Lines 95 and 96<\/strong> collectively instantiate an XLinkOut node within the DepthAI pipeline and designate the stream name for this XLinkOut node as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">out<\/code>. This node and naming are crucial as it facilitates identifying and retrieving this specific data stream on the host system.<\/p>\n<p><strong>Line 97<\/strong> links the output named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">out<\/code> of the Script node to the input of the XLinkOut node. This effectively means the processed tracklets from the Script node will be sent back to the host through the XLinkOut node.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"99\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"16\">    # send RGB preview frames to the host\n    xlinkOut = pipeline.create(dai.node.XLinkOut)\n    xlinkOut.setStreamName(\"preview\")\n    objectTracker.passthroughTrackerFrame.link(xlinkOut.input)\n\n    logging.info(\"Object Tracker pipeline created\")\n    return pipeline<\/pre>\n<p>In the concluding segment of the tracker function, an additional XLinkOut node is integrated into the DepthAI pipeline. However, on this occasion, it is employed to transmit the RGB preview frames, which have undergone processing by the object tracker, back to the host system.<\/p>\n<p>Specifically, on <strong>Lines 100 and 101<\/strong>, an XLinkOut node is instantiated, and the stream name for this XLinkOut node is assigned as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">preview<\/code>.<\/p>\n<p>On<strong> Line 102<\/strong>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">objectTracker.passthroughTrackerFrame.link(xlinkOut.input)<\/code> establishes a connection between the passthrough tracker frame of the Object Tracker node and the input of the XLinkOut node. This effectively signifies that the RGB preview frames processed by the Object Tracker node will be relayed back to the host via the XLinkOut node.<\/p>\n<p>Finally,<strong> Line 105<\/strong> ensures the pipeline is returned to the invoked function.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4Helper\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4Helper\"><strong>Helper Functions<\/strong><\/a><\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"108\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"17\">def frameNorm(frame, bbox):\n    # nn data, being the bounding box locations, are in &lt;0..1> range\n    # normalized them with frame width\/height\n    normVals = np.full(len(bbox), frame.shape[0])\n    normVals[::2] = frame.shape[1]\n    return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int)\n\n\ndef load_config(config_path):\n    # open the config file and load using json module\n    with config_path.open() as f:\n        config = json.load(f)\n        logging.info(\"Config loaded from file\")\n        return config<\/pre>\n<p>The above code defines two utility functions, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frameNorm<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">load_config<\/code>, which convert neural network output to usable bounding box values and load configuration settings from a file, respectively.<\/p>\n<p>On <strong>Lines 108-113<\/strong>, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frameNorm<\/code> is defined which accepts two arguments:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frame<\/code>: A frame from a video or image <\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">bbox<\/code>: A bounding box coordinate in normalized format (ranging from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code> to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">1<\/code>)<\/li>\n<\/ul>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frameNorm<\/code> function converts the normalized coordinates of the bounding box to actual pixel values based on the frame\u2019s dimensions. This is useful for mapping the bounding box correctly on the original image. The denormalized coordinates are returned to the calling function for annotating the frame with bounding boxes.<\/p>\n<p>Then, on <strong>Lines 116-121<\/strong>, we define the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">load_config<\/code> function, which accepts a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config_path<\/code>: Path to a configuration file. This function opens a JSON configuration file, loads its contents into a Python dictionary, and returns it to the calling function.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"124\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"18\">def to_planar(arr: np.ndarray, shape: tuple) -> np.ndarray:\n    # resize and rearrange dimensions\n    return cv2.resize(arr, shape).transpose(2, 0, 1).flatten()\n\n\ndef annotateFrame(frame, detections):\n    # loops over all detections in a given frame\n    # annotates the frame with bounding box on the object\n    color = (0, 0, 255)\n    for detection in detections:\n        bbox = frameNorm(\n            frame, (detection.xmin, detection.ymin, detection.xmax, detection.ymax)\n        )\n        cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)\n    # logging.info(f\"Annotated frame with {len(detections)} detections\")\n    return frame<\/pre>\n<p>We define two more utility functions, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">to_planar<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">annotateFrame<\/code>, which are used for resizing images and annotating images with bounding boxes, respectively.<\/p>\n<p>On <strong>Lines 124-126<\/strong>, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">to_planar<\/code> function is defined as accepting an array representing an image and a tuple representing the target shape to resize the image to. The function returns an n-dimensional array.   <\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">to_planar<\/code> function:<\/p>\n<ul>\n<li>resizes the input array (image) to the target shape specified by the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">shape<\/code> parameter<\/li>\n<li>transposes the dimensions so that the channel comes first, followed by height and width<\/li>\n<li>flattens the array and returns it<\/li>\n<\/ul>\n<p>Next, on <strong>Lines 129-139<\/strong>, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">annotateFrame<\/code> method is defined that accepts two parameters:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frame<\/code>: A frame from a video or an image that is to be annotated<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detections<\/code>: A list of detections, where each detection has the properties <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">xmin<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ymin<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">xmax<\/code>, and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">ymax<\/code> representing the coordinates of the bounding box<\/li>\n<\/ul>\n<p>This function iterates through all detections in the provided <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detections<\/code> list. For each detection, it:<\/p>\n<ul>\n<li>normalizes the bounding box coordinates to pixel values using the previously mentioned <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frameNorm<\/code> function<\/li>\n<li>draws a rectangle on the input frame using the normalized bounding box coordinates<\/li>\n<\/ul>\n<p>Finally, after looping over all the detections, the function returns the annotated frame.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"142\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"19\">class TextHelper:\n    def __init__(self) -> None:\n        self.bg_color = (0, 0, 0)\n        self.color = (255, 255, 255)\n        self.text_type = cv2.FONT_HERSHEY_SIMPLEX\n        self.line_type = cv2.LINE_AA\n\n    def putText(self, frame, text, coords):\n        cv2.putText(\n            frame, text, coords, self.text_type, 1, self.bg_color, 6, self.line_type\n        )\n        cv2.putText(\n            frame, text, coords, self.text_type, 1, self.color, 2, self.line_type\n        )<\/pre>\n<p>In the aforementioned code, a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> class is defined that facilitates the addition of text annotations to frames (images). This class is meticulously crafted to append text along with a background outline to enhance visibility. Here&#8217;s a succinct explanation of the class and its methods:<\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> class is particularly useful for incorporating text annotations in images, which is especially crucial in scenarios such as object detection, where clear visibility of text against the background is essential. The code is largely self-explanatory, so you are expected to grasp the underlying logic of the operations within the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> class.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"158\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"20\">def update(previewQ, outQ, detectionNN, text, frame, counters):\n    inDet = detectionNN.get()\n\n    if previewQ.has():\n        frame = previewQ.get().getCvFrame()\n        if inDet is not None and frame is not None:\n            detections = inDet.detections\n            annotateFrame(frame, detections)\n\n    if outQ.has():\n        jsonText = str(outQ.get().getData(), \"utf-8\")\n        logging.debug(f\"Received json text: {jsonText}\")\n        counters = json.loads(jsonText)\n\n    if counters is not None:\n        text.putText(frame, f\"Up: {counters['up']}, Down: {counters['down']}\", (30, 30))\n\n    logging.info(\"Frame and counters updated\")\n    return frame, counters<\/pre>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">update<\/code> function is the last in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils.py<\/code> script and also a very important function that will help us in the Python driver script <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">main.py<\/code>. It is part of an object detection and tracking pipeline, where the objects detected and tracked in video frames are processed, and the frames are annotated with relevant information. <\/p>\n<p>Let\u2019s first break down the function parameters:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">previewQ<\/code>: A queue that holds the preview (raw) frames<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">outQ<\/code>: A queue that has tracking information<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNN<\/code>: The detection queue that receives YOLO detection output from the device<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">text<\/code>: An instance of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> class that helps in annotating the frames with text<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frame<\/code>: The current frame being processed<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counters<\/code>: A dictionary that keeps count of certain attributes (e.g., the number of objects moving up or down).<\/li>\n<\/ul>\n<p>On <strong>Line 159<\/strong>, we fetch the detection results from the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNN<\/code> queue. <\/p>\n<p>Then, on <strong>Lines 161-165<\/strong>, we:<\/p>\n<ul>\n<li>check if there are frames available in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">previewQ<\/code>\n<ul>\n<li>retrieve the next frame from the queue and convert it to a format suitable for processing by OpenCV<\/li>\n<li>if <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">inDet<\/code> (YOLO detection) is not <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code> and the frame is not <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code>\n<ul>\n<li>extracts detections from the input<\/li>\n<li>calls the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">annotateFrame<\/code> function to annotate the frame with bounding boxes around the detected objects<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>On <strong>Lines 167-170<\/strong>, <\/p>\n<ul>\n<li>check if there is tracking data is available in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">outQ<\/code>\n<ul>\n<li>retrieve the output data from the queue, convert it to a string encoded in UTF-8<\/li>\n<li>deserialize the JSON string into a Python dictionary (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counters<\/code>).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>If <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counters<\/code> is not <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code> (this means that data was successfully retrieved from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">outQ<\/code>):<\/p>\n<ul>\n<li>We use the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> instance to add text to the frame, displaying the counters for <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code> (<strong>Lines 172 and 173<\/strong>).<\/li>\n<\/ul>\n<p>Finally, the updated frame and counters dictionary are returned to the calling function.<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4ObjectTrackerLogic\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4ObjectTrackerLogic\"><strong>Object Tracker Logic<\/strong><\/a><\/h4>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"21\"># import necessary packages\nimport json\n\n# initialize the data dictionary and the counter dictionary.\n# the counter dictionary tracks the number of times objects\n# move up, or down across the screen.\ndata = {}\ncounter = {\n    \"up\": 0,\n    \"down\": 0,\n}<\/pre>\n<p>We start by setting up some initial variables that will be used later in the script for object tracking in the video, where the directions of object movements will be counted.<\/p>\n<p>On <strong>Line 2, <\/strong>we import the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">json<\/code> module. This module allows you to work with JSON data (e.g., you can read JSON data from a file or write Python data structures to a file in JSON format).<\/p>\n<p>An empty dictionary named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">data<\/code> is initialized on <strong>Line 7<\/strong>. This <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">data<\/code> dictionary will be used later to store some data.<\/p>\n<p>A dictionary named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counter<\/code> is initialized with keys <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code> on <strong>Lines 8-11<\/strong>, and all the values are set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code>. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counter<\/code> dictionary will be used to keep track of the number of times objects move in the specified directions (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code>) across the frame.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"14\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"22\"># function to send the current state of the counter as json data\ndef send():\n    # initialize a buffer with a size of 50\n    b = Buffer(50)\n    # set the buffer's data to be the counter dictionary\n    # converted to json and then encoded as bytes\n    b.setData(json.dumps(counter).encode(\"utf-8\"))\n    # send the data via the output port of the node\n    node.io[\"out\"].send(b)<\/pre>\n<p>The above code snippet defines a function named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">send<\/code>. The purpose of this function is to send the current state of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counter<\/code> dictionary as JSON data through an output port of a node. <\/p>\n<p>Here&#8217;s how the function accomplishes this:<\/p>\n<p><strong>Line 17<\/strong> creates a buffer named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">b<\/code> with a size of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">50<\/code>. In DepthAI, the term <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">Buffer<\/code> generally refers to a temporary storage area for data used to manage the data flow between the host (e.g., a PC) and the DepthAI device (e.g., OAK). When data is being processed or transferred at different rates, buffers help handle this discrepancy efficiently. In our case, the buffer <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">b<\/code> will hold the data that needs to be sent.<\/p>\n<p><strong>Line 20<\/strong> sets the data in buffer <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">b<\/code>. It takes the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counter<\/code> dictionary, converts it to a JSON-formatted string using <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">json.dumps(counter)<\/code>, and then encodes this string into bytes using UTF-8 encoding with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">.encode(\"utf-8\")<\/code>. The resulting bytes are set as the data for the buffer.<\/p>\n<p><strong>Line 22<\/strong> sends the data through a node\u2019s output port, accessing the output port named &#8220;out&#8221; of a node defined in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils<\/code> module (<strong>Line 96)<\/strong>. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">.send(b)<\/code> function sends the data in object <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">b<\/code> through the output port. This is transmitting the data to the next stage in a pipeline.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"25\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"23\"># function to handle when a tracklet (a tracked object)\n# is removed and update the counter accordingly\ndef tracklet_removed(tracklet, coords2):\n    # get the initial coordinates of the tracklet\n    coords1 = tracklet[\"coords\"]\n    # calculate the difference in x and y coordinates\n    deltaX = coords2[0] - coords1[0]\n    deltaY = coords2[1] - coords1[1]\n\n    # check if the object moved significantly along the y-axis\n    if abs(deltaY) > abs(deltaX) and abs(deltaY) > THRESH_DIST_DELTA:\n        # if so, update the counter for the appropriate direction\n        # and send the updated data\n        direction = \"up\" if 0 > deltaY else \"down\"\n        counter[direction] += 1\n        send()<\/pre>\n<p>Next, we define a function named <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklet_removed<\/code>. This function is designed to handle situations where a tracklet (a tracked object) is removed and updates a counter according to the direction in which the object moved. This function takes two parameters: <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklet<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">coords2<\/code>.<\/p>\n<p>On <strong>Line 29<\/strong>, we extract the initial coordinates of the tracklet from the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">tracklet<\/code> dictionary using the key <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">coords<\/code> and assign it to the variable <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">coords1<\/code>.<\/p>\n<p>Then, on<strong> Lines 31 and 32<\/strong>, calculate the difference in the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">x<\/code> and <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">y<\/code> coordinates between the initial position (<code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">coords1<\/code>) and the final position (<code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">coords2<\/code>) of the tracklet.<\/p>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">if<\/code> condition on <strong>Line 35 <\/strong>checks if the object moved significantly along the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y<\/code>-axis. It does this by comparing the absolute value of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deltaY<\/code> with the absolute value of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deltaX<\/code> and checking if the absolute value of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deltaY<\/code> is greater than a threshold (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">THRESH_DIST_DELTA<\/code>).<\/p>\n<p>If this condition is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">True<\/code>, the object moves vertically. On <strong>Line 38<\/strong>, the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">direction<\/code> is set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code> if <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deltaY<\/code> is negative (meaning it moved up) or <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code> if <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deltaY<\/code> is positive (meaning it moved down).<\/p>\n<p>On <strong>Lines 39 and 40<\/strong>, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counter<\/code> dictionary is updated to increment the count for the appropriate direction by <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">1<\/code>, and the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">send()<\/code> function is called to send the updated counter data.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"43\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"24\"># function to compute the centroid of a region of interest (roi)\ndef get_centroid(roi):\n    # calculate the centroid coordinates by averaging\n    # the top-left and bottom-right coordinates\n    x1 = roi.topLeft().x\n    y1 = roi.topLeft().y\n    x2 = roi.bottomRight().x\n    y2 = roi.bottomRight().y\n    return (x2 - x1) \/ 2 + x1, (y2 - y1) \/ 2 + y1<\/pre>\n<p>Then the function called <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">get_centroid<\/code> is defined, which calculates the centroid of a region of interest (roi) in a frame. The centroid is the point equidistant from all the shape boundaries. Basically, the arithmetic mean position of all the points in the shape.<\/p>\n<p>The function takes a single parameter, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">roi<\/code>, representing the region of interest. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">roi<\/code> parameter is expected to have methods <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">topLeft()<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">bottomRight()<\/code>, which return the coordinates of the top-left and bottom-right corners of the region, respectively.<\/p>\n<p><strong>Lines 47-50<\/strong> retrieve: <\/p>\n<ul>\n<li>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code> coordinate of the top-left corner of the region of interest and assigns it to variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x1<\/code>.<\/li>\n<li>The<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\"> y<\/code> coordinate of the top-left corner of the region of interest and assigns it to variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y1<\/code>.<\/li>\n<li>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code> coordinate of the bottom-right corner of the region of interest and assigns it to variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x2<\/code>.<\/li>\n<li>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y<\/code> coordinate of the bottom-right corner of the region of interest and assigns it to variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y2<\/code>.<\/li>\n<\/ul>\n<p>Finally, on <strong>Line 51<\/strong>, we compute the average of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code>-coordinates (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x1<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x2<\/code>) and the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y<\/code>-coordinates (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y1<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y2<\/code>) to find the centroid and return the centroid coordinates as a tuple.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"54\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"25\"># send initial counter data (all zeros)\nsend()\n\n# main loop for processing tracklets\nwhile True:\n    # get the current set of tracklets\n    tracklets = node.io[\"tracklets\"].get()\n\n    for t in tracklets.tracklets:\n        # handle different tracking statuses\n        if t.status == Tracklet.TrackingStatus.NEW:\n            # if the tracklet is new, initialize a new dictionary\n            # for it in the data dictionary\n            data[str(t.id)] = {}\n            # store the centroid of the tracklet's region of interest\n            data[str(t.id)][\"coords\"] = get_centroid(t.roi)\n        elif t.status == Tracklet.TrackingStatus.TRACKED:\n            # if the tracklet is currently being tracked, reset its \"lost\"\n            # counter in the data dictionary\n            data[str(t.id)][\"lostCnt\"] = 0<\/pre>\n<p>Now starts our main loop for processing the tracklets, but before that, we call the previously defined <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">send<\/code> function that sends the initial counter data (which should be all zeros at the beginning) as JSON data.<\/p>\n<p><strong>Line 58<\/strong> starts an infinite loop, typical for video or stream processing, as the code needs to keep processing the frames as they come in.<\/p>\n<p><strong>Line 60<\/strong> gets the current set of tracklets from the node&#8217;s output. A tracklet here refers to a set of observations of an object\u2019s state being tracked. This line implies that there&#8217;s an ongoing tracking process, and it retrieves the tracking data.<\/p>\n<p><strong>Line 62<\/strong> starts iterating through each tracklet in the set of tracklets.<\/p>\n<p>On<strong> Line 64<\/strong>, we check if the status of the tracklet is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">NEW<\/code>. If a tracklet is new, it means that it has been newly identified in the frame and was not there in the previous frame(s):<\/p>\n<ul>\n<li>Initialize a new dictionary for this tracklet in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">data<\/code> dictionary using the tracklet&#8217;s ID as the key (<strong>Line 67<\/strong>)<\/li>\n<li>Compute the centroid of the tracklet\u2019s region of interest using the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">get_centroid<\/code> function defined earlier and store it in the data dictionary under the key <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">coords<\/code> (<strong>Line 69<\/strong>)<\/li>\n<\/ul>\n<p>If the status of the tracklet is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TRACKED<\/code> (<strong>Line 70<\/strong>), this means that the tracklet is currently being actively tracked across frames:<\/p>\n<ul>\n<li>Reset the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">lost<\/code> counter for this tracklet in the data dictionary. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">lost<\/code> counter represents how often the tracklet has been lost during tracking. Resetting it to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">0<\/code> indicates that the tracklet is currently being successfully tracked.<\/li>\n<\/ul>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"74\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"26\">        elif t.status == Tracklet.TrackingStatus.LOST:\n            # if the tracklet is lost, increment the \"lostCnt\" counter for the\n            # tracklet to keep track of the number of frames it has been lost\n            data[str(t.id)][\"lostCnt\"] += 1\n            # if tracklet has been lost for more than 10 frames, remove it\n            if 10 &lt; data[str(t.id)][\"lostCnt\"] and \"lost\" not in data[str(t.id)]:\n                # call the tracklet_removed function to handle the removal\n                # of the lost tracklet\n                tracklet_removed(data[str(t.id)], get_centroid(t.roi))\n                # mark the tracklet as lost by setting the \"lost\" flag to True\n                data[str(t.id)][\"lost\"] = True\n        elif (t.status == Tracklet.TrackingStatus.REMOVED) and \"lost\" not in data[\n            str(t.id)\n        ]:\n            # if the tracklet is removed and not marked as lost,\n            # call the tracklet_removed function to handle the removal of the tracklet\n            tracklet_removed(data[str(t.id)], get_centroid(t.roi))<\/pre>\n<p>The above part of the code continues handling the different states of the tracklets:<\/p>\n<p>Next, on <strong>Line 74<\/strong>, we check if the tracklet is <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">LOST<\/code>. This generally means that the object being tracked is not visible or not detectable in the current frame:<\/p>\n<ul>\n<li>Increment the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">lostCnt<\/code> counter for the tracklet to keep track of the number of frames it has lost (<strong>Line 77<\/strong>).<\/li>\n<li>If the tracklet has been lost for more than 10 frames and not yet marked as lost, then the code considers this tracklet as lost (<strong>Line 79<\/strong>).\n<ul>\n<li>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklet_removed<\/code> function finds the object\u2019s direction based on its initial and last coordinates (<strong>Line 82<\/strong>).<\/li>\n<li>Mark the tracklet as lost by setting the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">\"lost\"<\/code> key in the data dictionary to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">True<\/code> (<strong>Line 84<\/strong>).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><strong>Lines 85-87<\/strong> are the final condition when a tracklet is removed. A tracklet can be removed for various reasons (e.g., if the object has left the scene):<\/p>\n<ul>\n<li>Similar to the previous block, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tracklet_removed<\/code> function calculates the changes in the object\u2019s <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">y<\/code> coordinates and updates the movement direction counters accordingly.<\/li>\n<\/ul>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Driver\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Driver\"><strong>People Counting: Python Driver Script<\/strong><\/a><\/h3>\n<p>With the configurations and utilities implemented, we can finally get into the code walkthrough of people counting with OAK.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"27\"># import the necessary packages\nimport logging\nfrom pathlib import Path\nfrom time import monotonic\n\nimport cv2\nimport depthai as dai\n\nfrom pyimagesearch import config, utils\n\n# set up logging configuration\n# level: the root logger will delegate an event to all the handlers\n# if the event\u2019s level is greater than or equal to the handler\u2019s level.\n# format: handlers use this format for the emitted log message.\nlogging.basicConfig(\n    level=logging.INFO, format=\"%(asctime)s - \" \"%(levelname)s - %(message)s\"\n)<\/pre>\n<p>We start by importing the necessary packages on <strong>Lines 2-9<\/strong>. We import <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">logging<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cv2<\/code>, <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">depthai<\/code>, and custom modules <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils<\/code> from <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pyimagesearch<\/code>. <\/p>\n<p>On<strong> Lines 15-17<\/strong>, we set up the logging system, which will be used throughout the code to log messages for information. <\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"19\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"28\"># assign the path of the video to be processed\nvideoPath = config.INPUT_VIDEO_LONG\n\n# set the video codec to use with video writer. MJPG is a format that uses motion JPEG\nfourcc = cv2.VideoWriter_fourcc(*\"MJPG\")\n# create video writer object with parameters: output video path,\n# video codec, frame rate of output video, and dimensions of video frame\nout = cv2.VideoWriter(config.OUTPUT_VIDEO_LONG, fourcc, 20.0, config.CAMERA_PREV_DIM)\nlogging.info(\"video writer initialized.\")  # logging the information<\/pre>\n<p>On <strong>Line 20<\/strong>, we assign the path of the input video where we want to run people counting. <\/p>\n<p>The path is retrieved from the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">config.INPUT_VIDEO_LONG<\/code> file.<\/p>\n<p>Next, on <strong>Lines 23 and 26<\/strong>, we set the video codec to use with the video writer and initialize the video writer, which we will use to store the inference results in a video format. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cv2.VideoWriter<\/code> accepts the path to store the inference video, video codec, output video frame rate, and video frame dimensions.<\/p>\n<p><strong>Line 27<\/strong> logs a message indicating that the video writer has been initialized.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"29\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"29\"># create video pipeline with the specified yolo configuration and model\ndetectionNetwork, pipeline = utils.video_detection_pipeline(\n    config.YOLOV8N_CONFIG, config.YOLOV8N_MODEL\n)\n\n# add object tracker to the pipeline\npipeline = utils.object_tracker_pipeline(pipeline, detectionNetwork)\nlogging.info(\"detection and tracking pipeline created.\")  # logging the information<\/pre>\n<p>In the above code snippet, we set up the video detection pipeline defined in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils.py<\/code> by passing in the YOLOv8 configuration and model files. <\/p>\n<p>Subsequently, we add the object tracker to the pipeline by passing the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">pipeline<\/code> object and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNetwork<\/code> node.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"38\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"30\"># pipeline defined, now the device is connected to. HIGH means we choose high speed USB\nwith dai.Device(pipeline, dai.UsbSpeed.HIGH) as device:\n    logging.info(\"connected to the device.\")  # logging the information\n\n    # get the output and input queues of the device\n    previewQ = device.getOutputQueue(\"preview\")\n    outQ = device.getOutputQueue(\"out\")\n    detectionNN = device.getOutputQueue(\"nn\")\n    videoQ = device.getInputQueue(\"video_input\")\n\n    counters = None  # initialize counters\n    frame = None  # initialize frame\n    text = utils.TextHelper()  # create a TextHelper object\n\n    # capture the video from the specified path\n    cap = cv2.VideoCapture(str(Path(videoPath).resolve().absolute()))<\/pre>\n<p>The above code establishes a connection to a device with the defined pipeline and high-speed USB communication. Various queues are then set up for data handling, and video capture is initiated. <\/p>\n<p><strong>Line 39<\/strong> creates a context for connecting to the device using the previously defined pipeline and specifying high-speed USB communication through <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">dai.UsbSpeed.HIGH<\/code>. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">with<\/code> statement ensures the device is properly opened and closed, managing resources efficiently.<\/p>\n<p>On <strong>Lines 43-46<\/strong>, several queues are defined:<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">preview<\/code>: An output queue retrieved from the device and assigned to the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">previewQ<\/code>. This queue will handle preview (raw) frames from the processing pipeline.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">out<\/code>: An output queue retrieved and assigned to the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">outQ<\/code>. This queue holds the tracking outputs.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">nn<\/code>: Another output queue fetched and assigned to the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">detectionNN<\/code>. It&#8217;s used for neural network detection outputs.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_input<\/code>: An input queue retrieved from the device and assigned to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">videoQ<\/code>. This queue feeds video frames from the host to the OAK device.<\/li>\n<\/ul>\n<p>On<strong> Lines 48-50<\/strong>, we initialize<\/p>\n<ul>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counters<\/code>: set to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code> and used later for counting objects.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frame<\/code>: set it to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code>, which will store the video\u2019s individual frames.<\/li>\n<li><code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">text<\/code>: an instance of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">TextHelper<\/code> class (defined earlier in the code in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils<\/code> module) for adding text annotations to the frames.<\/li>\n<\/ul>\n<p>We initialize video capture by creating a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cv2.VideoCapture<\/code> object using OpenCV on <strong>Line 53<\/strong>. The video file&#8217;s path is converted to an absolute path and passed as an argument. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">VideoCapture<\/code> object is assigned to the variable <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cap<\/code>, which will read frames from the video.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"55\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"31\">while cap.isOpened():  # as long as the video is playing\n        read_correctly, video_frame = cap.read()  # read a frame from the video\n        if not read_correctly:  # if frame not read correctly, break the loop\n            break\n        logging.info(\"frame read successfully.\")  # logging the information\n\n        img = dai.ImgFrame()  # create an ImgFrame object\n        # reshapes the video frame to 640x640 and send the reshaped frame to the img object\n        img.setData(utils.to_planar(video_frame, config.CAMERA_PREV_DIM))\n        img.setType(dai.RawImgFrame.Type.BGR888p)  # set the type of img\n        img.setTimestamp(monotonic())  # set the timestamp of img\n        img.setWidth(config.CAMERA_PREV_DIM[0])  # set the width of img\n        img.setHeight(config.CAMERA_PREV_DIM[1])  # set the height of img\n        videoQ.send(img)  # send img to the video input queue\n        logging.info(\"frame sent to video input queue.\")  # logging the information<\/pre>\n<p>In this code block, we run a loop that reads frames from an opened video file, processes them, and sends them into a DepthAI pipeline for object detection and tracking.<\/p>\n<p>On <strong>Line 55<\/strong>, a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">while<\/code> loop runs as long as the video file is open (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cap.isOpened()<\/code>).<\/p>\n<p>Then read a single frame from the video on <strong>Line 56<\/strong>. <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">read_correctly<\/code> is a boolean indicating whether the frame was read properly, and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">video_frame<\/code> is the actual frame if it was read correctly.<\/p>\n<p>If the frame is not read correctly, the loop is broken, and the processing ends (<strong>Lines 57 and 58<\/strong>). This also usually indicates the end of the video file.<\/p>\n<p>On <strong>Lines 61-68<\/strong>, <\/p>\n<ul>\n<li>We initiate an <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">ImgFrame<\/code> object to forward the frame to the OAK device.<\/li>\n<li>The video frame (<strong>Line 63<\/strong>) is first transformed into a format suitable for neural network processing using the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">to_planar<\/code> function from the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">utils<\/code> module. This function resizes the frame to the desired dimensions (<code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">640x640<\/code>) and rearranges its data into a planar format.<\/li>\n<li>We then specify the image type as BGR (Blue Green Red) in a planar format using <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">setType<\/code>.<\/li>\n<li>The current monotonic time is set as the timestamp for the frame with <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">setTimestamp<\/code>.<\/li>\n<li>The dimensions of the image, both width and height, are explicitly set using <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">setWidth<\/code> and <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">setHeight<\/code>.<\/li>\n<li>Finally, the prepared frame is sent to the OAK device via an input queue named <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">videoQ<\/code> for further processing.<\/li>\n<\/ul>\n<p>On <strong>Line 69<\/strong>, we log information that the frame was sent to the video input queue.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"true\" data-enlighter-lineoffset=\"71\" data-enlighter-title=\"People Counter on OAK\" data-enlighter-group=\"32\">        # update the frame and counters with the detected objects and texts\n        frame, counters = utils.update(\n            previewQ, outQ, detectionNN, text, frame, counters\n        )\n        # if the frame is successfully updated\n        if frame is not None:\n            out.write(frame)  # write the frame to the output video\n            cv2.imshow(\"frame\", frame)  # show the frame\n            logging.info(\n                \"frame updated with detections and text.\"\n            )  # logging the information\n\n        # if the 'q' key is pressed, break from the loop\n        if cv2.waitKey(1) == ord(\"q\"):\n            break\n\n    logging.info(\"End of the video\")  # logging the information that the video ended\n\nout.release()  # release the VideoWriter object\nlogging.info(\"video writer released.\")  # logging the information<\/pre>\n<p>The above code block continues the while loop that reads frames from a video file, and processes and displays them.<\/p>\n<p>We have reached the concluding stage of annotating and displaying the results, having completed the majority of the intensive tasks such as: <\/p>\n<ul>\n<li>Setting up both the detection and tracking pipelines<\/li>\n<li>Defining the input and output queues<\/li>\n<li>Reading video frames<\/li>\n<li>Sending frames to the device<\/li>\n<li>Defining the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">update()<\/code> method in the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">utils<\/code> module<\/li>\n<\/ul>\n<p>On <strong>Lines 72-74<\/strong>, we call the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">update<\/code> function (defined in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">utils<\/code> module), which updates the frame with object detections and text overlays like the person <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code> count. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">counters<\/code> contain the statistics or counts of the person <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">up<\/code> and <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">down<\/code>.<\/p>\n<p>Next, on <strong>Lines 76-81<\/strong>, we check if the frame is not empty (meaning that it has been successfully updated with detections and counter information)<\/p>\n<ul>\n<li>Write the processed frame to the output video file using the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">VideoWriter<\/code> object <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">out<\/code>.<\/li>\n<li>Display the processed frame in a window titled <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">frame<\/code>.<\/li>\n<\/ul>\n<p>Finally, on <strong>Lines 84-90<\/strong>, we break out of the loop (if the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">q<\/code> key is pressed), log that the frame has been updated with detections and text, and release the <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">VideoWriter<\/code> resource. <\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h4Results\"\/>\n<h4><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh4Results\"><strong>Results<\/strong><\/a><\/h4>\n<p>A full video of the demo can be seen below:<\/p>\n<p><iframe loading=\"lazy\" width=\"700\" height=\"394\" src=\"https:\/\/www.youtube.com\/embed\/quosg9Yo52I?si=Z7h2pBnOHUFvMNMA\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen=\"\"><\/iframe><\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<div id=\"pitch\" style=\"padding: 40px; width: 100%; background-color: #F4F6FA;\">\n<h3>What&#8217;s next? I recommend <a  href=\"https:\/\/pyimagesearch.com\/pyimagesearch-university\/?utm_source=blogPost&#038;utm_medium=bottomBanner&#038;utm_campaign=What%27s%20next%3F%20I%20recommend\">PyImageSearch University<\/a>.<\/h3>\n<p>\t<script src=\"https:\/\/fast.wistia.com\/embed\/medias\/kno0cmko2z.jsonp\" async><\/script><script src=\"https:\/\/fast.wistia.com\/assets\/external\/E-v1.js\" async><\/script><\/p>\n<div class=\"wistia_responsive_padding\" style=\"padding:56.25% 0 0 0;position:relative;\">\n<div class=\"wistia_responsive_wrapper\" style=\"height:100%;left:0;position:absolute;top:0;width:100%;\">\n<div class=\"wistia_embed wistia_async_kno0cmko2z videoFoam=true\" style=\"height:100%;position:relative;width:100%\">\n<div class=\"wistia_swatch\" style=\"height:100%;left:0;opacity:0;overflow:hidden;position:absolute;top:0;transition:opacity 200ms;width:100%;\"><img decoding=\"async\" src=\"https:\/\/fast.wistia.com\/embed\/medias\/kno0cmko2z\/swatch\" style=\"filter:blur(5px);height:100%;object-fit:contain;width:100%;\" alt=\"\" aria-hidden=\"true\" onload=\"this.parentNode.style.opacity=1;\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<div style=\"margin-top: 32px; margin-bottom: 32px; \">\n\t\t<strong>Course information:<\/strong><br \/>\n\t\t80 total classes \u2022 105+ hours of on-demand code walkthrough videos \u2022 Last updated: September 2023<br \/>\n\t\t<span style=\"color: #169FE6;\">\u2605\u2605\u2605\u2605\u2605<\/span> 4.84 (128 Ratings) \u2022 16,000+ Students Enrolled\n\t<\/div>\n<p><strong>I strongly believe that if you had the right teacher you could <em>master<\/em> computer vision and deep learning.<\/strong><\/p>\n<p>Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?<\/p>\n<p>That\u2019s <em>not<\/em> the case.<\/p>\n<p>All you need to master computer vision and deep learning is for someone to explain things to you in <em>simple, intuitive<\/em> terms. <em>And that\u2019s exactly what I do<\/em>. My mission is to change education and how complex Artificial Intelligence topics are taught.<\/p>\n<p>If you&#8217;re serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you\u2019ll learn how to <em>successfully<\/em> and <em>confidently<\/em> apply computer vision to your work, research, and projects. Join me in computer vision mastery.<\/p>\n<p><strong>Inside PyImageSearch University you&#8217;ll find:<\/strong><\/p>\n<ul style=\"margin-left: 0px;\">\n<li style=\"list-style: none;\">&check; <strong>80 courses<\/strong> on essential computer vision, deep learning, and OpenCV topics<\/li>\n<li style=\"list-style: none;\">&check; <strong>80 Certificates<\/strong> of Completion<\/li>\n<li style=\"list-style: none;\">&check; <strong>105+ hours<\/strong> of on-demand video<\/li>\n<li style=\"list-style: none;\">&check; <strong>Brand new courses released <em>regularly<\/em><\/strong>, ensuring you can keep up with state-of-the-art techniques<\/li>\n<li style=\"list-style: none;\">&check; <strong>Pre-configured Jupyter Notebooks in Google Colab<\/strong><\/li>\n<li style=\"list-style: none;\">&check; Run all code examples in your web browser \u2014 works on Windows, macOS, and Linux (no dev environment configuration required!)<\/li>\n<li style=\"list-style: none;\">&check; Access to <strong>centralized code repos for <em>all<\/em> 520+ tutorials<\/strong> on PyImageSearch<\/li>\n<li style=\"list-style: none;\">&check; <strong> Easy one-click downloads<\/strong> for code, datasets, pre-trained models, etc.<\/li>\n<li style=\"list-style: none;\">&check; <strong>Access<\/strong> on mobile, laptop, desktop, etc.<\/li>\n<\/ul>\n<p style=\"text-align: center;\">\n\t\t<a  class=\"button link\" href=\"https:\/\/pyimagesearch.com\/pyimagesearch-university\/?utm_source=blogPost&#038;utm_medium=bottomBanner&#038;utm_campaign=What%27s%20next%3F%20I%20recommend\" style=\"background-color: #6DC713; border-bottom: none;\">Click here to join PyImageSearch University<\/a>\n\t<\/p>\n<\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h2Summary\"\/>\n<h2><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh2Summary\"><strong>Summary<\/strong><\/a><\/h2>\n<p>In today\u2019s tutorial, we took a deep dive into building a people counting system on the OAK device, using key Python packages: DepthAI and OpenCV. <\/p>\n<p>We began by discussing the essential libraries required and proceeded to set up the prerequisites for the project. A notable feature we explored was the Script node within DepthAI, which is pivotal in executing custom tracking logic on the OAK device. <\/p>\n<p>We then defined the utilities crucial for operating the people counting application, including the video detection and tracker pipeline, along with auxiliary helper functions. We also developed object tracker logic tailored to function within the Script node. <\/p>\n<p>As a final step, we brought together all the elements by creating a Python driver script that harmonized the utilities and logic for the effective functioning of the application. To wrap up, we assessed the results of the people counting system by applying it to real video data, showcasing the practicality and efficiency of the solution developed on the OAK platform.<\/p>\n<p>We hope you found immense value and insights in today\u2019s tutorial on crafting a sophisticated people counting system using DepthAI and OpenCV on the OAK platform!<\/p>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" id=\"h3Citation\"\/>\n<h3><a href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/#TOCh3Citation\"><strong>Citation Information<\/strong><\/a><\/h3>\n<p><strong>Sharma, A. <\/strong>\u201cPeople Counter on OAK,\u201d <em>PyImageSearch<\/em>, P. Chugh, A. R. Gosthipaty, S. Huot, K. Kidriavsteva, and R. Raha, eds., 2023, <a href=\"https:\/\/pyimg.co\/pi5v4\"  rel=\"noreferrer noopener\">https:\/\/pyimg.co\/pi5v4<\/a><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"classic\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"false\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@incollection{Sharma_2023_PeopleCounter,\n  author = {Aditya Sharma},\n  title = {People Counter on {OAK}},\n  booktitle = {PyImageSearch},\n  editor = {Puneet Chugh and Aritra Roy Gosthipaty and Susan Huot and Kseniia Kidriavsteva and Ritwik Raha},\n  year = {2023},\n  url = {https:\/\/pyimg.co\/pi5v4},\n}<\/pre>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<div style=\"padding: 40px; width: 100%; background-color: #F4F6FA;\">\n<img decoding=\"async\" src=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/05\/maskcv.png?lossy=2&#038;strip=1&#038;webp=1\" alt=\"Featured Image\" style=\"width: 100%; height: auto; margin-bottom: 20px;\" srcset=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/05\/maskcv.png?size=126x70&#038;lossy=2&#038;strip=1&#038;webp=1 126w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/05\/maskcv-300x166.png?lossy=2&#038;strip=1&#038;webp=1 300w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/05\/maskcv.png?size=378x209&#038;lossy=2&#038;strip=1&#038;webp=1 378w, https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2023\/05\/maskcv.png?lossy=2&#038;strip=1&#038;webp=1 500w\" sizes=\"(max-width: 500px) 100vw, 500px\"><\/p>\n<h3>Unleash the potential of computer vision with Roboflow &#8211; Free!<\/h3>\n<ul style=\"margin-left: 0px;\">\n<li style=\"list-style: none;\">Step into the realm of the future by <a  href=\"https:\/\/roboflow.com\/?ref=pyimagesearch\">signing up or logging into your Roboflow account<\/a>. Unlock a wealth of innovative dataset libraries and revolutionize your computer vision operations.<\/li>\n<li style=\"list-style: none;\">Jumpstart your journey by choosing from our broad array of datasets, or benefit from PyimageSearch\u2019s comprehensive library, crafted to cater to a wide range of requirements.<\/li>\n<li style=\"list-style: none;\">Transfer your data to Roboflow in any of the 40+ compatible formats. Leverage cutting-edge model architectures for training, and deploy seamlessly across diverse platforms, including API, NVIDIA, browser, iOS, and beyond. Integrate our platform effortlessly with your applications or your favorite third-party tools.<\/li>\n<li style=\"list-style: none;\">Equip yourself with the ability to train a potent computer vision model in a mere afternoon. With a few images, you can import data from any source via API, annotate images using our superior cloud-hosted tool, kickstart model training with a single click, and deploy the model via a hosted API endpoint. Tailor your process by opting for a code-centric approach, leveraging our intuitive, cloud-based UI, or combining both to fit your unique needs.<\/li>\n<li style=\"list-style: none;\">Embark on your journey today with absolutely no credit card required. Step into the future with Roboflow.<\/li>\n<\/ul>\n<p style=\"text-align: center;\">\n        <a  class=\"button link\" href=\"https:\/\/roboflow.com\/?ref=pyimagesearch\" style=\"background-color: #6DC713; border-bottom: none;\">Join Roboflow Now<\/a>\n    <\/p>\n<\/div>\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<p><strong>To download the source code to this post (and be notified when future tutorials are published here on PyImageSearch), <em>simply enter your email address in the form below!<\/em><\/strong><\/p>\n<div id=\"download-the-code\" class=\"post-cta-wrap\">\n<div class=\"gpd-post-cta\">\n<div class=\"gpd-post-cta-content\">\n<div class=\"gpd-post-cta-top\">\n<div class=\"gpd-post-cta-top-image\"><img decoding=\"async\" src=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2020\/01\/cta-source-guide-1.png?lossy=2&#038;strip=1&#038;webp=1\" alt=\"\" srcset=\"https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2020\/01\/cta-source-guide-1.png?lossy=2&#038;strip=1&#038;webp=1 410w,https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2020\/01\/cta-source-guide-1.png?size=126x174&#038;lossy=2&#038;strip=1&#038;webp=1 126w,https:\/\/b2633864.smushcdn.com\/2633864\/wp-content\/uploads\/2020\/01\/cta-source-guide-1.png?size=252x348&#038;lossy=2&#038;strip=1&#038;webp=1 252w\" sizes=\"(max-width: 410px) 100vw, 410px\" \/><\/div>\n<div class=\"gpd-post-cta-top-title\">\n<h4>Download the Source Code and FREE 17-page Resource Guide<\/h4>\n<\/div>\n<div class=\"gpd-post-cta-top-desc\">\n<p>Enter your email address below to get a .zip of the code and a <strong>FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.<\/strong> Inside you&#8217;ll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!<\/p>\n<\/div><\/div>\n<div class=\"gpd-post-cta-bottom\">\n<form id=\"footer-cta-code\" class=\"footer-cta\" action=\"https:\/\/www.getdrip.com\/forms\/4130035\/submissions\" method=\"post\"  data-drip-embedded-form=\"4130035\">\n\t\t\t\t\t<input name=\"fields[email]\" type=\"email\" value=\"\" placeholder=\"Your email address\" class=\"form-control\" \/><\/p>\n<p>\t\t\t\t\t<button type=\"submit\">Download the code!<\/button><\/p>\n<div style=\"display: none;\" aria-hidden=\"true\"><label for=\"website\">Website<\/label><br \/><input type=\"text\" id=\"website\" name=\"website\" tabindex=\"-1\" autocomplete=\"false\" value=\"\" \/><\/div>\n<\/p><\/form>\n<\/p><\/div>\n<\/p><\/div>\n<\/div>\n<\/div>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/\">People Counter on OAK<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/pyimagesearch.com\/\">PyImageSearch<\/a>.<\/p>\n\n<p class=\"syndicated-attribution\"><figure class= \\\"wp-block-image alignnone \\\"><img src= \\\"http:\/\/itteacheritfreelance.hk\/test\/wordpress\/wp-content\/uploads\/2016\/05\/logo2-2.png\\\" alt=\\\"IT\u96fb\u8166\u88dc\u7fd2 java\u88dc\u7fd2 \u70ba\u5927\u5bb6\u914d\u5c0d\u96fb\u8166\u88dc\u7fd2,IT freelance, \u79c1\u4eba\u8001\u5e2b, PHP\u88dc\u7fd2,CSS\u88dc\u7fd2,XML,Java\u88dc\u7fd2,MySQL\u88dc\u7fd2,graphic design\u88dc\u7fd2,\u4e2d\u5c0f\u5b78ICT\u88dc\u7fd2,\u4e00\u5c0d\u4e00\u79c1\u4eba\u88dc\u7fd2\u548cFreelance\u81ea\u7531\u5de5\u4f5c\u914d\u5c0d\u3002\\\"\/><figcaption>\u7acb\u523b\u8a3b\u518a\u53ca\u5831\u540d\u96fb\u8166\u88dc\u7fd2\u8ab2\u7a0b\u5427!<\/figcaption><\/figure>\r\n<\/br>Find A Teacher Form:\r\n<\/br>https:\/\/docs.google.com\/forms\/d\/1vREBnX5n262umf4wU5U2pyTwvk9O-JrAgblA-wH9GFQ\/viewform?edit_requested=true#responses\r\n<\/br><\/br>Email:\r\n<\/br>public1989two@gmail.com<br><br><br><br><br><br><br>\r\n<a href=www.itsec.hk style=color:#FFFFFF;>www.itsec.hk<\/a><br>\r\n<a href=\\\"www.itsec.vip\\\" style=color:#FFFFFF;>www.itsec.vip<\/a><br>\r\n<a href=\\\"www.itseceu.uk\\\" style=color:#FFFFFF;>www.itseceu.uk<\/a><br><\/p>","protected":false},"excerpt":{"rendered":"<div class=\"mh-excerpt\"><p>Table of Contents People Counter on OAK Introduction Configuring Your Development Environment Need Help Configuring Your Development Environment? Project Structure What Is a Script Node in DepthAI? Configuring the Prerequisites Defining the Utilities Setting Up Imports Video Detection Pipeline Tracker\u2026<\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/pyimagesearch.com\/2023\/08\/21\/people-counter-on-oak\/\">People Counter on OAK<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/pyimagesearch.com\/\">PyImageSearch<\/a>.<\/p>\n<\/div>","protected":false},"author":2019,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"slim_seo":{"title":"People Counter on OAK - ITTeacherITFreelance.hk","description":"Table of Contents People Counter on OAK Introduction Configuring Your Development Environment Need Help Configuring Your Development Environment? Project Struct"},"footnotes":""},"categories":[10700],"tags":[10742,10743,10744,10749,10750,10745,10746,10747,10748],"_links":{"self":[{"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/329286"}],"collection":[{"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/2019"}],"replies":[{"embeddable":true,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=329286"}],"version-history":[{"count":1,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/329286\/revisions"}],"predecessor-version":[{"id":329287,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/329286\/revisions\/329287"}],"wp:attachment":[{"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=329286"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=329286"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itteacheritfreelance.hk\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=329286"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}