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

发布于:2025-09-11 ⋅ 阅读:(17) ⋅ 点赞:(0)

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:
        break
    hsv = 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:
        break

cap.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 = 0

while True:
    ret, frame = cap.read()
    if not ret: break
    hsv = 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 += 1
    if 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:
        break

cap.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: break
    hsv = 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:
        break

cap.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)

    # CamShift
    ret, track_window = cv2.CamShift(back_proj, track_window, term_crit)
    pts = cv2.boxPoints(ret)
    pts = np.int0(pts)

    # 画椭圆
    center, (w_e,h_e), angle = ret
    result = 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:
        break

cap.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: break
    bboxes.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: break
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    result = frame.copy()

    # 3. 对每个目标执行 CamShift
    for 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:
        break

cap.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: break
    hsv = 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'] += 1
        else:
            tr['lost'] = 0
        if 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)——目标检测详解


网站公告

今日签到

点亮在社区的每一天
去签到