Computer Vision

[Object Tracking/Object Detection] Motorbike counter with OpenCV

Xin chào, ở bài viết “Simple vehicle counter with OpenCV” chúng ta đã tìm hiểu về thuật toán Centroid Tracking để định danh đối tượng, cũng như phối hợp với Background Subtraction để phát hiện và đếm phương tiện giao thông, hệ thống này tuy đơn giản nhưng cũng có nhiều nhược điểm (đã nêu trong bài viết), cũng như chúng ta vẫn chưa thực sự áp dụng Object Tracking. Vì thế trong bài viết hôm nay chúng ta sẽ tìm hiểu cách áp dụng Object Tracking và Object Detection vào bài toán Object Counter, cụ thể hơn là Motorbike counter, với sự trợ giúp của OpenCV 3, và đặc biệt tất cả sẽ được implement trên C++.


CÁC THÀNH PHẦN CỦA HỆ THỐNG

Để giải quyết bài toán Object Counter, hệ thống mới của chúng ta cần có các thành phần sau:

  • Object Detector: được dùng để phát hiện đối tượng cần đếm trong frame ảnh. Chúng ta có thể sử dụng Cascade Classifier, HOG-SVM hoặc deep-learning object detectors;
  • Object Tracker: được dùng để định vị đối tượng ở các frame ảnh tiếp theo sau khi được phát hiện bởi Object Detector. Áp dụng Object Tracker cùng với Object Detector giúp giảm yêu cầu tính toán, từ đó giúp tăng tốc độ xử lý của hệ thống. OpenCV 3.4 đã hỗ trợ khá nhiều thuật toán tracking, nổi bật nhất là: CSRT, KCF và MOSSE;
  • Centroid Tracker: được dùng để định danh từng đối tượng, từ đó chúng ta có thể phân biệt các đối tượng với nhau.

Cấu trúc của project như sau:

MotorbikeCounter
|—- build
|—- objecttracker
|—-|—- objecttracker.h
|—-|—- objecttracker.cpp
|—- hogsvm
|—-|—-lib
|—-|—-|—- tinyxml2.h
|—-|—-|—- tinyxml2.cpp
|—-|—- hogsvm.h
|—-|—- hogsvm.cpp
|—-|—- CMakeLists.txt
|—- output
|—-|—- motorbikeMultiScale.yml
|—- main.cpp
|—- CMakeLists.txt
|—- test.mp4

Trong đó:
+ folder build được dùng để chứa các binary files sau khi biên dịch source code;
+ thư viện objecttracker hogsvm có thể download từ github của mình;
+ file motorbikeMultiScale.yml chứa trained model để phát hiện motorbike có thể được download từ gits của mình.


OBJECT DETECTION

Chúng ta có thể sử dụng bất cứ loại Object Detector nào để phát hiện đối tượng, trong bài viết này mình sử dụng HOG-SVM Object Detector. Để hiểu rõ hơn về cách xây dựng một bộ Object Detector với HOG-SVM, các bạn có thể tham khảo bài viết này của mình.

Bên cạnh đó, mình cũng đã viết hogsvm, một wrapper dành cho HOG-SVM Object Detector của OpenCV 3, trong đó hỗ trợ imglab làm annotation tool để đánh dấu các bounding boxes trong từng bức ảnh. Cách sử dụng wrapper hogsvm được ghi chú chi tiết ở github.


OBJECT TRACKER

Chú ý: bài viết này không giải thích chi tiết về các thuật toán tracking.

OpenCV 3.4 hỗ trợ các Tracking Algorithm sau: BOOSTING Tracker, MIL Tracker, KCF Tracker, CSRT Tracker, MedianFlow Tracker, TLD Tracker, MOSSE Tracker, GOTURN Tracker.

Trong đó:
+ CSRT Tracker – độ chính xác cao nhưng khá chậm;
+ KCF Tracker – độ chính xác thấp hơn CSRT Tracker nhưng tốc độ cao hơn;
+ MOSSE Tracker – độ chính xác thấp hơn KCF Tracker nhưng tốc độ cao hơn.

Functions:

  • Tạo tracker instance:

cv::Ptr<cv::Tracker> tracker = cv::TrackerCSRT::create()
cv::Ptr<cv::Tracker> tracker = cv::
TrackerKCF::create()
cv::Ptr<cv::Tracker> tracker = cv::
TrackerMOSSE::create()

  • Khởi tạo object tracker:

tracker->init(cv::InputArray image, const cv::Rect2d& boundingBox)

Trong đó:
+ image – bức ảnh input 3 channels chứa đối tượng cần theo dõi;
+ boundingBox – bounding box chứa vùng ảnh của đối tượng.

  • Cập nhật bộ tracker:

tracker->update(cv::InputArray image, cv::Rect2d& boundingBox)

Trong đó:
+ image – bức ảnh input 3 channels chứa đối tượng cần theo dõi;
+ boundingBox – bounding box chứa vùng ảnh mà tracker cho rằng của đối tượng mà nó đang theo dõi.


CENTROID TRACKER

Để hiểu rõ hơn về thuật toán Centroid Tracker, các bạn có thể tham khảo bài viết này.

Mặc dù Object Detector và Object Tracker đã giúp chúng ta phát hiện đối tượng, nhưng giữa 2 khung hình liên tiếp, chúng ta vẫn chưa thể phân biệt các đối tượng với nhau, do đó không thể đếm số lượng đối tượng đang chuyển động. Đó là lý do chúng ta cần sử dụng Centroid Tracker.

Object Tracker sẽ lưu các đối tượng vào một danh sách đăng ký, trong đó mỗi đối tượng được cấp một ID riêng. Trong quá trình theo dõi, những đối tượng không còn xuất hiện sẽ bị loại khỏi danh sách, còn các đối tượng mới sẽ được thêm vào. Các đối tượng trong danh sách cũng được gắn nhãn (rằng đã được đếm hay chưa) nhằm tránh trùng lặp khi đếm.


SOURCE CODE

File: CMakeLists.txt

cmake_minimum_required(VERSION 3.4)

project(ObjectTracking)

add_compile_options(
	-std=c++11
	-Wall
	-Werror
	-O2
)

find_package(OpenCV REQUIRED)

# Target filename
set(TARGET_FILE main)

# Add sub-directory
add_subdirectory(./hogsvm ./hogsvm)

# Path to libraries 
set(INCLUDE_DIRS ./hogsvm
		 ./objecttracker)

set(INCLUDE_SOURCES ./objecttracker/objecttracker.cpp)

# Load all source files in project folder
set(SOURCES main.cpp)

message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")

message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")

include_directories(${OpenCV_INCLUDE_DIRS} ${INCLUDE_DIRS})

add_executable(${TARGET_FILE} ${SOURCES} ${INCLUDE_SOURCES})
target_link_libraries(${TARGET_FILE} ${OpenCV_LIBS} hogsvm)

File: main.cpp

#include <vector>
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "hogsvm.h"
#include "objecttracker.h"

using namespace std;
using namespace cv;


int main() {
	HOGSVM detector = HOGSVM::createMultiScale();
	detector.loadModel("../output/detectorMultiScale.yml");

	VideoCapture video("../test.mp4");

	if (!video.isOpened()) {
		cerr << "File is not opened" << endl;
		return -1;
	}

	ObjectTracker tracker(10);

	Mat frame;
	vector<Rect> boxes;
	map<int, Point> centroids;
	map<int, bool> states;

	Scalar color;
	int counter = 0;
	int frameCounter = 0;
	int barrier = 300;
	Size frameSize(640,480);

	while (1) {
		boxes.clear();

		video >> frame;
		if (frame.empty()) {
			cout << "No frame is captured" << endl;
			break;
		}

		resize(frame, frame, frameSize);
		
		if (frameCounter % 10 == 0) {
			#ifdef DEBUG
				cout << "[Use detector]" << endl;
			#endif

			vector<Rect> result = detector.detect(frame);

			for (const auto& r : result) {
				if (r.y + r.height/2 <= barrier) {
					boxes.push_back(r);
				}
			}

			tracker.update(frame, false, boxes);

			color = Scalar(0,0,255);
		}	
		else {
			vector<Rect> result = tracker.update(frame);

			for (const auto& r : result) {
				if (r.y + r.height/2 <= barrier) {
					boxes.push_back(r);
				}
			}

			color = Scalar(0,255,0);
		}

		#ifdef DEBUG
			tracker.showInfo();
		#endif

		centroids = tracker.getCentroids();
		states = tracker.getStates();

		for (const auto& e : centroids) {
			int ID = e.first;
			Point centroid = e.second;
			int objectState = (states[ID] == false) ? 0 : 1;

			circle(frame, 
					centroid, 
					3, 
					Scalar(0, 255*(1-objectState), 255*objectState), 
					-1);

			if ((centroid.y >= barrier) && (objectState == 0)) {
				tracker.setState(ID, true);
				counter++;
			}
		}

		putText(frame, "Count: " + to_string(counter),
				Point(20,30), 
				cv::FONT_HERSHEY_SIMPLEX, 
				1, 
				Scalar(0,255,0), 
				2);

		line(frame, 
			Point(0,barrier), 
			Point(640,barrier), 
			Scalar(255,0,0), 
			2);

		Mat overlay = Mat::zeros(frameSize.height, frameSize.width, frame.type());
		rectangle(overlay, 
				Rect(0, barrier,  frameSize.width, frameSize.height-barrier), 
				Scalar(255,0,0), 
				-1);

		addWeighted(frame, 0.7, overlay, 0.3, 0, frame);

		imshow("Frame", frame);
		int k = waitKey(1);
		if (k == 27) {
			break;
		}

		frameCounter++;
	}

	video.release();
	destroyAllWindows();
}

Giải thích:
– Dòng 14, 16: load model file và file video test.mp4;
– Dòng 47-63: gọi Object Detector để phát hiện đối tượng sau mỗi 10 frame, sau đó cập nhật bộ tracker;
– Dòng 65-74: sử dụng bộ tracker để cập nhật vị trí các đối tượng đang được theo dõi;
– Dòng 83-98: đếm các đối tượng đã di chuyển tới vị trí cần thiết và cập nhật trạng thái của các đối tượng đã được đếm;
– Dòng 100-121: hiển thị kết quả.


BIÊN DỊCH SOURCE CODE

$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make

Execution:
$ ./main


KẾT QUẢ


Cảm ơn các bạn đã theo dõi bài viết.
Thân ái và quyết thắng.


References:
[1] OpenCV Object Tracking.
[2] Object Tracking using OpenCV (C++/Python).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s