97 个 OpenCV 实例(十三):目标跟踪,CamShift 反向投影

上一篇做了仪表读数,一个能真正落地的项目。这一篇换个玩法------让程序「盯住」一个运动的东西。目标跟踪是视频分析的第一步,工业现场到处都要用它:传送带上的零件要跟踪、机械臂抓取的目标要锁定。而 OpenCV 里最经典的颜色跟踪算法,就是 CamShift。

一、效果先行

先看跟踪效果。左边第一帧框选了一个绿色圆作为目标,中间是它移动中途,右边最后一帧------红色椭圆框始终死死咬住绿圆,两条轨迹线(绿色真实、红色跟踪)几乎重合,右上角标注平均误差 0.71 像素:

第二张是 CamShift 的「内功」------反向投影概率图(左)和目标的颜色签名(右)。左图暖色代表「颜色像目标」的像素,绿色圆处一个亮斑;右图是目标 H 通道的直方图,尖峰就是它标志性的绿色:

两张图记住两件事:CamShift 的跟踪精度能做到亚像素;它靠的不是形状,是颜色。

二、CamShift 是什么:从 MeanShift 讲起

CamShift 的全称是 Continuously Adaptive MeanShift(连续自适应均值漂移),它是 MeanShift 的升级版。先理解 MeanShift:在一张「概率图」上,从当前窗口出发,反复把窗口中心移到窗口内概率的质心,直到收敛------所以叫「均值漂移」。

MeanShift 只能定位,窗口大小是死的。目标走近变大、走远变小,它就框不准了。CamShift 的改进就在名字里:定位之后,再根据概率的二阶矩自适应调整窗口的大小和角度,返回一个可旋转矩形(RotatedRect)。目标缩放、旋转都能框住。这是它相对 MeanShift 最关键的一步。

三、反向投影:理解 CamShift 的钥匙

CamShift 不找形状,找颜色。整个流程围绕一个核心概念------反向投影(back projection)。

先说正向:从目标框里算出它 H 通道的颜色直方图,这是目标的「颜色签名」------比如一个绿球,签名就是在绿色 H 值处有个尖峰。再说反向:拿这个签名去扫描整幅图,每个像素的 H 值换成「它在签名里出现的频数」,得到一张概率图。亮的地方 = 颜色像目标,暗的地方 = 不像。

CamShift 本质上就是在这张概率图上跑 MeanShift。理解了反向投影,就理解了 CamShift。

四、五步 pipeline

官方 camshiftdemo 依赖摄像头和鼠标框选,我改成了静态合成图加程序化平移(模拟运动),核心五步不变:

cpp 复制代码
// ① 转 HSV:把色相(H)和亮度/饱和度分离,颜色跟踪只看 H
cvtColor(frame, hsv, COLOR_BGR2HSV);
// ② inRange:饱和度+亮度阈值 → mask,过滤掉低饱和的背景
inRange(hsv, Scalar(0, smin, min(vmin,vmax)),
        Scalar(180, 256, max(vmin,vmax)), mask);
// ③ 提取 H 通道(0~180)
extractChannel(hsv, hue, 0);
// ④ 从初始 ROI 建目标颜色签名(只建一次)
calcHist({roi}, {0}, mask_roi, hist, {hsize}, {0,180});
normalize(hist, hist, 0, 255, NORM_MINMAX);
// ⑤ 反向投影得概率图 → CamShift 求质心+自适应窗口
calcBackProject({hue}, {0}, hist, backproj, {0,180}, 1.0);
backproj &= mask;
RotatedRect box = CamShift(backproj, trackWindow,
                    TermCriteria(TermCriteria::EPS | TermCriteria::COUNT, 10, 1));

每一步环环相扣:mask 过滤背景干扰,颜色签名锁定目标特征,反向投影把「找颜色」变成「找概率峰值」,CamShift 在概率峰值上自适应跟踪。

五、实测对账:亚像素精度

合成场景是深灰背景加一个绿色圆,让它匀速直线运动 30 帧(每帧平移 8px 横向、5px 纵向)。程序化指定初始框,逐帧 CamShift,把「真实轨迹 vs 跟踪轨迹」画出来对账:

  • 平均误差 0.707 像素,逐帧误差都在 0.5px 的亚像素精度内
  • 因为 CamShift 返回的是 RotatedRect 的中心(浮点坐标),精度天然比整数像素高

用已知运动对账,比肉眼看图靠谱得多------这是本系列一直在用的验证方法。

六、踩坑记录

现象 原因 正确姿势
mixChannels 段错误 程序直接崩 官方用 Mat* 重载,4.14 有 bug 用 extractChannel 提取单通道
calcBackProject 编译不过 expects 6 arguments vector 重载的 scale 参数无默认值 显式传 6 个参数,末尾加 1.0
hconcat 抛异常 rows 不一致 反投影图 640×480 拼 320×200 直方图 先 resize 直方图到同高
红色目标难跟踪 直方图裂成两半 HSV 里红色 H 在 0 和 180 两端 示例用绿/蓝,避开 H 环绕

最后一条是 CamShift 的固有软肋:它「找颜色」,所以目标撞色、光照剧变导致 H 漂移时就会失效。这是它相对光流、特征点法的取舍------便宜、快、简单,但挑场景。

七、AI 与 LLM Wiki:这一课沉淀了什么

LLM Wiki 现在 20 页了,这一课新增「目标跟踪:CamShift反向投影」概念页,沉淀了 Meanshift→CamShift 的演进逻辑、反向投影的本质、五步 pipeline、以及 0.707px 的实测对账数据。两张截图归档进素材目录,进度表上 camshiftdemo 勾选完成,从 25 个变成 26 个,进度 27%。

这一课最值得进知识库的,是「反向投影」这个思维转换------把「找颜色」变成「找概率峰值」。想跟踪一个绿球,不用教程序「圆长什么样」,而是给它「绿色长什么样」,它在概率图上一钻就找到了。这种「换一个空间解决问题」的思路,比 CamShift 本身的 API 更值钱。

写在最后

97 个实例,今天完成第 26 个,进度 27%。这一课是系列第一次碰「目标跟踪」,CamShift 便宜、快、简单,是颜色跟踪的入门经典,但挑场景。后面特征检测阶段还会见到更强的跟踪方法(光流、特征点匹配),到时候能对比出各自的边界。

下一篇做图像分割------GrabCut 图割和 inpaint 图像修复,一个抠图、一个去水印。到时候见。

相关推荐
m4Rk_16 分钟前
【论文阅读】Agent 记忆机制(54):MINJA——普通用户如何仅通过查询污染 Agent 的长期记忆
论文阅读·人工智能·学习·开源·github
十三画者17 分钟前
【文献分享】CancerCellNet:评估癌症模型转录保真度的计算工具
人工智能·深度学习·数据挖掘·数据分析·数据可视化
智圣新创0119 分钟前
从制度落地到效能可测:智圣新创第二课堂成绩单体系高职场景建设的全国普适性实践
大数据·人工智能
workflower21 分钟前
人形机器人“手”是关键
人工智能·机器学习·机器人·无人机
伍树明21 分钟前
Hermes‑Agent(自进化agent)浅析
人工智能
木圭的AI时代指南23 分钟前
商汤日日新TokenPlan大调整与接入全解
人工智能·ai·语言模型
✎ ﹏梦醒͜ღ҉繁华落℘24 分钟前
截止到2026/9/1-AI编程工具排名
人工智能
j7~25 分钟前
【AI应用--白话大模型(篇二)】《搞懂大模型:看懂应用场景,分清开源与闭源,上手 HuggingFace & 魔搭社区,理解大模型运行逻辑》
人工智能·开源社区·大模型工作原理·开源和闭源大模型·大模型的应用场景
HyperAI超神经28 分钟前
在线教程丨最高2K+原生双声道,MiniMax H3统一音视频生成
人工智能·文生视频·多模态大模型·图生视频·ai视频生成