Opencv基于CamShift算法实现目标跟踪
作者:-牧野- 发布时间:2023-02-15 15:39:56
CamShift算法全称是“Continuously Adaptive Mean-Shift”(连续的自适应MeanShift算法),是对MeanShift算法的改进算法,可以在跟踪的过程中随着目标大小的变化实时调整搜索窗口大小,对于视频序列中的每一帧还是采用MeanShift来寻找最优迭代结果,至于如何实现自动调整窗口大小的,可以查到的论述较少,我的理解是通过对MeanShift算法中零阶矩的判断实现的。
在MeanShift算法中寻找搜索窗口的质心用到窗口的零阶矩M00和一阶矩M10,M01:
零阶矩是搜索窗口内所有像素的积分,即所有像素值之和,物理上的意义是计算搜索窗口的尺寸。经过目标的H分量直方图反向投影后,目标区域的搜索窗口大部分像素值归一化后应该是最大值255,如果计算出来零阶矩大于某一阈值,可以认为此时目标铺满了整个搜索窗口,有理由认为在搜索窗口之外的区域还存在目标区域,需要增大搜索窗口的尺寸;相应的,如果零阶矩小于某一阈值,则需要缩小搜索窗口的尺寸,如此一来,当目标的大小发生变化的时候,CamShift算法就可以自适应的调整目标区域进行跟踪。
以上过程中涉及到一个关键的概念——反向投影,CamShift和MeanShift的运算都是在反向投影图像上进行的,反向投影的实现过程如下:计算并生成目标区域的H分量的直方图,反向投影其实就是把目标图像上每一个像素点的像素值替换为当前像素值所在bin对应的直方图bin的数值。
Opencv中CamShfit在使用上跟MeanShift一致:
CamShift( InputArray probImage, CV_OUT CV_IN_OUT Rect& window,
TermCriteria criteria );
第一个参数probImage是反向投影图像
第二个参数window是输入和输出的搜索窗口/目标窗口,window的尺寸会自动调整
第三个参数criteria是迭代收敛终止条件
#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include "video/tracking.hpp"
#include<iostream>
using namespace cv;
using namespace std;
Mat image;
Mat rectImage;
Mat imageCopy; //绘制矩形框时用来拷贝原图的图像
bool leftButtonDownFlag=false; //左键单击后视频暂停播放的标志位
Point originalPoint; //矩形框起点
Point processPoint; //矩形框终点
Mat targetImageHSV;
int histSize=200;
float histR[]={0,255};
const float *histRange=histR;
int channels[]={0,1};
Mat dstHist;
Rect rect;
vector<Point> pt; //保存目标轨迹
void onMouse(int event,int x,int y,int flags ,void* ustc); //鼠标回调函数
int main(int argc,char*argv[])
{
VideoCapture video(argv[1]);
double fps=video.get(CV_CAP_PROP_FPS); //获取视频帧率
double pauseTime=1000/fps; //两幅画面中间间隔
namedWindow("跟踪木头人",0);
setMouseCallback("跟踪木头人",onMouse);
while(true)
{
if(!leftButtonDownFlag) //判定鼠标左键没有按下,采取播放视频,否则暂停
{
video>>image;
}
if(!image.data||waitKey(pauseTime)==27) //图像为空或Esc键按下退出播放
{
break;
}
if(originalPoint!=processPoint&&!leftButtonDownFlag)
{
Mat imageHSV;
Mat calcBackImage;
cvtColor(image,imageHSV,CV_RGB2HSV);
calcBackProject(&imageHSV,2,channels,dstHist,calcBackImage,&histRange); //反向投影
TermCriteria criteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.001);
CamShift(calcBackImage, rect, criteria);
Mat imageROI=imageHSV(rect); //更新模板
targetImageHSV=imageHSV(rect);
calcHist(&imageROI, 2, channels, Mat(), dstHist, 1, &histSize, &histRange);
normalize(dstHist, dstHist, 0.0, 1.0, NORM_MINMAX); //归一化
rectangle(image, rect, Scalar(255, 0, 0),3); //目标绘制
pt.push_back(Point(rect.x+rect.width/2,rect.y+rect.height/2));
for(int i=0;i<pt.size()-1;i++)
{
line(image,pt[i],pt[i+1],Scalar(0,255,0),2.5);
}
}
imshow("跟踪木头人",image);
waitKey(100);
}
return 0;
}
//*******************************************************************//
//鼠标回调函数
void onMouse(int event,int x,int y,int flags,void *ustc)
{
if(event==CV_EVENT_LBUTTONDOWN)
{
leftButtonDownFlag=true; //标志位
originalPoint=Point(x,y); //设置左键按下点的矩形起点
processPoint=originalPoint;
}
if(event==CV_EVENT_MOUSEMOVE&&leftButtonDownFlag)
{
imageCopy=image.clone();
processPoint=Point(x,y);
if(originalPoint!=processPoint)
{
//在复制的图像上绘制矩形
rectangle(imageCopy,originalPoint,processPoint,Scalar(255,0,0),2);
}
imshow("跟踪木头人",imageCopy);
}
if(event==CV_EVENT_LBUTTONUP)
{
leftButtonDownFlag=false;
rect=Rect(originalPoint,processPoint);
rectImage=image(rect); //子图像显示
imshow("Sub Image",rectImage);
cvtColor(rectImage,targetImageHSV,CV_RGB2HSV);
imshow("targetImageHSV",targetImageHSV);
calcHist(&targetImageHSV,2,channels,Mat(),dstHist,1,&histSize,&histRange,true,false);
normalize(dstHist,dstHist,0,255,CV_MINMAX);
imshow("dstHist",dstHist);
}
}
蓝色窗口是跟踪的目标,绿色线条是目标走过的轨迹。
来源:http://blog.csdn.net/dcrmg/article/details/52771372


猜你喜欢
- 1.打开File >> setting,选择Plugins>>Browse Repositories2.搜索Jreb
- 代理OkHttp 支持设置代理,使用OkHttpClient.proxy()即可设置。什么是代理?根据代理的对象不同,可分为正向代理和反向代
- 前言:$是c# 6.0 的语言特性,功能类似string.format(),更方便的地方在于不要像format一样使用索引,可以直接使用变量
- Java去掉指定字符串的开头的指定字符/** * 去掉指定字符串的开头的指定字符 *
- RESTful 一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设
- 前言:Springboot的出现极大的简化了开发人员的配置,而这之中的一大利器便是springboot的starter,starter是sp
- 相关阅读C#使用LINQ查询操作符实例代码(一)C#使用LINQ查询操作符实例代码(二)示例业务背景介绍示例参考《C#高级编程(第六版)》L
- C#支持的位逻辑运算符如表2.9所示。运算符号意义运算对象类型运算结果类型对象数实例~位逻辑非运算整型,字符型整型1~a&位逻辑与运
- 文档中的设置有序或无序列表是一种反应内容上下级关系或者内容相同属性的方式,与单纯的文字叙述相比,它能有效增强文档内容的条理性,突出重点。因此
- 安卓开发网络请求可谓是安卓开发的灵魂,如果你不会网络请求,那么你开发的应用软件就是一具没有灵魂的枯骨。在安卓开发中进行网络请求和java中的
- 一:讲故事上一篇介绍的 6 个特性从园子里的反馈来看效果不错,那这一篇就再带来 6 个特性同大家一起欣赏。二:特性分析1. 像弱类型语言一样
- 一、流程和任务的关系以下是一个简单的请假流程图,其中有一个开始事件,两个用户任务,一个结束事件。启动流程后,activiti会自动创建第一个
- 本文实例讲述了Android编程实现canvas绘制饼状统计图功能。分享给大家供大家参考,具体如下:本例的目的是实现一个简单的饼状统计图,效
- 一.为什么要用线程池先来看个简单的例子1.直接new Thread的情况:public static void main(String[]
- 多个线程访问共享对象和数据的方式有两种情况:1、每个线程执行的代码相同,例如,卖票:多个窗口同时卖这100张票,这100张票需要多个线程共享
- 前提在日常使用SpringMVC进行开发的时候,有可能遇到前端各种类型的请求参数,这里做一次相对全面的总结。SpringMVC中处理控制器参
- 类加载是什么把磁盘中的java文件加载到内存中的过程叫做类加载当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把
- 最近项目中有截屏的需求,普通的view截屏方法网上一搜一大把,但是SurfaceView截屏黑屏问题很多文章说的并不清楚,自己参考了一些别的
- SpringFramework5.0是自2013年12月版本4发布之后SpringFramework的第一个主发行版。SpringFrame
- 前言在foreach中删除元素时,每一次删除都会导致集合的大小和元素索引值发生变化,从而导致在foreach中删除元素时会抛出异常。集合已修