├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── application ├── include │ └── mqtt.h └── src │ ├── main.cpp │ └── mqtt.cpp ├── cmake ├── FindDemoOpenVINO.cmake ├── osdetect.cmake └── pahomqtt.cmake ├── docs └── images │ ├── arch1.png │ ├── arch2.png │ ├── arch3.png │ └── parking-lot-counter.png ├── resources ├── config.json └── diagrams.txt └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | Vagrantfile 3 | build/ 4 | *.mp4 5 | json 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "thread": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Intel Corporation. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | cmake_minimum_required(VERSION 2.8.11) 23 | project(FAC) 24 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") 25 | 26 | # Fancy colorized output messages 27 | string(ASCII 27 Esc) 28 | set(CR "${Esc}[m") 29 | set(Red "${Esc}[1;31m") 30 | set(Green "${Esc}[1;32m") 31 | set(Blue "${Esc}[1;34m") 32 | 33 | # Allow users to manually set OpenCV path if desired 34 | set(OpenCV_DIR CACHE PATH "Path to Intel OpenCV library") 35 | 36 | message(STATUS "${Blue}Intel Shopper Gaze Monitor${CR}") 37 | 38 | # Check OS 39 | message(STATUS "${Blue}Checking OS version...${CR}") 40 | 41 | set(REQUIRED_OS_ID "Ubuntu") 42 | set(REQUIRED_OS_VERSION "18.04") 43 | include(osdetect) 44 | 45 | # Check for Intel OpenVINO 46 | message(STATUS "${Blue}Checking prerequisites...${CR}") 47 | 48 | find_package(DemoOpenVINO) 49 | if(NOT OpenCV_FOUND) 50 | message(FATAL_ERROR "${Red}Intel OpenVINO was not found!\n${Blue}Have you sourced the ${Green}setupvars.sh${Blue} script provided by the Intel OpenVINO?${CR}") 51 | endif( ) 52 | 53 | include_directories(${OpenCV_INCLUDE_DIRS}) 54 | include_directories(json/single_include) 55 | include_directories(application/include) 56 | 57 | # Install paho MQTT dependency 58 | include(pahomqtt) 59 | 60 | # Application executables 61 | set(MONITOR monitor) 62 | set(DSOURCES application/src/main.cpp application/src/mqtt.cpp) 63 | add_executable(${MONITOR} ${DSOURCES}) 64 | add_dependencies(${MONITOR} pahomqtt) 65 | set_target_properties(${MONITOR} ${TRAINER} PROPERTIES COMPILE_FLAGS "-pthread -std=c++11") 66 | target_link_libraries (${MONITOR} ${OpenCV_LIBS} pthread paho-mqtt3cs) 67 | 68 | # Install 69 | install(TARGETS ${MONITOR} DESTINATION bin) 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Intel Corporation 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DISCONTINUATION OF PROJECT # 2 | This project will no longer be maintained by Intel. 3 | Intel has ceased development and contributions including, but not limited to, maintenance, bug fixes, new releases, or updates, to this project. 4 | Intel no longer accepts patches to this project. 5 | # Parking Lot Counter 6 | 7 | | Details | | 8 | |-----------------------|---------------| 9 | | Target OS: | Ubuntu\* 18.04 LTS | 10 | | Programming Language: | C++ | 11 | | Time to Complete: | 45 min | 12 | 13 | ![app image](./docs/images/parking-lot-counter.png) 14 | 15 | ## Introduction 16 | 17 | This reference implementations is designed for a parking space area with mounted camera which monitors available parking space by tracking the count of the vehicles entering and leaving the parking space area. 18 | 19 | ## Requirements 20 | 21 | ### Hardware 22 | * 6th to 8th Generation Intel® Core™ processors with Intel® Iris® Pro graphics or Intel® HD Graphics 23 | 24 | ### Software 25 | * [Ubuntu\* 18.04 LTS](http://releases.ubuntu.com/18.04/)
26 | **Note**: We recommend using a 4.14+ kernel to use this software. Run the following command to determine your kernel version: 27 | ``` 28 | uname -a 29 | ``` 30 | 31 | * OpenCL™ Runtime Package 32 | * Intel® Distribution of OpenVINO™ toolkit 2020 R3 Release 33 | 34 | ## How it Works 35 | 36 | The application uses a video source, such as a camera, to grab frames, and then uses a Deep Neural Network (DNNs) to process the data. The network detects vehicles in the frame and then if successful it tracks the vehicles leaving or entering the parking area and adjusts the count of the vehicles in the parking area for providing the information about the available parking spaces. 37 | 38 | The data can then optionally be sent to a MQTT machine to machine messaging server, as part of a parking space data analytics system. 39 | 40 | ![Code organization](./docs/images/arch3.png) 41 | 42 | The program creates three threads for concurrency: 43 | 44 | - Main thread that performs the video I/O 45 | - Worker thread that processes video frames using DNNs 46 | - Worker thread that publishes any MQTT messages 47 | 48 | 49 | ## Setup 50 | ### Get the code 51 | Clone the reference implementation: 52 | ``` 53 | sudo apt-get update && sudo apt-get install git 54 | git clone https://github.com/intel-iot-devkit/parking-lot-counter-cpp.git 55 | ``` 56 | 57 | ### Install OpenVINO 58 | Refer to https://software.intel.com/en-us/articles/OpenVINO-Install-Linux for more information about how to install and setup the Intel® Distribution of OpenVINO™ toolkit. 59 | 60 | You will need the OpenCL™ Runtime package if you plan to run inference on the GPU as shown by the 61 | instructions below. It is not mandatory for CPU inference. 62 | 63 | ## Other dependencies 64 | **Mosquitto**
65 | 66 | Mosquitto is an open source message broker that implements the MQTT protocol. The MQTT protocol provides a lightweight method of carrying out messaging using a publish/subscribe model. 67 | 68 | ## Which model to use 69 | 70 | This application uses the [pedestrian-and-vehicle-detector-adas-0001](https://docs.openvinotoolkit.org/2020.3/_models_intel_pedestrian_and_vehicle_detector_adas_0001_description_pedestrian_and_vehicle_detector_adas_0001.html) Intel® model, that can be downloaded using the **model downloader**. The **model downloader** downloads the __.xml__ and __.bin__ files that will be used by the application. 71 | 72 | To download the models and install the dependencies of the application, run the below command in the `parking-lot-counter-cpp` directory: 73 | ``` 74 | ./setup.sh 75 | ``` 76 | 77 | ### The Config File 78 | 79 | The _resources/config.json_ contains the path of video that will be used by the application as input. 80 | 81 | For example: 82 | ``` 83 | { 84 | "inputs": [ 85 | { 86 | "video":"path_to_video/video1.mp4" 87 | } 88 | ] 89 | } 90 | ``` 91 | 92 | The `path/to/video` is the path to an input video file. 93 | 94 | ### Which Input Video to use 95 | 96 | The application works with any input video. Sample videos are provided [here](https://github.com/intel-iot-devkit/sample-videos/). 97 | 98 | For first-use, we recommend using the [car-detection.mp4](https://github.com/intel-iot-devkit/sample-videos/blob/master/car-detection.mp4) video. 99 | For example: 100 | ``` 101 | { 102 | "inputs": [ 103 | { 104 | "video":"sample-videos/car-detection.mp4" 105 | } 106 | ] 107 | } 108 | ``` 109 | If the user wants to use any other video, it can be used by providing the path in the config.json file. 110 | 111 | ### Using the Camera Stream instead of video 112 | 113 | Replace `path/to/video` with the camera ID in the config.json file, where the ID is taken from the video device (the number **X** in /dev/video**X**). 114 | 115 | On Ubuntu, to list all available video devices use the following command: 116 | 117 | ``` 118 | ls /dev/video* 119 | ``` 120 | 121 | For example, if the output of above command is __/dev/video0__, then config.json would be: 122 | 123 | ``` 124 | { 125 | "inputs": [ 126 | { 127 | "video":"0" 128 | } 129 | ] 130 | } 131 | ``` 132 | 133 | ### Setup the Environment 134 | 135 | Configure the environment to use the Intel® Distribution of OpenVINO™ toolkit by exporting environment variables: 136 | 137 | ``` 138 | source /opt/intel/openvino/bin/setupvars.sh 139 | ``` 140 | 141 | __Note__: This command needs to be executed only once in the terminal where the application will be executed. If the terminal is closed, the command needs to be executed again. 142 | 143 | ### Build the application 144 | 145 | To build, go to the `parking-lot-counter-cpp` and run the following commands: 146 | 147 | ``` 148 | mkdir -p build && cd build 149 | cmake .. 150 | make 151 | ``` 152 | 153 | ## Run the application 154 | 155 | To see a list of the various options: 156 | ``` 157 | ./monitor -help 158 | ``` 159 | 160 | ### Run on the CPU 161 | To run the application with the required models using webcam, execute the below command: 162 | ``` 163 | ./monitor -m=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.bin -c=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.xml 164 | ``` 165 | 166 | To control the position of the parking entrance/exit use the `-entrance, -e` command line flag. For example: 167 | ``` 168 | ./monitor -m=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.bin -c=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.xml -e="b" 169 | ``` 170 | 171 | The `-entrance` flag controls which part of the video stream frame has to be used for counting the cars entering or exiting the parking lot: 172 | * `"b"`: bottom 173 | * `"l"`: left 174 | * `"r"`: right 175 | * `"t"`: top 176 | 177 | To control the car detection DNN confidence level, use the `-carconf, -cc` flag. For example, `-carconf=0.6` will track all cars whose DNN detection confidence level is higher than `60%`. 178 | 179 | The calculations made to track the movement of vehicles using centroids have two parameters that can be set via command line flags. `--max_distance` set the maximum distance in pixels between two related centroids. In other words, how big of a distance of movement between frames show be allowed before assuming that the object is a different vehicle. `--max_frames_gone` is the maximum number of frames to track a centroid which doesn't change, possibly due to being a parked vehicle. 180 | 181 | ### Run on the Integrated GPU 182 | 183 | This application can take advantage of the hardware acceleration in the Intel® Distribution of OpenVINO™ toolkit by using the `-b` and `-t` parameters. 184 | 185 | - To run the application on the integrated Intel® GPU in 32-bit mode, use the below command: 186 | ``` 187 | ./monitor -m=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.bin -c=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP32/pedestrian-and-vehicle-detector-adas-0001.xml -b=2 -t=1 188 | ``` 189 | **FP32**: FP32 is single-precision floating-point arithmetic uses 32 bits to represent numbers. 8 bits for the magnitude and 23 bits for the precision. For more information, [click here](https://en.wikipedia.org/wiki/Single-precision_floating-point_format)
190 | 191 | - To run the application on the integrated Intel® GPU in 16-bit mode, use the below command: 192 | ``` 193 | ./monitor -m=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP16/pedestrian-and-vehicle-detector-adas-0001.bin -c=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP16/pedestrian-and-vehicle-detector-adas-0001.xml -b=2 -t=2 194 | ``` 195 | **FP16**: FP16 is half-precision floating-point arithmetic uses 16 bits. 5 bits for the magnitude and 10 bits for the precision. For more information, [click here](https://en.wikipedia.org/wiki/Half-precision_floating-point_format)
196 | 197 | ### Run on the Intel® Neural Compute Stick 198 | 199 | To run the application using the Intel® Neural Compute Stick, use the below command: 200 | ``` 201 | ./monitor -m=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP16/pedestrian-and-vehicle-detector-adas-0001.bin -c=/opt/intel/openvino/deployment_tools/open_model_zoo/tools/downloader/intel/pedestrian-and-vehicle-detector-adas-0001/FP16/pedestrian-and-vehicle-detector-adas-0001.xml -b=2 -t=3 202 | ``` 203 | 204 | ### Machine to Machine Messaging with MQTT 205 | 206 | #### Install Mosquitto Broker 207 | ``` 208 | sudo apt-get update 209 | sudo apt-get install mosquitto mosquitto-clients 210 | ``` 211 | 212 | If you wish to use a MQTT server to publish data, you should set the following environment variables before running the program: 213 | ``` 214 | export MQTT_SERVER=localhost:1883 215 | export MQTT_CLIENT_ID=cvservice 216 | ``` 217 | 218 | Change the `MQTT_SERVER` to a value that matches the MQTT server you are connecting to. 219 | 220 | You should change the `MQTT_CLIENT_ID` to a unique value for each monitoring station, so you can track the data for individual locations. For example: 221 | ``` 222 | export MQTT_CLIENT_ID=parkinglot1337 223 | ``` 224 | 225 | If you want to monitor the MQTT messages sent to your local server, and you have the mosquitto client utilities installed, you can run the following command on a new terminal while the application is running: 226 | ``` 227 | mosquitto_sub -t 'parking/counter' 228 | ``` 229 | 230 | -------------------------------------------------------------------------------- /application/include/mqtt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Intel Corporation. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef MQTT_H_INCLUDED 25 | #define MQTT_H_INCLUDED 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | extern "C" { 36 | #include "MQTTClient.h" 37 | #include "MQTTClientPersistence.h" 38 | } 39 | 40 | #define QOS 1 41 | #define TIMEOUT 1000L 42 | 43 | struct mqtt_service_config 44 | { 45 | std::string server; 46 | std::string client_id; 47 | std::string topic; 48 | std::string username; 49 | std::string password; 50 | std::string cert; 51 | std::string cert_key; 52 | std::string ca_root; 53 | }; 54 | 55 | std::string std_getenv(const std::string &name); 56 | std::pair get_mqtt_config(); 57 | int mqtt_start(MQTTClient_messageArrived* msgrcv); 58 | void mqtt_close(); 59 | void mqtt_connect(); 60 | void mqtt_disconnect(); 61 | int mqtt_publish(std::string const &topic, std::string const &message); 62 | void mqtt_subscribe(std::string const &topic); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /application/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Intel Corporation. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | // OpenCV includes 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | // MQTT 49 | #include "mqtt.h" 50 | 51 | using namespace std; 52 | using namespace cv; 53 | using namespace dnn; 54 | using json = nlohmann::json; 55 | json jsonobj; 56 | 57 | // OpenCV-related variables 58 | Mat frame, blob; 59 | VideoCapture cap; 60 | int delay = 5; 61 | Net net; 62 | 63 | // Application parameters 64 | String model; 65 | String config; 66 | float carconf; 67 | int backendId; 68 | int targetId; 69 | string entrance; 70 | int max_distance; 71 | int max_frames_gone; 72 | int rate; 73 | 74 | // Flag to control background threads 75 | atomic keepRunning(true); 76 | 77 | // Flag to handle UNIX signals 78 | static volatile sig_atomic_t sig_caught = 0; 79 | 80 | // MQTT parameters 81 | const string topic = "parking/counter"; 82 | 83 | // Car contains information about trajectory of tracked car 84 | struct Car { 85 | int id; 86 | vector traject; 87 | bool counted; 88 | bool gone; 89 | int direction; 90 | }; 91 | 92 | // tracked_cars tracks detected cars by their ids 93 | map tracked_cars; 94 | 95 | // id is a counter used to generate ids for tracked centroids 96 | int id = 0; 97 | 98 | // Centroid is the center point of detected car rectangle 99 | struct Centroid { 100 | int id; 101 | Point p; 102 | int gone_count; 103 | }; 104 | 105 | // centroids maps centroids by their ids 106 | map centroids; 107 | 108 | // Total cars in and out of the parking 109 | int total_in = 0; 110 | int total_out = 0; 111 | 112 | // ParkingInfo contains information about available parking spaces 113 | struct ParkingInfo 114 | { 115 | int total_in; 116 | int total_out; 117 | map centroids; 118 | }; 119 | 120 | // currentInfo contains the latest ParkingInfo as tracked by the application 121 | ParkingInfo currentInfo; 122 | // nextImage provides queue for captured video frames 123 | queue nextImage; 124 | // currentPerf stores the label which contains application performance information 125 | String currentPerf; 126 | // Mutexes used in program to control thread access to shared variables 127 | mutex m, m1, m2; 128 | 129 | const char* keys = 130 | "{ help | | Print help message. }" 131 | "{ device d | 0 | camera device number. }" 132 | "{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera. }" 133 | "{ model m | | Path to .bin file of model containing face recognizer. }" 134 | "{ config c | | Path to .xml file of model containing network configuration. }" 135 | "{ carconf cc | 0.5 | Confidence factor for car detection required. }" 136 | "{ backend b | 0 | Choose one of computation backends: " 137 | "0: automatically (by default), " 138 | "1: Halide language (http://halide-lang.org/), " 139 | "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " 140 | "3: OpenCV implementation }" 141 | "{ target t | 0 | Choose one of target computation devices: " 142 | "0: CPU target (by default), " 143 | "1: OpenCL, " 144 | "2: OpenCL fp16 (half-float precision), " 145 | "3: VPU }" 146 | "{ entrance e | b | Plane axis for parking entrance and exit division mark. Possible options: " 147 | "b: Bottom frame, " 148 | "t: Top frame, " 149 | "l: Left frame, " 150 | "r: Right frame }" 151 | "{ max_distance md | 200 | Max distance in pixels between two related centroids. }" 152 | "{ max_frames_gone mg | 25 | Max number of frames to track the centroid which does not change. }" 153 | "{ rate r | 0.5 | Number of seconds between data updates to MQTT server. }"; 154 | 155 | // nextImageAvailable returns the next image from the queue in a thread-safe way 156 | Mat nextImageAvailable() { 157 | Mat rtn; 158 | m.lock(); 159 | if (!nextImage.empty()) { 160 | rtn = nextImage.front(); 161 | nextImage.pop(); 162 | } 163 | m.unlock(); 164 | return rtn; 165 | } 166 | 167 | // addImage adds an image to the queue in a thread-safe way 168 | void addImage(Mat img) { 169 | m.lock(); 170 | if (nextImage.size() < 300) { 171 | nextImage.push(img); 172 | } 173 | m.unlock(); 174 | } 175 | 176 | // getCurrentInfo returns the most-recent ParkingInfo for the application. 177 | ParkingInfo getCurrentInfo() { 178 | m2.lock(); 179 | ParkingInfo info; 180 | info = currentInfo; 181 | m2.unlock(); 182 | return info; 183 | } 184 | 185 | // updateInfo updates the current ParkingInfo for the application to the latest detected values 186 | void updateInfo() { 187 | m2.lock(); 188 | currentInfo.total_in = total_in; 189 | currentInfo.total_out = total_out; 190 | currentInfo.centroids = centroids; 191 | m2.unlock(); 192 | } 193 | 194 | // resetInfo resets the current ParkingInfo for the application. 195 | void resetInfo() { 196 | m2.lock(); 197 | currentInfo.total_in = 0; 198 | currentInfo.total_out = 0; 199 | currentInfo.centroids = map(); 200 | m2.unlock(); 201 | } 202 | 203 | // getCurrentPerf returns a display string with the most current performance stats for the Inference Engine. 204 | string getCurrentPerf() { 205 | string perf; 206 | m1.lock(); 207 | perf = currentPerf; 208 | m1.unlock(); 209 | return perf; 210 | } 211 | 212 | // savePerformanceInfo sets the display string with the most current performance stats for the Inference Engine. 213 | void savePerformanceInfo() { 214 | m1.lock(); 215 | vector times; 216 | double freq = getTickFrequency() / 1000; 217 | double t = net.getPerfProfile(times) / freq; 218 | string label = format("Car inference time: %.2f ms", t); 219 | currentPerf = label; 220 | m1.unlock(); 221 | } 222 | 223 | // Publish MQTT message with a JSON payload 224 | void publishMQTTMessage(const string& topic, const ParkingInfo& info) { 225 | ostringstream s; 226 | s << "{\"TOTAL_IN\": \"" << info.total_in << "\" \"TOTAL_OUT\": \"" << info.total_out << "\"}"; 227 | string payload = s.str(); 228 | mqtt_publish(topic, payload); 229 | string msg = "MQTT message published to topic: " + topic; 230 | syslog(LOG_INFO, "%s", msg.c_str()); 231 | syslog(LOG_INFO, "%s", payload.c_str()); 232 | } 233 | 234 | // Message handler for the MQTT subscription for any desired control channel topic 235 | int handleMQTTControlMessages(void *context, char *topicName, int topicLen, MQTTClient_message *message) { 236 | string topic = topicName; 237 | string msg = "MQTT message received: " + topic; 238 | syslog(LOG_INFO, "%s", msg.c_str()); 239 | return 1; 240 | } 241 | 242 | /* closestCentroid finds the id of the tracked centroid which is the closest to the point passed in as parameter. 243 | The function uses Euclidean distance as a measure of the closeness of the points and returns both as a pair */ 244 | pair closestCentroid(const Point p, const map centroids) { 245 | int id = 0; 246 | double dist = DBL_MAX; 247 | 248 | for (map::const_iterator it = centroids.begin(); it != centroids.end(); ++it) { 249 | int _id = it->second.id; 250 | Point _p = it->second.p; 251 | 252 | // If the movement is horizontal, only consider centroids with some small Y coordinate fluctuation 253 | if (entrance.compare("l") == 0 || entrance.compare("r") == 0) { 254 | if ((_p.y < (p.y-70)) || (_p.y > (p.y+70))){ 255 | continue; 256 | } 257 | } 258 | 259 | // If the movement is vertical, only consider centroids with some small X coordinate fluctuation 260 | if (entrance.compare("b") == 0 || entrance.compare("t") == 0) { 261 | if (_p.x < (p.x-50) || _p.x > (p.x+50)){ 262 | continue; 263 | } 264 | } 265 | 266 | double dx = double(p.x - _p.x); 267 | double dy = double(p.y - _p.y); 268 | double _dist = sqrt(dx*dx + dy*dy); 269 | 270 | if (_dist < dist) { 271 | dist = _dist; 272 | id = _id; 273 | } 274 | } 275 | return make_pair(id, dist); 276 | } 277 | 278 | // addCentroid adds a new centroid to the list of tracked centroids and increments id counter 279 | void addCentroid(Point p) { 280 | Centroid c; 281 | c.id = id; 282 | c.p = p; 283 | c.gone_count = 0; 284 | centroids[c.id] = c; 285 | id++; 286 | } 287 | 288 | // removeCentroid removes existing centroid from the list of tracked centroids 289 | void removeCentroid(int id) { 290 | centroids.erase(id); 291 | // Find gone centroid id in tracked cars map and mark it as gone 292 | if (tracked_cars.find(id) != tracked_cars.end()){ 293 | tracked_cars[id].gone = true; 294 | } 295 | } 296 | 297 | // updateCentroids takes detected centroid points and updates tracked centroids 298 | void updateCentroids(vector points) { 299 | if (points.size() == 0) { 300 | for (map::iterator it = centroids.begin(); it != centroids.end(); ++it) { 301 | it->second.gone_count++; 302 | if (it->second.gone_count > max_frames_gone) { 303 | removeCentroid(it->second.id); 304 | } 305 | } 306 | return; 307 | } 308 | 309 | if (centroids.empty()) { 310 | for(const auto& p: points) { 311 | addCentroid(p); 312 | } 313 | } else { 314 | set checked_points; 315 | set checked_centroids; 316 | // Iterate through all detected points and update tracked centroid positions 317 | for(vector::size_type i = 0; i != points.size(); i++) { 318 | pair closest = closestCentroid(points[i], centroids); 319 | 320 | /* If the distance from the point to the closest centroid is too large, don't associate them together. 321 | Also avoid associating with centroid which already has a different association */ 322 | if (closest.second > max_distance || (checked_points.find(closest.first) != checked_points.end())) { 323 | continue; 324 | } 325 | // Update position of the closest centroid 326 | centroids[closest.first].p = points[i]; 327 | centroids[closest.first].gone_count = 0; 328 | // Add centroids to checked points 329 | checked_points.insert(i); 330 | checked_centroids.insert(closest.first); 331 | } 332 | 333 | /* Iterate through all *already tracked* centroids and increment their gone frame count, 334 | if they weren't updated from the list of detected centroid points */ 335 | for (map::iterator it = centroids.begin(); it != centroids.end(); ++it) { 336 | // If centroid wasn't updated, we assume it's missing from the frame 337 | if (checked_centroids.find(it->second.id) == checked_centroids.end()) { 338 | it->second.gone_count++; 339 | if (it->second.gone_count > max_frames_gone) { 340 | removeCentroid(it->second.id); 341 | } 342 | } 343 | } 344 | 345 | /* Iterate through *detected* centroids and add the ones which werent associated 346 | with any of the tracked centroids and add start tracking them */ 347 | for(vector::size_type i = 0; i != points.size(); i++) { 348 | // If detected point was not associated with any already tracked centroids we add it in 349 | if (checked_points.find(i) == checked_points.end()) { 350 | addCentroid(points[i]); 351 | } 352 | } 353 | } 354 | return; 355 | } 356 | 357 | /* carMovement calculates movement of the car along particular movement axis according to the entrance position 358 | as a mean value of all the previous poisitions of the car centroids and returns it */ 359 | int carMovement(vector traject, string entrance) { 360 | int mean_movement = 0; 361 | 362 | for(vector::size_type i = 0; i != traject.size(); i++) { 363 | // When movement is horizontal only consider trajectory along X axis 364 | if (entrance.compare("l") == 0 || entrance.compare("r") == 0) { 365 | mean_movement = mean_movement + traject[i].x; 366 | } 367 | // When movement is vertical only consider trajectory along Y axis 368 | if (entrance.compare("b") == 0 || entrance.compare("t") == 0) { 369 | mean_movement = mean_movement + traject[i].y; 370 | } 371 | } 372 | 373 | // Calculate average centroid movement 374 | mean_movement = mean_movement / traject.size(); 375 | return mean_movement; 376 | } 377 | 378 | /* carDirection calculates the direction of the car movement along particular movement axis based on 379 | the entrance position as a difference between current car's position and its previous movement */ 380 | int carDirection(Point p, int movement, string entrance) { 381 | int direction = 0; 382 | 383 | // When movement is horizontal only consider trajectory along X axis 384 | if (entrance.compare("l") == 0 || entrance.compare("r") == 0) { 385 | direction = p.x - movement; 386 | } 387 | // When movement is vertical only consider trajectory along Y axis 388 | if (entrance.compare("b") == 0 || entrance.compare("t") == 0) { 389 | direction = p.y - movement; 390 | } 391 | return direction; 392 | } 393 | 394 | // centroids2Cars iterates through all centroids and associates them with tracked_cars 395 | void centroids2Cars() { 396 | // Iterate through updated centroids and update tracked car counts 397 | for (map::iterator it = centroids.begin(); it != centroids.end(); ++it) { 398 | int id = it->second.id; 399 | Point p = it->second.p; 400 | 401 | Car car; 402 | // If the centroid is not tracked yet, add it to tracked_cars 403 | if (tracked_cars.find(id) == tracked_cars.end()) { 404 | car.id = id; 405 | car.traject.push_back(p); 406 | car.counted = false; 407 | car.gone = false; 408 | car.direction = 0; 409 | } 410 | else { 411 | car = tracked_cars[id]; 412 | // Calculate mean movement from car trajectory 413 | int movement = carMovement(car.traject, entrance); 414 | // Add the centroid to the car trajectory 415 | car.traject.push_back(p); 416 | // Calculate car direction based on trajectory and current position 417 | car.direction = carDirection(p, movement, entrance); 418 | } 419 | tracked_cars[id] = car; 420 | } 421 | } 422 | 423 | // updateCarTotals iterates through all tracked cars and updates total counts both in and out of the parking 424 | void updateCarTotals() { 425 | for (map::iterator it = tracked_cars.begin(); it != tracked_cars.end(); ++it) { 426 | int id = it->second.id; 427 | int direction = it->second.direction; 428 | bool gone = it->second.gone; 429 | bool counted = it->second.counted; 430 | 431 | if (!counted) { 432 | if (!gone) { 433 | if (entrance.compare("t") == 0 || entrance.compare("l") == 0) { 434 | // Direction is "positive" i.e. movement along Y/X axis goes up 435 | if (direction > 0) { 436 | total_in++; 437 | it->second.counted = true; 438 | } 439 | } 440 | 441 | if (entrance.compare("b") == 0 || entrance.compare("r") == 0) { 442 | // Direction is "negative" i.e. movement along Y/X axis goes down 443 | if (direction < 0) { 444 | total_in++; 445 | it->second.counted = true; 446 | } 447 | } 448 | } 449 | else { 450 | if (entrance.compare("t") == 0 || entrance.compare("l") == 0) { 451 | // "Negative" direction i.e. car centroid coords along movement axis go down 452 | if (direction < 0) { 453 | total_out++; 454 | tracked_cars.erase(id); 455 | } 456 | } 457 | 458 | if (entrance.compare("b") == 0 || entrance.compare("r") == 0) { 459 | // "Positive" direction i.e. car centroid coords along movement axis go up 460 | if (direction > 0) { 461 | total_out++; 462 | tracked_cars.erase(id); 463 | } 464 | } 465 | } 466 | } 467 | else { 468 | if (gone) { 469 | tracked_cars.erase(id); 470 | } 471 | } 472 | } 473 | } 474 | 475 | // Function called by worker thread to process the next available video frame. 476 | void frameRunner() { 477 | while (keepRunning.load()) { 478 | Mat next = nextImageAvailable(); 479 | if (!next.empty()) { 480 | // Convert to 4d vector as required by vehicle detection model and detect cars 481 | blobFromImage(next, blob, 1.0, Size(672, 384)); 482 | net.setInput(blob); 483 | Mat result = net.forward(); 484 | 485 | // Get detected cars and in and out counts 486 | vector frame_cars; 487 | int count_in = 0; 488 | int count_out = 0; 489 | float* data = (float*)result.data; 490 | 491 | for (size_t i = 0; i < result.total(); i += 7) { 492 | int label = (int)data[i + 1]; 493 | float confidence = data[i + 2]; 494 | if (label == 1 && confidence > carconf) { 495 | int left = (int)(data[i + 3] * frame.cols); 496 | int top = (int)(data[i + 4] * frame.rows); 497 | int right = (int)(data[i + 5] * frame.cols); 498 | int bottom = (int)(data[i + 6] * frame.rows); 499 | int width = right - left + 1; 500 | int height = bottom - top + 1; 501 | 502 | // Check whether the detected object is going out of range of the frame 503 | if(bottom >= next.rows) { 504 | height = next.rows - top; 505 | } 506 | frame_cars.push_back(Rect(left, top, width, height)); 507 | } 508 | } 509 | 510 | vector frame_centroids; 511 | vector car_detections; 512 | for(auto const& fc: frame_cars) { 513 | // Make sure the car rect is completely inside the main Mat 514 | if ((fc & Rect(0, 0, next.cols, next.rows)) != fc) { 515 | continue; 516 | } 517 | 518 | // Detected car rectangle dimensions 519 | int width = fc.width; 520 | int height = fc.height; 521 | // If detected rectangle is too small, skip it 522 | if (width < 70 || height < 70) { 523 | continue; 524 | } 525 | 526 | /* Sometimes detected car rectangle stretches way over the actual car dimensions 527 | so we clip the sizes of the rectangle to avoid skewing the centroid positions */ 528 | int w_clip = 200; 529 | if (width > w_clip) { 530 | if ((fc.x + w_clip) < frame.cols) { 531 | width = w_clip; 532 | } 533 | } 534 | else if ((fc.x + width) > frame.cols) { 535 | width = frame.cols - fc.x; 536 | } 537 | 538 | int h_clip = 350; 539 | if (height > h_clip) { 540 | if ((fc.y + h_clip) < frame.rows){ 541 | height = h_clip; 542 | } 543 | } 544 | else if ((fc.y + height) > frame.rows) { 545 | height = frame.rows - fc.y; 546 | } 547 | 548 | // Calculate detected car centroid coordinates 549 | int x = fc.x + static_cast(width/2.0); 550 | int y = fc.y + static_cast(height/2.0); 551 | 552 | // Append detected centroid and draw rectangle 553 | frame_centroids.push_back(Point(x,y)); 554 | car_detections.push_back(Rect(fc.x, fc.y, width, height)); 555 | } 556 | 557 | // Update tracked centroids using the centroids detected in the frame 558 | updateCentroids(frame_centroids); 559 | 560 | // Associate centroids with tracked cars 561 | centroids2Cars(); 562 | // Update tracked cars total counters 563 | updateCarTotals(); 564 | // Update analytics and performance info 565 | updateInfo(); 566 | savePerformanceInfo(); 567 | } 568 | } 569 | 570 | cout << "Video processing thread stopped" << endl; 571 | } 572 | 573 | // Function called by worker thread to handle MQTT updates. Pauses for rate second(s) between updates. 574 | void messageRunner() { 575 | while (keepRunning.load()) { 576 | ParkingInfo info = getCurrentInfo(); 577 | publishMQTTMessage(topic, info); 578 | this_thread::sleep_for(chrono::seconds(rate)); 579 | } 580 | 581 | cout << "MQTT sender thread stopped" << endl; 582 | } 583 | 584 | // Signal handler for the main thread 585 | void handle_sigterm(int signum) 586 | { 587 | // We only handle SIGTERM and SIGKILL here 588 | if (signum == SIGTERM) { 589 | cout << "Interrupt signal (" << signum << ") received" << endl; 590 | sig_caught = 1; 591 | } 592 | } 593 | 594 | int main(int argc, char** argv) 595 | { 596 | std::string conf_file = "../resources/config.json", input; 597 | std::ifstream confFile(conf_file); 598 | confFile>>jsonobj; 599 | auto obj = jsonobj["inputs"]; 600 | input = obj[0]["video"]; 601 | 602 | // Parse command line arguments 603 | CommandLineParser parser(argc, argv, keys); 604 | parser.about("Use this script to using OpenVINO."); 605 | if (argc == 1 || parser.has("help")) 606 | { 607 | parser.printMessage(); 608 | return 0; 609 | } 610 | 611 | model = parser.get("model"); 612 | config = parser.get("config"); 613 | carconf = parser.get("carconf"); 614 | backendId = parser.get("backend"); 615 | targetId = parser.get("target"); 616 | entrance = parser.get("entrance"); 617 | rate = parser.get("rate"); 618 | max_distance = parser.get("max_distance"); 619 | max_frames_gone = parser.get("max_frames_gone"); 620 | 621 | // Connect MQTT messaging 622 | int result = mqtt_start(handleMQTTControlMessages); 623 | if (result == 0) { 624 | syslog(LOG_INFO, "MQTT started."); 625 | } else { 626 | syslog(LOG_INFO, "MQTT NOT started: have you set the ENV varables?"); 627 | } 628 | 629 | mqtt_connect(); 630 | 631 | // Read in car detection model 632 | net = readNet(model, config); 633 | net.setPreferableBackend(backendId); 634 | net.setPreferableTarget(targetId); 635 | 636 | // open video capture source 637 | if (input.size() == 1 && *(input.c_str()) >= '0' && *(input.c_str()) <= '9') 638 | cap.open(std::stoi(input)); 639 | else 640 | { 641 | cap.open(input); 642 | double fps = cap.get(CAP_PROP_FPS); 643 | delay = 1000/fps; 644 | } 645 | if (!cap.isOpened()) { 646 | cerr << "ERROR! Unable to open video source\n"; 647 | return -1; 648 | } 649 | 650 | // Register SIGTERM signal handler 651 | signal(SIGTERM, handle_sigterm); 652 | 653 | // Start worker threads 654 | thread t1(frameRunner); 655 | thread t2(messageRunner); 656 | 657 | // Read video input data 658 | for (;;) { 659 | cap.read(frame); 660 | 661 | if (frame.empty()) { 662 | keepRunning = false; 663 | cout << "Video Finished\n"; 664 | break; 665 | } 666 | 667 | addImage(frame); 668 | 669 | // Print Inference Engine performance info 670 | string label = getCurrentPerf(); 671 | putText(frame, label, Point(0, 25), FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(255, 255, 255)); 672 | 673 | ParkingInfo info = getCurrentInfo(); 674 | label = format("Cars In: %d Cars Out: %d", info.total_in, info.total_out); 675 | putText(frame, label, Point(0, 45), FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(255, 255, 255)); 676 | // Draw car centroids 677 | for (map::const_iterator it = info.centroids.begin(); it != info.centroids.end(); ++it) { 678 | circle(frame, it->second.p, 5.0, CV_RGB(0, 255, 0), 2); 679 | label = format("[%d, %d]", it->second.p.x, it->second.p.y); 680 | putText(frame, label, Point(it->second.p.x+5, it->second.p.y), 681 | FONT_HERSHEY_SIMPLEX, 0.5, CV_RGB(0, 255, 0)); 682 | } 683 | 684 | imshow("Parking Lot Counter", frame); 685 | 686 | if (waitKey(delay) >= 27 || sig_caught) { 687 | cout << "Attempting to stop background threads" << endl; 688 | keepRunning = false; 689 | break; 690 | } 691 | } 692 | 693 | // Wait for the threads to finish 694 | t1.join(); 695 | t2.join(); 696 | cap.release(); 697 | 698 | // Disconnect MQTT messaging 699 | mqtt_disconnect(); 700 | mqtt_close(); 701 | 702 | return 0; 703 | } 704 | -------------------------------------------------------------------------------- /application/src/mqtt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Intel Corporation. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "mqtt.h" 25 | 26 | bool mqtt_initialized = false; 27 | MQTTClient client; 28 | MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; 29 | MQTTClient_message pubmsg = MQTTClient_message_initializer; 30 | MQTTClient_deliveryToken token; 31 | MQTTClient_SSLOptions sslOptions = MQTTClient_SSLOptions_initializer; 32 | 33 | std::string std_getenv(const std::string &name) 34 | { 35 | auto value = getenv(name.c_str()); 36 | return value != nullptr ? std::string(value) : std::string(); 37 | } 38 | 39 | void mqtt_init(mqtt_service_config const &config) 40 | { 41 | if (mqtt_initialized) 42 | { 43 | return; 44 | } 45 | 46 | std::vector server_c( 47 | config.server.c_str(), 48 | config.server.c_str() + config.server.size() + 1 49 | ); 50 | 51 | std::vector client_id_c( 52 | config.client_id.c_str(), 53 | config.client_id.c_str() + config.client_id.size() + 1 54 | ); 55 | 56 | MQTTClient_create(&client, 57 | &server_c[0], 58 | &client_id_c[0], 59 | MQTTCLIENT_PERSISTENCE_NONE, 60 | NULL); 61 | 62 | // connection options 63 | conn_opts.keepAliveInterval = 20; 64 | conn_opts.cleansession = 1; 65 | 66 | if (!config.username.empty()) 67 | { 68 | std::vector username_c( 69 | config.username.c_str(), 70 | config.username.c_str() + config.username.size() + 1 71 | ); 72 | 73 | conn_opts.username = &username_c[0]; 74 | } 75 | 76 | if (!config.password.empty()) 77 | { 78 | std::vector password_c( 79 | config.password.c_str(), 80 | config.password.c_str() + config.password.size() + 1 81 | ); 82 | 83 | conn_opts.password = &password_c[0]; 84 | } 85 | 86 | // ssl options 87 | if (!config.cert.empty() && !config.cert_key.empty() && !config.ca_root.empty()) 88 | { 89 | std::vector cert_c( 90 | config.cert.c_str(), 91 | config.cert.c_str() + config.cert.size() + 1 92 | ); 93 | 94 | std::vector cert_key_c( 95 | config.cert_key.c_str(), 96 | config.cert_key.c_str() + config.cert_key.size() + 1 97 | ); 98 | 99 | std::vector ca_root_c( 100 | config.ca_root.c_str(), 101 | config.ca_root.c_str() + config.ca_root.size() + 1 102 | ); 103 | 104 | sslOptions.keyStore = &cert_c[0]; 105 | sslOptions.privateKey = &cert_key_c[0]; 106 | sslOptions.trustStore = &ca_root_c[0]; 107 | } 108 | else 109 | { 110 | sslOptions.enableServerCertAuth = false; 111 | }; 112 | conn_opts.ssl = &sslOptions; 113 | 114 | mqtt_initialized = true; 115 | }; 116 | 117 | int mqtt_start(MQTTClient_messageArrived* msgrcv) 118 | { 119 | auto mqtt_config_result = get_mqtt_config(); 120 | 121 | mqtt_service_config mqtt_config; 122 | bool mqtt_config_valid; 123 | 124 | std::tie(mqtt_config, mqtt_config_valid) = mqtt_config_result; 125 | 126 | if (!mqtt_config_valid) 127 | { 128 | return 1; 129 | } 130 | 131 | mqtt_init(mqtt_config); 132 | MQTTClient_setCallbacks(client, NULL, NULL, msgrcv, NULL); 133 | return 0; 134 | } 135 | 136 | void mqtt_close() 137 | { 138 | if (mqtt_initialized) 139 | { 140 | //std::cout << "Closing MQTT..." << std::endl; 141 | MQTTClient_destroy(&client); 142 | } 143 | }; 144 | 145 | void mqtt_connect() 146 | { 147 | if (mqtt_initialized) 148 | { 149 | int rc; 150 | if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) 151 | { 152 | //std::cout << "Failed to connect to MQTT server, return code:" << rc << std::endl; 153 | return; 154 | } 155 | } 156 | } 157 | 158 | void mqtt_disconnect() 159 | { 160 | if (mqtt_initialized) 161 | { 162 | MQTTClient_disconnect(client, 10000); 163 | } 164 | } 165 | 166 | int mqtt_publish(std::string const &topic, std::string const &message) 167 | { 168 | if (!mqtt_initialized) { 169 | return -1; 170 | } 171 | 172 | std::vector topic_c( 173 | topic.c_str(), 174 | topic.c_str() + topic.size() + 1 175 | ); 176 | 177 | std::vector message_c( 178 | message.c_str(), 179 | message.c_str() + message.size() + 1 180 | ); 181 | 182 | pubmsg.payload = &message_c[0]; 183 | pubmsg.payloadlen = strlen(&message_c[0]); 184 | pubmsg.qos = QOS; 185 | pubmsg.retained = 0; 186 | int result = MQTTClient_publishMessage(client, &topic_c[0], &pubmsg, &token); 187 | if (result != 0) { // MQTTCLIENT_SUCCESS = 0 188 | return result; 189 | } 190 | return MQTTClient_waitForCompletion(client, token, TIMEOUT); 191 | } 192 | 193 | void mqtt_subscribe(std::string const &topic) 194 | { 195 | if (!mqtt_initialized) { 196 | return; 197 | } 198 | 199 | MQTTClient_subscribe(client, topic.c_str(), 1); 200 | } 201 | 202 | std::pair get_mqtt_config() 203 | { 204 | const auto mqtt_server = std_getenv("MQTT_SERVER"); 205 | const auto mqtt_client_id = std_getenv("MQTT_CLIENT_ID"); 206 | const auto mqtt_username = std_getenv("MQTT_USERNAME"); 207 | const auto mqtt_password = std_getenv("MQTT_PASSWORD"); 208 | const auto mqtt_cert = std_getenv("MQTT_CERT"); 209 | const auto mqtt_cert_key = std_getenv("MQTT_CERT_KEY"); 210 | const auto mqtt_ca_root = std_getenv("MQTT_CA_ROOT"); 211 | 212 | auto config_valid = false; 213 | 214 | mqtt_service_config config = { 215 | mqtt_server, 216 | mqtt_client_id, 217 | mqtt_username, 218 | mqtt_password, 219 | mqtt_cert, 220 | mqtt_cert_key, 221 | mqtt_ca_root}; 222 | 223 | if (!mqtt_server.empty() && !mqtt_client_id.empty()) 224 | { 225 | config_valid = true; 226 | } 227 | 228 | std::pair result = { 229 | config, 230 | config_valid 231 | }; 232 | 233 | return result; 234 | } 235 | -------------------------------------------------------------------------------- /cmake/FindDemoOpenVINO.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Intel Corporation. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | # Use system variables set by CV SDK 23 | if(NOT DEFINED OpenCV_DIR AND DEFINED ENV{OpenCV_DIR}) 24 | set(OpenCV_DIR "$ENV{OpenCV_DIR}") 25 | endif() 26 | 27 | if(NOT DEFINED OpenCV_DIR) 28 | if(EXISTS "${INTEL_CVSDK_DIR}/opencv/share/OpenCV" ) 29 | set(OpenCV_DIR "${INTEL_CVSDK_DIR}/opencv/share/OpenCV") 30 | elseif(EXISTS "$ENV{INTEL_CVSDK_DIR}/opencv/share/OpenCV") 31 | set(OpenCV_DIR "$ENV{INTEL_CVSDK_DIR}/opencv/share/OpenCV") 32 | elseif(EXISTS "/opt/intel/computer_vision_sdk/opencv/share/OpenCV") 33 | set(OpenCV_DIR "/opt/intel/computer_vision_sdk/opencv/share/OpenCV") 34 | endif() 35 | endif() 36 | 37 | # Avoid system installed OpenCV 38 | find_package(OpenCV PATHS ${OpenCV_DIR} QUIET NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) 39 | 40 | if(OpenCV_FOUND) 41 | message(STATUS "Intel OpenVINO was found") 42 | message(STATUS "OpenCV_INCLUDE_DIRS=${OpenCV_INCLUDE_DIRS}") 43 | message(STATUS "OpenCV_LIBS=${OpenCV_LIBS}") 44 | endif() 45 | -------------------------------------------------------------------------------- /cmake/osdetect.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Intel Corporation. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | # Detects current OS 23 | 24 | find_program(LSB_RELEASE lsb_release) 25 | if(LSB_RELEASE-NOTFOUND) 26 | message(FATAL_ERROR "${Red}Unsupported OS! This demo is for ${REQUIRED_OS_ID} ${REQUIRED_OS_VERSION}.${CR}") 27 | endif() 28 | execute_process(COMMAND ${LSB_RELEASE} -is 29 | OUTPUT_VARIABLE LSB_RELEASE_ID 30 | OUTPUT_STRIP_TRAILING_WHITESPACE 31 | ) 32 | if(NOT LSB_RELEASE_ID STREQUAL ${REQUIRED_OS_ID}) 33 | message(STATUS "OS found: ${LSB_RELEASE_ID}") 34 | message(FATAL_ERROR "${Red}Unsupported OS! This demo is for ${REQUIRED_OS_ID} ${REQUIRED_OS_VERSION}.${CR}") 35 | endif() 36 | execute_process(COMMAND ${LSB_RELEASE} -rs 37 | OUTPUT_VARIABLE LSB_RELEASE_VERSION 38 | OUTPUT_STRIP_TRAILING_WHITESPACE 39 | ) 40 | if(LSB_RELEASE_VERSION VERSION_LESS ${REQUIRED_OS_VERSION}) 41 | message(FATAL_ERROR "${Red}Unsupported OS version! This demo is for ${REQUIRED_OS_ID} ${REQUIRED_OS_VERSION}.${CR}") 42 | elseif(LSB_RELEASE_VERSION VERSION_GREATER ${REQUIRED_OS_VERSION}) 43 | message(WARNING "${Red}This demo has not been tested on ${REQUIRED_OS_ID} ${LSB_RELEASE_VERSION}.${CR}") 44 | endif() 45 | -------------------------------------------------------------------------------- /cmake/pahomqtt.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018 Intel Corporation. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | # This module installs paho mqtt as an external dependency 23 | # Depends on openSSL 24 | 25 | include(ExternalProject) 26 | find_package(Git REQUIRED) 27 | find_package(PkgConfig REQUIRED) 28 | pkg_search_module(OPENSSL REQUIRED openssl) 29 | 30 | ExternalProject_add( 31 | pahomqtt 32 | GIT_REPOSITORY "https://github.com/eclipse/paho.mqtt.c.git" 33 | GIT_TAG "v1.3.0" 34 | UPDATE_COMMAND "" 35 | PATCH_COMMAND "" 36 | SOURCE_DIR "${CMAKE_BINARY_DIR}/paho-src" 37 | CMAKE_ARGS -DPAHO_WITH_SSL=TRUE -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/paho-build 38 | ) 39 | 40 | include_directories(${CMAKE_BINARY_DIR}/paho-build/include) 41 | link_directories(${CMAKE_BINARY_DIR}/paho-build/lib) 42 | -------------------------------------------------------------------------------- /docs/images/arch1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel-iot-devkit/parking-lot-counter-cpp/61e7b45feb6bb36c8af4bca2103426a8107c743d/docs/images/arch1.png -------------------------------------------------------------------------------- /docs/images/arch2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel-iot-devkit/parking-lot-counter-cpp/61e7b45feb6bb36c8af4bca2103426a8107c743d/docs/images/arch2.png -------------------------------------------------------------------------------- /docs/images/arch3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel-iot-devkit/parking-lot-counter-cpp/61e7b45feb6bb36c8af4bca2103426a8107c743d/docs/images/arch3.png -------------------------------------------------------------------------------- /docs/images/parking-lot-counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel-iot-devkit/parking-lot-counter-cpp/61e7b45feb6bb36c8af4bca2103426a8107c743d/docs/images/parking-lot-counter.png -------------------------------------------------------------------------------- /resources/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputs":[ 3 | { 4 | "video":"../resources/car-detection.mp4" 5 | } 6 | ] 7 | } 8 | 9 | -------------------------------------------------------------------------------- /resources/diagrams.txt: -------------------------------------------------------------------------------- 1 | graph TD 2 | A[C++ code] -->B[OpenCV] 3 | B --> C[DNN module] 4 | C-->V[OpenVINO backend] 5 | C -->O[OpenCV backend] 6 | V-->D[CPU] 7 | V-->E[GPU 32-bit] 8 | V-->F[GPU 16-bit] 9 | V-->G[VPU] 10 | 11 | graph TD 12 | A[C++] -->B[OpenCV] 13 | B --> C[DNN module] 14 | C-->Caffe 15 | C -->TensorFlow 16 | C -->Torch 17 | C -->Darknet 18 | C-->V(OpenVINO IR) 19 | 20 | graph TD 21 | subgraph cloud 22 | S[MQTT Server] 23 | end 24 | subgraph local 25 | subgraph Frame thread 26 | Q[Take next available frame from queue]-->P[Track vehicles DNN] 27 | P-->U[Update stats and alert] 28 | end 29 | subgraph Main thread 30 | C[Capture frames]-->MQ[Add to queue] 31 | MQ-->W[Window with display including stats] 32 | end 33 | subgraph Messaging thread 34 | M[Send MQTT messages to server with stats]-- optional -->S 35 | end 36 | end 37 | 38 | graph LR 39 | subgraph Model creation 40 | A[Define model architecture] -->|train|B[Trained model] 41 | T(Training data set) -->B 42 | end 43 | subgraph Model usage 44 | B --> |optimize|M[Optimized model] 45 | M --> |inference|C[Result data] 46 | R(Runtime data) -->C 47 | end 48 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | : ' 2 | * Copyright (c) 2018 Intel Corporation. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | ' 23 | 24 | sudo apt-get update 25 | sudo apt-get install mosquitto mosquitto-clients 26 | sudo apt-get install libssl-dev 27 | sudo ssh install cmd 28 | 29 | if [ -d "json" ] 30 | then 31 | sudo rm -r json 32 | fi 33 | 34 | git clone https://github.com/nlohmann/json # Cloning json parser from github 35 | 36 | mkdir resources 37 | cd resources 38 | wget -O car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/car-detection.mp4 39 | 40 | cd /opt/intel/openvino/deployment_tools/tools/model_downloader 41 | sudo ./downloader.py --name pedestrian-and-vehicle-detector-adas-0001 42 | --------------------------------------------------------------------------------