OpenCV 目标跟踪算法介绍与实现
本文总结OpenCV中常见的目标跟踪算法,并提供Python和C++的双版本实现。
一、OpenCV中目标跟踪算法介绍
OpenCV提供了多种目标跟踪算法,主要分为两类:
1. 传统跟踪算法
- • BOOSTING: 基于Haar级联的AdaBoost算法
2. 深度学习算法
二、代码实现
Python版本
环境要求:(亲测,opencv版本不能大于3)
opencv-python=3.4.15.55opencv-contrib-python=3.4.15.55numpy=1.24.4
import cv2import sysimport timeclass OpenCVTracker:def __init__(self, tracker_type="KCF"):""" 初始化跟踪器 Args: tracker_type: 跟踪算法类型 "BOOSTING", "MIL", "KCF", "TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT" """self.tracker_type = tracker_typeself.tracker = self._create_tracker()self.bbox = Noneself.frame_count = 0self.fps = 0def _create_tracker(self):"""根据类型创建跟踪器""" tracker_map = {"BOOSTING": cv2.TrackerBoosting_create,"MIL": cv2.TrackerMIL_create,"KCF": cv2.TrackerKCF_create,"TLD": cv2.TrackerTLD_create,"MEDIANFLOW": cv2.TrackerMedianFlow_create,"GOTURN": cv2.TrackerGOTURN_create,"MOSSE": cv2.TrackerMOSSE_create,"CSRT": cv2.TrackerCSRT_create }if self.tracker_type in tracker_map:try:return tracker_map[self.tracker_type]()except AttributeError:print(f"警告: OpenCV版本可能不支持{self.tracker_type}算法")print("尝试使用KCF作为替代...")return cv2.TrackerKCF_create()else:print(f"错误: 不支持的跟踪器类型: {self.tracker_type}")print("使用默认的KCF算法")return cv2.TrackerKCF_create()def initialize(self, frame, bbox):""" 初始化跟踪器 Args: frame: 初始帧图像 bbox: 边界框 (x, y, width, height) """self.bbox = bbox success = self.tracker.init(frame, bbox)self.frame_count = 0self.start_time = time.time()return successdef update(self, frame):""" 更新跟踪器 Args: frame: 当前帧图像 Returns: success: 是否跟踪成功 bbox: 更新后的边界框 """self.frame_count += 1# 更新跟踪器 success, bbox = self.tracker.update(frame)# 计算FPSif success: current_time = time.time() elapsed_time = current_time - self.start_timeif elapsed_time > 0:self.fps = self.frame_count / elapsed_timereturn success, bboxdef draw_bbox(self, frame, bbox, color=(0, 0, 255), thickness=1):"""在图像上绘制边界框""" x, y, w, h = [int(v) for v in bbox] cv2.rectangle(frame, (x, y), (x + w, y + h), color, thickness)# 显示跟踪器类型和FPS info = f"{self.tracker_type} FPS: {self.fps:.1f}" cv2.putText(frame, info, (60, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 4)return framedef run_tracker(video_source=0, tracker_type="KCF"):""" 运行目标跟踪器 Args: video_source: 视频源 (0-摄像头,或视频文件路径) tracker_type: 跟踪算法类型 """# 创建视频捕获对象 cap = cv2.VideoCapture(video_source)if not cap.isOpened():print("错误: 无法打开视频源")return# 读取第一帧 ret, frame = cap.read()if not ret:print("错误: 无法读取视频") cap.release()return# 选择ROI(感兴趣区域)print("请选择跟踪目标区域,然后按SPACE或ENTER键确认,按ESC取消") cv2.namedWindow("Select ROI", cv2.WINDOW_NORMAL) cv2.imshow("Select ROI", frame) bbox = cv2.selectROI("Select ROI", frame, False) cv2.destroyWindow("Select ROI")if bbox == (0, 0, 0, 0):print("未选择区域,退出程序") cap.release()return# 创建跟踪器 tracker = OpenCVTracker(tracker_type)# 初始化跟踪器if not tracker.initialize(frame, bbox):print("错误: 跟踪器初始化失败") cap.release()returnprint(f"开始跟踪,使用算法: {tracker_type}")print("按ESC键退出")while True: ret, frame = cap.read()if not ret:break# 更新跟踪器 success, bbox = tracker.update(frame)# 绘制结果if success: frame = tracker.draw_bbox(frame, bbox)else: cv2.putText(frame, "Track Failed", (60, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)# 显示结果 cv2.namedWindow("Select ROI", cv2.WINDOW_NORMAL) cv2.imshow("Select ROI", frame)# 按键处理 key = cv2.waitKey(1) & 0xFFif key == 27: # ESC键break# 清理资源 cap.release() cv2.destroyAllWindows()if __name__ == "__main__":run_tracker("../WIN_20221029_16_49_19_Pro.mp4", "KCF")
C++版本
CmakeList.txt:
cmake_minimum_required(VERSION 3.10)project(OpenCVTracker)set(CMAKE_CXX_STANDARD 11)set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找OpenCVfind_package(OpenCV REQUIRED)# 包含目录include_directories(${OpenCV_INCLUDE_DIRS})# 可执行文件add_executable(tracker_comparison tracker_main.cpp)# 链接库target_link_libraries(tracker_comparison ${OpenCV_LIBS})
Code:(opencv版本依然使用4以下版本)
#include<opencv2/opencv.hpp>#include<opencv2/tracking.hpp>#include<iostream>#include<chrono>#include<map>#include<string>using namespace cv;using namespace std;using namespace std::chrono;class OpenCVTracker{private: Ptr<Tracker> tracker; string trackerType; Rect2d bbox;int frameCount;double fps; steady_clock::time_point startTime;public:OpenCVTracker(const string& type = "CSRT") : trackerType(type), frameCount(0), fps(0) {createTracker(); }boolcreateTracker(){// 创建跟踪器if (trackerType == "BOOSTING") { tracker = TrackerBoosting::create(); } else if (trackerType == "MIL") { tracker = TrackerMIL::create(); } else if (trackerType == "KCF") { tracker = TrackerKCF::create(); } else if (trackerType == "TLD") { tracker = TrackerTLD::create(); } else if (trackerType == "MEDIANFLOW") { tracker = TrackerMedianFlow::create(); } else if (trackerType == "GOTURN") { tracker = TrackerGOTURN::create(); } else if (trackerType == "MOSSE") { tracker = TrackerMOSSE::create(); } else if (trackerType == "CSRT") { tracker = TrackerCSRT::create(); } else { cerr << "错误: 不支持的跟踪器类型: " << trackerType << endl; cerr << "使用默认的CSRT算法" << endl; trackerType = "CSRT"; tracker = TrackerCSRT::create();return false; }if (tracker.empty()) { cerr << "错误: 无法创建跟踪器" << endl;return false; } cout << "创建跟踪器: " << trackerType << endl;return true; }boolinitialize(const Mat& frame, const Rect2d& boundingBox){ bbox = boundingBox; frameCount = 0; startTime = steady_clock::now();return tracker->init(frame, bbox); }boolupdate(const Mat& frame, Rect2d& updatedBox){ frameCount++;bool success = tracker->update(frame, bbox); updatedBox = bbox;// 计算FPSif (success) {auto currentTime = steady_clock::now();auto elapsedTime = duration_cast<milliseconds>(currentTime - startTime);if (elapsedTime.count() > 0) { fps = frameCount * 1000.0 / elapsedTime.count(); } }return success; }voiddrawBoundingBox(Mat& frame, const Rect2d& box, const Scalar& color = Scalar(0, 255, 0), int thickness = 2){rectangle(frame, box, color, thickness);// 显示跟踪器类型和FPS string info = trackerType + " | FPS: " + to_string(fps).substr(0, 4);putText(frame, info, Point(10, 30), FONT_HERSHEY_SIMPLEX, 0.8, color, 2); }string getTrackerType()const{return trackerType; }doublegetFPS()const{return fps; }};voidrunTracker(int videoSource = 0, const string& trackerType = "CSRT"){// 打开视频源VideoCapture cap(videoSource);if (!cap.isOpened()) { cerr << "错误: 无法打开视频源" << endl;return; }// 读取第一帧 Mat frame;if (!cap.read(frame)) { cerr << "错误: 无法读取视频" << endl; cap.release();return; }// 选择ROI Rect2d bbox = selectROI("选择跟踪区域", frame, false);if (bbox.width == 0 || bbox.height == 0) { cout << "未选择区域,退出程序" << endl; cap.release();destroyAllWindows();return; }// 创建跟踪器OpenCVTracker tracker(trackerType);// 初始化跟踪器if (!tracker.initialize(frame, bbox)) { cerr << "错误: 跟踪器初始化失败" << endl; cap.release();destroyAllWindows();return; } cout << "开始跟踪,使用算法: " << trackerType << endl; cout << "按ESC键退出,按R键重新选择区域" << endl;namedWindow("目标跟踪", WINDOW_AUTOSIZE);while (cap.read(frame)) { Rect2d updatedBox;// 更新跟踪器bool success = tracker.update(frame, updatedBox);// 绘制结果if (success) { tracker.drawBoundingBox(frame, updatedBox); } else {putText(frame, "跟踪失败", Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2); }// 显示结果imshow("目标跟踪", frame);// 按键处理char key = (char)waitKey(30);if (key == 27) { // ESC键break; } else if (key == 'r' || key == 'R') { // R键重置 cout << "重新选择跟踪区域..." << endl; bbox = selectROI("选择跟踪区域", frame, false);if (bbox.width > 0 && bbox.height > 0) { tracker.initialize(frame, bbox); } } }// 清理资源 cap.release();destroyAllWindows();}intmain(int argc, char** argv){runTracker(0, "CSRT"); // 使用摄像头,CSRT算法return 0;}
三、编译与运行说明
Python版本运行
# 安装依赖(使用清华镜像源,下载速度快一些)pip install opencv-python==3.4.15.55 opencv-contrib-python==3.4.15.55 numpy==1.24.4 -i https://pypi.tuna.tsinghua.edu.cn/simple/# 运行python tracker.py
C++版本编译
# 使用CMake编译mkdir buildcd buildcmake ..make# 运行程序./tracker_demo test.mp4 KCF
四、总结和使用建议
- 4. 简单场景:选择MedianFlow或MOSSE