当前位置: 首页 > news >正文

OpenCV计算机视觉实战(24)——目标追踪算法

OpenCV计算机视觉实战(24)——目标追踪算法

    • 0. 前言
    • 1. MeanShift 目标追踪
      • 1.1 实现过程
      • 1.2 改进思路
    • 2. CamShift 目标追踪
      • 2.1 实现过程
      • 2.2 改进思路
    • 3. 多目标追踪
      • 3.1 代码实现
      • 3.2 优化思路
    • 小结
    • 系列链接

0. 前言

在视频分析与智能监控中,目标追踪 (Object Tracking) 是识别出目标后,实时在后续帧中持续定位其位置的核心技术。本文将从经典的 MeanShift 算法原理入手,介绍其在 OpenCV 中的实现;接着讲解 CamShift (Continuously Adaptive Mean Shift) 的改进策略;最后讨论多目标追踪时面临的挑战与常见解决思路。

1. MeanShift 目标追踪

MeanShift 是一种基于统计密度的迭代搜索算法,用于在目标色彩直方图模型下,不断调整搜索窗口使其收敛到概率密度最大的区域,实现对目标位置的跟踪。

1.1 实现过程

  • ROI 选择:通过 cv2.selectROI 在首帧指定目标窗口,获取 (x,y,w,h)
  • 直方图建模:将 ROI 转为 HSV 空间,提取 H 通道并使用掩码剔除低饱和/低亮度像素,然后用 calcHist 计算目标色彩分布并归一化
  • 反向投影:对每帧图像计算其 HSV 图像在目标直方图下的概率分布,用以指导搜索方向
  • MeanShift 更新:cv2.meanShift 根据反向投影图,在当前窗口附近迭代移动,使窗口中心逐步趋向密度最大处
  • 可视化:每次迭代后在原图上绘制新窗口
import cv2
import numpy as np# 基于 MeanShift 算法,在视频中跟踪指定 ROI 内的对象
cap = cv2.VideoCapture('r2.mp4')
ret, frame = cap.read()# 1. 初始化追踪窗口(x,y,w,h)
x, y, w, h = cv2.selectROI('Select ROI', frame, False, False)
track_window = (x, y, w, h)
cv2.destroyWindow('Select ROI')# 2. 计算 ROI 的 HSV 直方图并归一化
roi = frame[y:y+h, x:x+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0,60,32)), np.array((180,255,255)))
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0,180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)# 3. 设置 MeanShift 终止条件:迭代 10 次或窗口中心移动少于 1 像素
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)while True:ret, frame = cap.read()if not ret:breakhsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)# 4. 反向投影:得到每像素属于目标直方图的概率back_proj = cv2.calcBackProject([hsv], [0], roi_hist, [0,180], 1)# 5. 执行 MeanShift 更新窗口ret, track_window = cv2.meanShift(back_proj, track_window, term_crit)x, y, w, h = track_window# 6. 绘制结果result = cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)cv2.imshow('MeanShift Tracking', result)if cv2.waitKey(30) & 0xFF == 27:breakcap.release()
cv2.destroyAllWindows()

目标追踪

关键函数解析:

  • cv2.calcHist(images, channels, mask, histSize, ranges):计算单通道直方图
  • cv2.calcBackProject(images, channels, hist, ranges, scale):反向投影,输出概率图
  • cv2.meanShift(probImage, window, criteria)MeanShift 算法核心,返回新的窗口位置
  • cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT:迭代终止条件,结合迭代次数和精度

1.2 改进思路

  • 多通道直方图融合:除了 H 通道,还可将 S 通道或 V 通道一并用于反向投影,提升对比度弱目标的追踪稳定性
  • 动态模型更新:在每若干帧后,用新的 ROI 对象区域更新直方图,以适应光照或外观变化,避免模型漂移
  • 自适应窗口初始大小:根据目标在前几帧的速度变化,自适应调整搜索窗口大小与迭代次数
import cv2
import numpy as np# 多通道 MeanShift,动态更新直方图模型
cap = cv2.VideoCapture('r2.mp4')
ret, frame = cap.read()# 1. 选择 ROI
x, y, w, h = cv2.selectROI('Select ROI', frame, False, False)
track_window = (x, y, w, h)
cv2.destroyWindow('Select ROI')# 2. 计算初始直方图(H+S 通道)
roi = frame[y:y+h, x:x+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, (0,30,32), (180,255,255))
roi_hist = cv2.calcHist([hsv_roi], [0,1], mask, [180,256], [0,180,0,256])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)
frame_count = 0while True:ret, frame = cap.read()if not ret: breakhsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)# 3. 反向投影(H+S)back_proj = cv2.calcBackProject([hsv], [0,1], roi_hist, [0,180,0,256], 1)# 4. MeanShift 更新ret, track_window = cv2.meanShift(back_proj, track_window, term_crit)x,y,w,h = track_window# 5. 绘制跟踪窗口img2 = cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)cv2.imshow('MeanShift Multi-Channel', img2)# 6. 每 30 帧动态更新模型frame_count += 1if frame_count % 30 == 0:new_roi = frame[y:y+h, x:x+w]hsv_new = cv2.cvtColor(new_roi, cv2.COLOR_BGR2HSV)mask_new = cv2.inRange(hsv_new, (0,30,32), (180,255,255))roi_hist = cv2.calcHist([hsv_new], [0,1], mask_new, [180,256], [0,180,0,256])cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)if cv2.waitKey(30) & 0xFF == 27:breakcap.release()
cv2.destroyAllWindows()

关键函数解析:

  • cv2.calcBackProject([hsv], [0,1], roi_hist, ranges, scale):支持多通道反向投影,将 HS 联合概率映射到灰度图,增强区分度
  • 动态更新:每 N 帧用当前窗口区域重新计算直方图,防止光照/目标外观改变导致跟丢

2. CamShift 目标追踪

CamShift (Continuously Adaptive Mean Shift) 在 MeanShift 的基础上,会根据目标大小变化自动调整窗口尺寸,并输出目标的旋转矩形,实现更灵活的跟踪。

2.1 实现过程

  • ROI 与直方图初始化:与 MeanShift 相同,将目标模型化为 HSV 直方图
  • CamShift 调用:cv2.CamShiftMeanShift 基础上,每次不仅更新窗口中心,还根据反向投影分布自动计算新的窗口大小和方向(即 K-L 椭圆)
  • 旋转矩形绘制:用 boxPoints 获得输出的旋转矩形四个顶点,并通过 polylines 画在图像上
import cv2
import numpy as np# 基于 CamShift 算法,自适应调整窗口大小与角度
cap = cv2.VideoCapture('r2.mp4')
ret, frame = cap.read()# 1. 初始化 ROI 与直方图(同 MeanShift)
x, y, w, h = cv2.selectROI('Select ROI', frame, False, False)
track_window = (x, y, w, h)
cv2.destroyWindow('Select ROI')
roi = frame[y:y+h, x:x+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, np.array((0,60,32)), np.array((180,255,255)))
roi_hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0,180])
cv2.normalize(roi_hist, roi_hist, 0, 255, cv2.NORM_MINMAX)term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)while True:ret, frame = cap.read()if not ret: breakhsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)back_proj = cv2.calcBackProject([hsv], [0], roi_hist, [0,180], 1)# 2. 执行 CamShift,返回旋转矩形ret, track_window = cv2.CamShift(back_proj, track_window, term_crit)pts = cv2.boxPoints(ret)  # 4 顶点pts = np.int0(pts)# 3. 绘制旋转矩形result = frame.copy()cv2.polylines(result, [pts], True, (0,255,0), 2)cv2.imshow('CamShift Tracking', result)if cv2.waitKey(30) & 0xFF == 27:breakcap.release()
cv2.destroyAllWindows()

追踪结果

关键函数解析

  • cv2.CamShift(probImage, window, criteria):改进版 MeanShift,返回旋转矩形 (center, (w,h), angle) 与新窗口
  • cv2.boxPoints(rotRect):将旋转矩形转换为 4 个顶点坐标
  • cv2.polylines(img, pts, isClosed, color, thickness):绘制多边形,填充或描边追踪轮廓

2.2 改进思路

  • 椭圆可视化:除了多边形,还可绘制 cv2.ellipse,直观展示目标方向与长短轴长度
  • 自适应迭代:对颜色分布复杂的场景,可根据窗口面积动态调整 term_crit 中的最大迭代次数
  • 背景补偿:在反向投影前对背景图像进行高斯模糊或直方图均衡,增强目标对比
import cv2
import numpy as np# CamShift + 椭圆可视化 + 背景直方图均衡
cap = cv2.VideoCapture('r2.mp4')
ret, frame = cap.read()x,y,w,h = cv2.selectROI('ROI', frame, False, False)
track_window = (x,y,w,h)
cv2.destroyWindow('ROI')roi = frame[y:y+h, x:x+w]
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_roi, (0,30,32), (180,255,255))
roi_hist = cv2.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv2.normalize(roi_hist, roi_hist, 0,255,cv2.NORM_MINMAX)term_crit = (cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_COUNT, 15, 1)while True:ret, frame = cap.read()if not ret: break# 背景均衡blur = cv2.GaussianBlur(frame, (5,5), 0)hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)back_proj = cv2.calcBackProject([hsv],[0],roi_hist,[0,180],1)# CamShiftret, track_window = cv2.CamShift(back_proj, track_window, term_crit)pts = cv2.boxPoints(ret)pts = np.int0(pts)# 画椭圆center, (w_e,h_e), angle = retresult = frame.copy()cv2.ellipse(result, (tuple(map(int,center)), (int(w_e),int(h_e)), angle), (0,255,0), 2)cv2.polylines(result, [pts], True, (255,0,0), 2)cv2.imshow('CamShift Enhanced', result)if cv2.waitKey(30) & 0xFF == 27:breakcap.release()
cv2.destroyAllWindows()

追踪结果
关键函数解析:

  • cv2.ellipse(img, (center, axes, angle), color, thickness):直接绘制旋转椭圆,反映目标方向与尺度
  • cv2.GaussianBlur(frame, (5,5), 0):先做背景均衡模糊,提高反向投影鲁棒性

3. 多目标追踪

在复杂场景中,通常需要同时跟踪多个目标。最简单的思路是为每个目标分别初始化多个 CamShift 跟踪器,但要解决的挑战包括目标遮挡、相互干扰与误跟踪漂移。

3.1 代码实现

import cv2
import numpy as np# 多目标 CamShift 跟踪
cap = cv2.VideoCapture('r2.mp4')
ret, frame = cap.read()# 1. 手动选择多个 ROI
bboxes = []
while True:bbox = cv2.selectROI('Select ROI, Enter为空结束', frame, False, False)if sum(bbox) == 0: breakbboxes.append(bbox)
cv2.destroyAllWindows()# 2. 初始化每个 ROI 的直方图与窗口
trackers = []
for (x,y,w,h) in bboxes:hsv_roi = cv2.cvtColor(frame[y:y+h, x:x+w], cv2.COLOR_BGR2HSV)mask = cv2.inRange(hsv_roi, np.array((0,60,32)), np.array((180,255,255)))hist = cv2.calcHist([hsv_roi], [0], mask, [180], [0,180])cv2.normalize(hist, hist, 0, 255, cv2.NORM_MINMAX)trackers.append({'hist':hist, 'window':(x,y,w,h)})term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)while True:ret, frame = cap.read()if not ret: breakhsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)result = frame.copy()# 3. 对每个目标执行 CamShiftfor tr in trackers:back_proj = cv2.calcBackProject([hsv], [0], tr['hist'], [0,180], 1)ret, tr['window'] = cv2.CamShift(back_proj, tr['window'], term_crit)pts = cv2.boxPoints(ret).astype(int)cv2.polylines(result, [pts], True, (0,255,0), 2)cv2.imshow('Multi-Object Tracking', result)if cv2.waitKey(30) & 0xFF == 27:breakcap.release()
cv2.destroyAllWindows()

多目标追踪

3.2 优化思路

  • 唯一颜色标识:为每个目标随机分配追踪框颜色,便于视觉区分
  • 丢失与重新检测:若某个目标超过连续 N 帧未被追踪到,调用检测重新初始化 ROI
# 在初始化 trackers 时为每个分配随机 color
trackers = [{'hist':..., 'window':..., 'color':tuple(np.random.randint(0,255,3))} for ... in bboxes]while True:ret, frame = cap.read()if not ret: breakhsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)for tr in trackers:back = cv2.calcBackProject([hsv],[0],tr['hist'],[0,180],1)ret, tr['window'] = cv2.CamShift(back, tr['window'], term_crit)if ret[1][0]*ret[1][1] < 100:  # 窗口面积过小,视为丢失tr['lost'] += 1else:tr['lost'] = 0if tr['lost'] > 10:tr['window'] = detect_new_roi(frame)  # 调用检测模块重定位pts = cv2.boxPoints(ret).astype(int)cv2.polylines(frame, [pts], True, tr['color'], 2)cv2.imshow('Multi-Object Advanced', frame)if cv2.waitKey(30)&0xFF==27: break

小结

在本文中,我们从 MeanShift 的色彩直方图密度搜索出发,深入剖析了其多通道建模与动态模型更新技巧,又在 CamShift 中加入了背景均衡、椭圆可视化和自适应迭代策略,显著提升了跟踪的精度与鲁棒性。最后,探讨了多目标追踪,通过这些实践,了解了从单目标到多目标、从传统算法到深度检测器融合的完整追踪管线,能够在复杂视频场景中稳定地识别、定位并持续追踪各类移动目标。

系列链接

OpenCV计算机视觉实战(1)——计算机视觉简介
OpenCV计算机视觉实战(2)——环境搭建与OpenCV简介
OpenCV计算机视觉实战(3)——计算机图像处理基础
OpenCV计算机视觉实战(4)——计算机视觉核心技术全解析
OpenCV计算机视觉实战(5)——图像基础操作全解析
OpenCV计算机视觉实战(6)——经典计算机视觉算法
OpenCV计算机视觉实战(7)——色彩空间详解
OpenCV计算机视觉实战(8)——图像滤波详解
OpenCV计算机视觉实战(9)——阈值化技术详解
OpenCV计算机视觉实战(10)——形态学操作详解
OpenCV计算机视觉实战(11)——边缘检测详解
OpenCV计算机视觉实战(12)——图像金字塔与特征缩放
OpenCV计算机视觉实战(13)——轮廓检测详解
OpenCV计算机视觉实战(14)——直方图均衡化
OpenCV计算机视觉实战(15)——霍夫变换详解
OpenCV计算机视觉实战(16)——图像分割技术
OpenCV计算机视觉实战(17)——特征点检测详解
OpenCV计算机视觉实战(18)——视频处理详解
OpenCV计算机视觉实战(19)——特征描述符详解
OpenCV计算机视觉实战(20)——光流法运动分析
OpenCV计算机视觉实战(21)——模板匹配详解
OpenCV计算机视觉实战(22)——图像拼接详解
OpenCV计算机视觉实战(23)——目标检测详解


文章转载自:

http://BbbzVX06.mLhfr.cn
http://6mEroI0R.mLhfr.cn
http://HWgbyaTi.mLhfr.cn
http://UKgfX4DM.mLhfr.cn
http://tO4etsG6.mLhfr.cn
http://M1bMX0CK.mLhfr.cn
http://OY1Qeg9z.mLhfr.cn
http://iWPEFBZq.mLhfr.cn
http://frHwwVh5.mLhfr.cn
http://v29gZOcn.mLhfr.cn
http://BO0jmeUv.mLhfr.cn
http://WnsFY870.mLhfr.cn
http://ZbOfubKQ.mLhfr.cn
http://ZEEQIRFC.mLhfr.cn
http://VAsrtJN5.mLhfr.cn
http://Ev1fN8XH.mLhfr.cn
http://BWdsmx9P.mLhfr.cn
http://1G9PMprW.mLhfr.cn
http://LDJTpZ6Q.mLhfr.cn
http://zF3oGBRW.mLhfr.cn
http://AbeqvCdb.mLhfr.cn
http://M4szjeQK.mLhfr.cn
http://RRDtNqpw.mLhfr.cn
http://4Kzhseyg.mLhfr.cn
http://4ISskdrf.mLhfr.cn
http://annJEQlx.mLhfr.cn
http://TNdZYnab.mLhfr.cn
http://q1ivR8Mn.mLhfr.cn
http://ihxwZNpw.mLhfr.cn
http://APN4HnYV.mLhfr.cn
http://www.dtcms.com/a/377525.html

相关文章:

  • 4.2 I2C通信协议
  • Spring Boot 读取 YAML 配置文件
  • 【系统分析师】第20章-关键技术:微服务系统分析与设计(核心总结)
  • SAP-MM:SAP MM模块精髓:仓储地点(Storage Location)完全指南图文详解
  • Shell脚本周考习题及答案
  • 广东省省考备考(第九十六天9.10)——言语(刷题巩固第二节课)
  • Pthread定时锁与读写锁详解
  • Go模块自动导入教学文档
  • 技术文章大纲:开学季干货——知识梳理与经验分享
  • TensorFlow平台介绍
  • Vue3 中实现按钮级权限控制的最佳实践:从指令到组件的完整方案
  • 生成模型与概率分布基础
  • Cookie之domain
  • JavaSSM框架-MyBatis 框架(五)
  • 中州养老:设备管理介绍
  • 【Day 51|52 】Linux-tomcat
  • MySQL - 如果没有事务还要锁吗?
  • “高德点评”上线,阿里再战本地生活
  • JUC的常见类、多线程环境使用集合类
  • 《C++ 108好库》之1 chrono时间库和ctime库
  • C++篇(7)string类的模拟实现
  • 弱加密危害与修复方案详解
  • 【Linux】Linux常用指令合集
  • Android- Surface, SurfaceView, TextureView, SurfaceTexture 原理图解
  • 如何设计Agent 架构
  • MySQL主从不一致?DBA急救手册:14种高频坑点+3分钟定位+无损修复!
  • 拍我AI:PixVerse国内版,爱诗科技推出的AI视频生成平台
  • 3D柱状图--自定义柱子颜色与legend一致(Vue3)
  • LeetCode热题100--199. 二叉树的右视图--中等
  • Next系统学习(三)