Skip to content

Commit 2617c92

Browse files
Working Code: Yolo detection , Kalman tracking
1 parent db63ff4 commit 2617c92

30 files changed

+2936
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.vscode/
2+
build/
3+
videos/project_track_and_detect.avi
4+
model/yolov3.weights

CMakeLists.txt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
cmake_minimum_required(VERSION 2.6)
2+
set(CMAKE_CXX_STANDARD 14)
3+
project(ObjectDetectionAndTracking)
4+
5+
set(CXX_FLAGS "-Wall")
6+
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++14 -pthread -g")
7+
8+
include_directories(include
9+
${PROJECT_SOURCE_DIR}/include
10+
${PROJECT_SOURCE_DIR}/include/Detector
11+
${PROJECT_SOURCE_DIR}/include/Tracker
12+
)
13+
14+
find_package(OpenCV 4.1 REQUIRED)
15+
find_package (Eigen3 REQUIRED)
16+
include_directories(${OpenCV_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIRS})
17+
link_directories(${OpenCV_LIBRARY_DIRS})
18+
add_definitions(${OpenCV_DEFINITIONS})
19+
20+
add_executable(detect_and_track src/main.cpp
21+
src/frame_grabber.cpp
22+
src/yolo_object_detector.cpp
23+
src/frame_writer.cpp
24+
src/hungarian.cpp
25+
src/kalman_filter.cpp
26+
src/kalman_track.cpp
27+
src/tracker.cpp
28+
src/object_tracker.cpp
29+
)
30+
31+
target_link_libraries(detect_and_track ${OpenCV_LIBRARIES} ${EIGEN3_DIR})

README.md

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,168 @@
1-
# MultiThreaded-Object-Detection-and-Tracking-cpp
1+
# MultiThreaded Object Detection and Tracking
2+
3+
## Description
4+
5+
This repository contains an implementation of a multithreaded application for detecting objects and tracking objects in a user-specified video. Example output of running the application on the input video (`videos/input_video.mp4`) is the resulting video (`videos/project_track_and_detect.avi`).
6+
7+
The whole detection and tracking application runs on four asynchronous tasks. First task start reading frames using Frame Grabber object (`frame_grabber.h`) and pushes them them into a message queue (`message_queue.h`) which is thread safe implementation using conditional variables. The second tasks detects the frames by pulling the frames from message queue as they become available using Object Detector (`object_detector.h`). The class (`object_detector.h`) is a abtract class. After detection of each frame, the detector puts detected frame along with the required input data for tracking into another queue having a special message is type of tracking messages (`tracking_msg.h`). This two tasks run in parallel.
8+
Once they are completed another task is started, which pulls the tracking messages from another queue and does the tracking using Object Tracker (`object_tracker.h`). The Object Tracker uses the Tracker (`tracker.h`) which provides functionality to update the tracks. The tracks are implements as abstract class (`track.h`). Each frame after tracking is put in the output queue.
9+
After the completion of tracking task, the final task is started using Frame Writer object (`frame_writer.h`) which pulls the messages from the output queue and writes them to the user-specified output video file.
10+
11+
### Red dot on the objects shows the tracking and the green dot shows the position measured by detecting the object and calculating center of bounding box
12+
13+
<img src="data/output.gif"/>
14+
15+
## Detectors:
16+
17+
This project provides the abstact implementation of object detector, hence any custom object detector can be added. Currently, YOLO3-Object-Detector is implemented(`yolo_object_detector.h`).
18+
19+
### YOLO3-Object-Detector
20+
21+
This object detector is trained on coco dataset and can recognize upto 80 different classes of objects in the moving frame.
22+
23+
## Trackers: Kalman
24+
25+
The project provides abstract implementation of the tracker in form of tracks, hence any custom object tracker can be added. Currently, kalman tracker (`kalman_track.h`) is implemented using Kalman-Filter (`kalman_filter.h`). An assignment problem is used to associate the objects detected by detectors and tracked by trackers. Here it is solved using Hungarian Algorithm.
26+
27+
Tracking consist of two major steps:
28+
1. Prediction: Predict the object locations in the next frame.
29+
2. Data association: Use the predicted locations to associate detections across frames to form tracks.
30+
31+
## Dependencies for Running Locally
32+
* cmake >= 3.7
33+
* All OSes: [click here for installation instructions](https://cmake.org/install/)
34+
* make >= 4.1 (Linux, Mac), 3.81 (Windows)
35+
* Linux: make is installed by default on most Linux distros
36+
* Mac: [install Xcode command line tools to get make](https://developer.apple.com/xcode/features/)
37+
* Windows: [Click here for installation instructions](http://gnuwin32.sourceforge.net/packages/make.htm)
38+
* gcc/g++ >= 5.4
39+
* Linux: gcc / g++ is installed by default on most Linux distros
40+
* Mac: same deal as make - [install Xcode command line tools](https://developer.apple.com/xcode/features/)
41+
* Windows: recommend using [MinGW](http://www.mingw.org/)
42+
* OpenCV >= 4.1
43+
* The OpenCV 4.1.0 source code can be found [here](https://github.com/opencv/opencv/tree/4.1.0)
44+
* Eigen3
45+
* Install using "sudo apt-get install libeigen3"
46+
47+
## Basic Build Instructions
48+
49+
1. Clone this repo.
50+
51+
2. Run the following commands to download object detection models from [Darknet](https://pjreddie.com/darknet/)
52+
53+
```
54+
cd CppND-Capstone
55+
mkdir model && cd model
56+
wget https://pjreddie.com/media/files/yolov3.weights
57+
wget https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg?raw=true -O ./yolov3.cfg
58+
```
59+
60+
3. Make a build directory in the top level directory: `mkdir build && cd build`
61+
62+
4. Compile: `cmake .. && make`
63+
64+
5. Run it: `./detect_and_track` (**Runtime can vary depending on the number of frames in the video**)
65+
66+
6. Progress can tracked while the program is running
67+
```
68+
Model Loaded Successfully!
69+
Loaded 80 Class Names
70+
Initialised the tracker
71+
Read 10 frames from total 1260 frames
72+
Read 20 frames from total 1260 frames
73+
Read 30 frames from total 1260 frames
74+
Read 40 frames from total 1260 frames
75+
Read 50 frames from total 1260 frames
76+
....
77+
....
78+
Detected 10 frames
79+
Read 520 frames from total 1260 frames
80+
Detected 20 frames
81+
Read 530 frames from total 1260 frames
82+
Detected 30 frames
83+
Read 540 frames from total 1260 frames
84+
....
85+
....
86+
Tracked 1240 frames
87+
Tracked 1250 frames
88+
Written 1253 frames
89+
------- Done !! -------
90+
91+
7. The video can be played from (`videos/project_track_and_detect.avi`) after the program terminates
92+
93+
## Code Instructions
94+
95+
1. Change the model_config file path and model_weight_path in main.cpp
96+
2. Change the input and output video file path in main.cpp
97+
2. Turn the track flag to false in main.cpp if you want to run detection only
98+
99+
## Satisfied Rubric Points
100+
101+
### The application reads data from a file and process the data.
102+
103+
* The project reads frames from a video file using the `ObjectDetector` class (`line 46 include/Detector/object_detector.h`).
104+
105+
### The application implements the abstract classes and pure virtual functions.
106+
107+
* The object detector class (`include/Detector/object_detector.h`) and the tracks class (`include/Tracker/track.h`).
108+
109+
### Use of Object Oriented Programming techniques.
110+
111+
* The classes `FrameGrabber` (`src/frame_grabber.cpp` , `include/frame_grabber.h`), `FrameWriter` (`src/frame_writer.cpp` , `include/frame_writer.h`), `MessageQueue` (`include/message_queue.h`), `YOLODetector` (`include/Detector/yolo_object_detector.h`, `src/yolo_object_detector.cpp`), `KalmanFilter` (`include/Tracker/kalman_filter.h` , `src/kalman_filter.cpp`), `Tracker` (`include/Tracker/tracker.h` , `src/tracker.cpp`) and `KalmanTrack` (`include/Tracker/kalman_track.h` , `src/kalman_track.cpp`).
112+
113+
### Use of Inheritence techniques.
114+
115+
* The class `YOLODetector` is inherited from parent class `ObjectDetector` (`line 15 include/Detector/yolo_object_detector.h`).
116+
* The class `KalmanTrack` is inherited from parent class `Track` (`line 8 include/Tracker/yolo_object_detector.h`).
117+
118+
### Classes use appropriate access specifiers for class members.
119+
120+
* Example of class-specific access specifiers in `Tracker` class definition (`include/Tracker/tracker.h`).
121+
122+
### Use of Overloaded Functions.
123+
124+
* The function getTracks() is overloaded in `Tracker` class (`line 47,49 include/Tracker/tracker.h`).
125+
126+
### Templates generalize functions.
127+
128+
* The `MessageQueue` is a templated class (`include/message_queue.h`).
129+
130+
### Use of references in function declarations.
131+
132+
* Example of method that uses reference in function declaration is the implementation of the `YOLODetector` constructor (`line 4 src/yolo_object_detector.cpp`).
133+
134+
### Use of scope / Resource Acquisition Is Initialization (RAII) where appropriate.
135+
136+
* Example use of RAII can be seen acquisition and release of locks in `MessageQueue` (`lines 19,55,62 include/message_queue.h`).
137+
138+
### Use of move semantics to move data, instead of copying it, where possible.
139+
140+
* Example use of move semantics is the pushing and removing of items in `MessageQueue` (`line 41 include/message_queue.h`).
141+
142+
### Use of smart pointers.
143+
144+
* Example use of the smart pointers (std::unique_ptr) is (`line 7 src/yolo_object_detector.cpp`), (`line 15 src/tracker.cpp`).
145+
146+
### Use of multithreading.
147+
148+
* The project uses two asynchronous tasks (std::async) (`lines 54,57,63,68 src/main.cpp`).
149+
150+
### Use of condition variable.
151+
152+
* A condition variable is used in the project in the implementation of message queue (`lines 38,28 include/message_queue.h`).
153+
154+
## References
155+
156+
* The video used in this repository was taken from the repository [udacity/CarND-Vehicle-Detection](udacity/CarND-Vehicle-Detection).
157+
158+
* OpenCV YOLO Object Detection (https://github.com/opencv/opencv/blob/master/samples/dnn/object_detection.cpp)
159+
160+
* Kalman Filter - [Artificial Intelligence for Robotics](https://www.udacity.com/course/artificial-intelligence-for-robotics--cs373#) Udacity Course
161+
162+
* Hungarian Algorithm - [here](http://www.mathworks.com/matlabcentral/fileexchange/6543-functions-for-the-rectangular-assignment-problem)
163+
164+
* Motion-Based Multiple Object Tracking - [here](https://in.mathworks.com/help/vision/examples/motion-based-multiple-object-tracking.html)
165+
166+
* Multiple Object Tracking - [here](https://in.mathworks.com/help/vision/ug/multiple-object-tracking.html)
167+
168+
* Computer Vision for tracking - [here](https://towardsdatascience.com/computer-vision-for-tracking-8220759eee85)

content

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Description
2+
3+
Obstacle detection or tracking moving objects is one of the most interesting topics in computer vision.
4+
This problem could be solved in two steps:
5+
6+
1, Detecting moving objects in each frame
7+
2, Tracking historical objects with some tracking algorithms
8+
9+
An assignment problem is used to associate the objects detected by detectors and tracked by trackers.
10+
11+
We can found some introduction of this framework here,
12+
https://towardsdatascience.com/computer-vision-for-tracking-8220759eee85
13+
14+
Another example in more detail with matlab code (detecors and trackers may different),
15+
https://www.mathworks.com/help/vision/examples/motion-based-multiple-object-tracking.html
16+
17+
Here I implemented a highly efficient and scalable C++ framework to combine the state of art
18+
deep-learning based detectors (Yolo3 demoed here) and correlation filters based trackers
19+
(KCF, Kalman Filters also implemented). The assignment problem is solved by hungarian algorithm.
20+
21+
# Detectors: Yolo3
22+
23+
Yolo3 is trained for detecting bottles, cans and hands in this demo. It is trained with Keras
24+
and compiled with tensorflow C++ into a DLL. (YOLO3.DLL under bin folder). CUDA 9.2 is used to
25+
compile the tensorflow C++ library.
26+
27+
YOLO3.DLL can be compiled with the code under folder detectors and tensorflow C++ library if
28+
you have tensorflow C++ library compiled.
29+
30+
# Trackers: Kalman Filter and KCF
31+
32+
Kalman filter is fast but less accurate. KCF is accurate but much slower.
33+
They are implemnted with exactly same interface, so we can easily switch from one to another
34+
in the project.
35+
36+
# Live Camera Capture: OpenCV
37+
38+
OpenCV is used to capture live video frames and used for image preprocessing.
39+
40+
# Misc
41+
42+
YOLO3.DLL and the model file are too big. They can be downloaded from following link:
43+
https://pan.baidu.com/s/1CPYU2o59vutoq-OJewObRw

data/output.gif

21.3 MB
Loading

include/Detector/object_detector.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#ifndef OBJECT_DETECTOR_H
2+
#define OBJECT_DETECTOR_H
3+
4+
#include <fstream>
5+
#include <string>
6+
#include <iostream>
7+
#include <opencv2/opencv.hpp>
8+
#include <opencv2/highgui.hpp>
9+
#include <opencv2/dnn.hpp>
10+
#include "message_queue.h"
11+
#include "point2D.h"
12+
#include "tracking_msg.h"
13+
14+
// Abstract class which provides functionality to detect object in the frames and have some functions to write the video file
15+
class ObjectDetector
16+
{
17+
public:
18+
19+
// Constructor / Destructor
20+
ObjectDetector(){};
21+
~ObjectDetector(){};
22+
23+
// Method to detect object in the given frame
24+
virtual void detectObject(cv::Mat &frame, std::vector<cv::Mat> &info) = 0;
25+
26+
// Method to detect object in the queue having frames
27+
virtual void detectObjectInQueue(MessageQueue<cv::Mat> &msgq, MessageQueue<cv::Mat> &outq) = 0;
28+
29+
// Method to detect object in the msgq having frames and put the detected frame and detected points in trackq
30+
virtual void detectObjectInQueueAndTrack(MessageQueue<cv::Mat> &msgq, MessageQueue<TrackingMsg> &trackq) = 0;
31+
32+
// Method to find the bounding boxes over the confident predictions
33+
virtual void postProcessDetectedObjectFrame(cv::Mat &frame, const std::vector<cv::Mat> &info) = 0;
34+
35+
// Function to add inference time to the detected object frame
36+
virtual void addDetectionTimeToFrame(cv::Mat &frame) = 0;
37+
38+
// Draws detected object bounding boxes on supplied video frame
39+
inline void drawBoundingBoxToFrame(int left, int top, int right, int bottom, cv::Mat& frame)
40+
{
41+
//Draw a rectangle displaying the bounding box
42+
cv::rectangle(frame, cv::Point(left, top), cv::Point(right, bottom), cv::Scalar(0, 0, 255));
43+
}
44+
45+
// Function to add object class to the detected object frame
46+
inline void addObjectClassToFrame(int classId, float conf, int left, int top, cv::Mat& frame)
47+
{
48+
std::string label = std::to_string(conf);
49+
if (!_classes.empty())
50+
{
51+
label = _classes[classId] + ":" + label;
52+
}
53+
else
54+
{
55+
return;
56+
}
57+
58+
//Add the label at the top of the bounding box
59+
int baseLine;
60+
cv::Size labelSize = getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.6, 1, &baseLine);
61+
top = std::max(top, labelSize.height);
62+
cv::rectangle(frame, cv::Point(left, top), cv::Point(left+labelSize.width, top - std::min(top, labelSize.height)), cv::Scalar(255, 0, 255), -1);
63+
cv::putText(frame, label, cv::Point(left, top), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255,255,255));
64+
}
65+
66+
// Function to load the class names in a vector
67+
inline void loadClasses(std::string classesFilename)
68+
{
69+
std::fstream file(classesFilename);
70+
std::string line;
71+
while(std::getline(file, line))
72+
{
73+
_classes.push_back(line);
74+
}
75+
std::cout << "Loaded " << _classes.size() <<" Class Names" << std::endl;
76+
}
77+
78+
// Confidence threshold for class
79+
float _confidenceThresh;
80+
81+
// Non-max Suppression threshold to remove the access bounding boxes
82+
float _nonMaxSuppressionThresh;
83+
84+
// Unique Pointer holding the Object Dectector
85+
std::unique_ptr<cv::dnn::Net> _detector;
86+
87+
// Vector containing class names
88+
std::vector<std::string> _classes;
89+
90+
// Frames Detected
91+
int _numFramesDetected{0};
92+
93+
};
94+
95+
#endif
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#ifndef YOLO_DETECTOR_H
2+
#define YOLO_DETECTOR_H
3+
4+
#include <vector>
5+
#include <string>
6+
#include <iostream>
7+
#include <opencv2/opencv.hpp>
8+
#include <opencv2/highgui.hpp>
9+
#include <opencv2/dnn.hpp>
10+
#include "object_detector.h"
11+
#include "point2D.h"
12+
#include "tracking_msg.h"
13+
14+
// class which provides functionality to detect object using YOLO algorithm
15+
class YOLODetector: public ObjectDetector
16+
{
17+
public:
18+
19+
// Constructor and Destructor
20+
YOLODetector(std::string modelWeights, std::string modelConfig, std::string classesFilename, float confThresh, float nmsThresh);
21+
~YOLODetector();
22+
23+
// Method to detect object in the msgq having frames and put the detected frames in the outq
24+
void detectObjectInQueue(MessageQueue<cv::Mat> &msgq, MessageQueue<cv::Mat> &outq);
25+
26+
// Method to detect object in the msgq having frames and put the detected frame and detected points in trackq
27+
void detectObjectInQueueAndTrack(MessageQueue<cv::Mat> &msgq, MessageQueue<TrackingMsg> &trackq);
28+
29+
// Method to detect object in the given frame
30+
void detectObject(cv::Mat &frame, std::vector<cv::Mat> &info);
31+
32+
protected:
33+
34+
// Method to the bounding boxes over the confident predictions
35+
void postProcessDetectedObjectFrame(cv::Mat &frame, const std::vector<cv::Mat> &info);
36+
37+
// Method to add inference time to detected object frame
38+
void addDetectionTimeToFrame(cv::Mat &frame);
39+
40+
private:
41+
42+
// Input width and height of the image to the network
43+
int _inputWidth;
44+
int _inputHeight;
45+
float _frameDetectionTime;
46+
std::vector<Point2D> _objectCenters;
47+
};
48+
49+
#endif

0 commit comments

Comments
 (0)