这种跟踪和我之前想的一样,应该是首先鼠标选中要跟踪的目标,然后计算其直方图。接下来就根据这个原始直方图计算每一帧的反向投影。(反向投影: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; }
时间: 2024-11-08 23:39:08