标签:
这种跟踪和我之前想的一样,应该是首先鼠标选中要跟踪的目标,然后计算其直方图。接下来就根据这个原始直方图计算每一帧的反向投影。(反向投影:opencv docs给出的概念是“一种记录给定图像中的像素点如何适应直方图模型像素分布的方式。简单的讲, 所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。) 然后根据反向投影用meanshift跟踪 meanShift(backproj, trackWindow,TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
//#include "StdAfx.h"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <ctype.h>
using namespace cv;
using namespace std;
Mat image;
bool backprojMode = false; //表示是否要进入反向投影模式,ture表示准备进入反向投影模式
bool selectObject = false;//代表是否在选要跟踪的初始目标,true表示正在用鼠标选择
int trackObject = 0; //目标跟踪状态标记
bool showHist = true;//是否显示直方图
Point origin;//用于保存鼠标选择第一次单击时点的位置
Rect selection;//用于保存鼠标选择的矩形框
void onMouse( int event, int x, int y, int, void* )
{
if(selectObject)//只有当鼠标左键按下去时才有效,然后通过if里面代码就可以确定所选择的矩形区域selection了
{
selection.x = MIN(x, origin.x);//矩形左上角顶点坐标
selection.y = MIN(y, origin.y);
selection.width = std::abs(x - origin.x);//矩形宽
selection.height = std::abs(y - origin.y);//矩形高
selection &= Rect(0, 0, image.cols, image.rows);//用于确保所选的矩形区域在图片范围内
}
switch(event)
{
case CV_EVENT_LBUTTONDOWN:
origin = Point(x,y);
selection = Rect(x,y,0,0);//鼠标刚按下去时初始化了一个矩形区域
selectObject = true;
break;
case CV_EVENT_LBUTTONUP:
selectObject = false;
if(selection.width > 0 && selection.height > 0)
trackObject = -1;
break;
}
}
void help()
{
cout << "\nThis is a demo that shows mean-shift based tracking\n"
"You select a color objects such as your face and it tracks it.\n"
"This reads from video camera (0 by default, or the camera number the user enters\n"
"Usage: \n"
" ./camshiftdemo [camera number]\n";
cout << "\n\nHot keys: \n"
"\tESC - quit the program\n"
"\tc - stop the tracking\n"
"\tb - switch to/from backprojection view\n"
"\th - show/hide object histogram\n"
"\tp - pause video\n"
"To initialize tracking, select the object with mouse\n";
}
int main( int argc, const char** argv )
{
help();
VideoCapture cap; //定义一个摄像头捕捉的类对象
Rect trackWindow;
int hsize = 16;
float hranges[] = {0,180};//hranges在后面的计算直方图函数中要用到
const float* phranges = hranges;
char filename[50];
int count = 0;
cap.open("C:\\Users\\Administrator\\Desktop\\工作\\testv\\a (6).avi");
if( !cap.isOpened() )
{
help();
cout << "***Could not initialize capturing...***\n";
cout << "Current parameter's value: \n";
cout << "D:\\Vedio_Example\\768x576.avi";
return -1;
}
namedWindow("Histogram", 0);
namedWindow("CamShift Demo");
setMouseCallback("CamShift Demo", onMouse, 0); //鼠标消息响应机制
Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
bool paused = false;
for(;;)
{
if(!paused)//没有暂停
{
cap >> frame;//从摄像头抓取一帧图像并输出到frame中
count++;
cout<<"帧序数"<<count<<endl;
if( frame.empty() )
break;
}
frame.copyTo(image);
if(!paused)//没有按暂停键
{
cvtColor(image, hsv, CV_BGR2HSV);//将rgb摄像头帧转化成hsv空间的
if(trackObject)//trackObject初始化为0,按完键盘的'c'键后也为0,只有在跟踪区域选定的情况下才符合条件,进入跟踪
{
//构建掩膜mask
inRange(hsv, Scalar(0, 30, 10),Scalar(180, 256, 256), mask);
int ch[] = {0, 0};
hue.create(hsv.size(), hsv.depth());//hue初始化为与hsv大小深度一样的矩阵,色调的度量是用角度表示的,红绿蓝之间相差120度,反色相差180度
mixChannels(&hsv, 1, &hue, 1, ch, 1);//将hsv第一个通道(色调,索引为0)的数复制到hue的第一个通道(索引为0)中,最后一个参数代表只是一组通道间的数据复制
if(trackObject < 0)//鼠标选择区域松开后,该函数内部又将其赋值1
{
//分别设置色调图hue及掩码图中的selection部分为其感兴趣区域
Mat roi(hue, selection), maskroi(mask, selection);
//calcHist()函数第一个参数为输入矩阵序列,第2个参数表示输入的矩阵数目,第3个参数表示将被计算直方图维数通道的列表,第4个参数表示可选的掩膜函数
//第5个参数表示输出直方图,第6个参数表示直方图的维数,第7个参数为每一维直方图数组的大小,第8个参数为每一维直方图bin的边界
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);//将roi的0通道计算直方图并放入hist中,hsize为每一维直方图的大小
normalize(hist, hist, 0, 255, CV_MINMAX);//将hist矩阵进行数组范围归一化,都归一化到0~255
trackWindow = selection;
trackObject = 1;//只要鼠标选完区域松开后,且没有按键盘清0键'c',则trackObject一直保持为1,因此该if函数只能执行一次,除非重新选择跟踪区域
histimg = Scalar::all(0);//与按下'c'键是一样的,这里的all(0)表示的是标量全部清0
int binW = histimg.cols / hsize; //histing是一个200*300的矩阵,hsize应该是每一个bin的宽度,也就是histing矩阵能分出几个bin出来
Mat buf(1, hsize, CV_8UC3);//定义一个缓冲单bin矩阵
for(int i = 0; i < hsize; i++)//saturate_case函数为从一个初始类型准确变换到另一个初始类型
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);//Vec3b为3个char值的向量
cvtColor(buf, buf, CV_HSV2BGR);//将hsv又转换成bgr
for(int i = 0; i < hsize; i++)
{
int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);//at函数为返回一个指定数组元素的参考值
rectangle( histimg, Point(i*binW,histimg.rows), //在一幅输入图像上画一个简单抽的矩形,指定左上角和右下角,并定义颜色,大小,线型等
Point((i+1)*binW,histimg.rows - val),
Scalar(buf.at<Vec3b>(i)), -1, 8 );
}
}
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);//计算直方图的反向投影,计算hue图像0通道直方图hist的反向投影,并存入backproj中
backproj &= mask;
//meanShift迭代跟踪
meanShift(backproj, trackWindow, //trackWindow为鼠标选择的区域,TermCriteria为确定迭代终止的准则
TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));//CV_TERMCRIT_EPS是通过forest_accuracy,CV_TERMCRIT_ITER
if(backprojMode)
cvtColor(backproj, image, CV_GRAY2BGR);//因此投影模式下显示的也是rgb图?
rectangle(image,Point(trackWindow.x,trackWindow.y),Point(trackWindow.x+trackWindow.width,trackWindow.y+trackWindow.height),Scalar(0,0,255),2,CV_AA);
}
}
else if(trackObject < 0)//暂停键按了,另外还重新选定了矩形框,则继续开始执行跟踪
paused = false;
//后面的代码是不管pause为真还是为假都要执行的
imshow("CamShift Demo", image);
// sprintf(filename,"%s%d%s","frame",count,".jpg");
// imwrite(filename,image);
imshow("Histogram", histimg);
char c = (char)waitKey(30);
if( c == 27 ) //退出键
break;
switch(c)
{
case 'b': //反向投影模型交替
backprojMode = !backprojMode;
break;
case 'c': //清零跟踪目标对象
trackObject = 0;
histimg = Scalar::all(0);
break;
case 'h': //显示直方图交替
showHist = !showHist;
if( !showHist )
destroyWindow("Histogram");
else
namedWindow("Histogram", 1);
break;
case 'p': //暂停跟踪交替
paused = !paused;
break;
default:
;
}
}
return 0;
}标签:
原文地址:http://blog.csdn.net/yang332233/article/details/51350647