想通过camShift中的RotatedRect成员center来勾勒出目标的运动轨迹。
即将质心存入vector中,在需要显示时,用line来展现出来。
以下为代码:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <OpenCV245.h> using namespace std; using namespace cv; Mat image; bool backprojMode = false; //表示是否要进入反向投影模式,ture表示准备进入反向投影模式 bool selectObject = false; //用来判断是否选中,当鼠标左键按下时为ture,左键松开时为false int trackObject = 0; //代表跟踪目标数目 bool showHist = true; //是否显示直方图 Point origin; //选中的起点 Rect selection; //选中的区域 Point centroid; //质心 Mat trackPic; //轨迹图 Point startP; //前点 Point endP; //后点 bool mark = false; vector<Point> trackPointVec; int vmin = 10, vmax = 256, smin = 30; //鼠标事件响应函数,这个函数从按下左键时开始响应直到左键释放 static void onMouse(int event, int x, int y, int, void* ) { if( selectObject ) { selection.x = MIN(x, origin.x); selection.y = MIN(y, origin.y); selection.width = abs(x - origin.x); selection.height = abs(y - origin.y); } switch (event) { case CV_EVENT_LBUTTONDOWN: //按下鼠标时,捕获点origin 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; } } //const char* keys = //{ // "{1| | 0 | camera number}" //}; //const char* keys = { "{1| | 0 | camera number}" }; int _tmain(int argc, _TCHAR* argv[]) { const string strFilePath = "C:\\Users\\sony\\Desktop\\k\\video\\停车场出入口.flv"; VideoCapture cap; Rect trackWindow; //跟踪的窗口 int hsize = 16; //创建直方图时要用的常量 float hranges[] = {0, 180}; const float* phranges = hranges; cap.open(strFilePath); if (!cap.isOpened()) { cout << "fail to open the video"<<endl; return -1; } //关于显示窗口的一些设置 namedWindow("Histogram", 0); namedWindow("CamShift Demo", 0); //设置鼠标事件,把鼠标响应与onMouse函数关联起来 setMouseCallback("CamShift Demo", onMouse, 0); //创建三个滑块条,特定条件用滑块条选择不同参数能获得较好的跟踪效果 createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 ); createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 ); createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 ); Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj; bool paused = false; for(;;) { if ( !paused ) //没有暂停 { cap >> frame; //从摄像头输入frame if(frame.empty()) break; } frame.copyTo(image); if( !paused ) { cvtColor(image, hsv, CV_BGR2HSV); if( trackObject ) //松开鼠标左键时,trackObject为-1,执行核心部分 { int _vmin = vmin, _vmax = vmax; //int vmin = 10, vmax = 256, smin = 30; //inRange用来检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵mask(0-1矩阵) //这里用于制作掩膜板,只处理像素值为H:0~180,S:smin~256, V:vmin~vmax之间的部分。mask是要求的,单通道 inRange(hsv, Scalar(0, smin, MIN(_vmin, _vmax)), Scalar(180, 256, MAX(_vmin, _vmax)), mask); int ch[] = {0, 0}; hue.create(hsv.size(), hsv.depth()); mixChannels(&hsv, 1, &hue, 1, ch, 1);//将H分量拷贝到hue中,其他分量不拷贝 if ( trackObject < 0 ) { 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); normalize(hist, hist, 0, 255, CV_MINMAX); 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); for( int i = 0; i < hsize; i++ ) buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180/hsize), 255, 255); cvtColor(buf, buf, CV_HSV2BGR); for( int i = 0; i < hsize; i++ ) { int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255); 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); //计算两个矩阵backproj、mask的每个元素的按位与,返回backproj backproj &= mask; RotatedRect trackBox = CamShift(backproj, trackWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1)); if( trackWindow.area() <= 1 ) { int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6; trackWindow = Rect(trackWindow.x - r, trackWindow.y - r, trackWindow.x + r, trackWindow.y + r) & Rect(0, 0, cols, rows); } if (backprojMode) cvtColor( backproj, image, CV_GRAY2BGR); ellipse(image, trackBox, Scalar(0, 0, 255), 3, CV_AA); trackPointVec.push_back(trackBox.center); /*trackPic = Mat::zeros(backproj.size(), CV_8UC3);*/ //让if中的内容只执行一次 /*if ( mark ) { startP = Point(0,0); startP = trackBox.center; mark = !mark; } */ /*line(trackPic, startP, trackBox.center, Scalar(255, 0, 0), 3); startP = trackBox.center; imshow( "trackpicture",trackPic);*/ } } else if( trackObject < 0 ) paused = false; if( selectObject && selection.width > 0 && selection.height > 0 ) { Mat roi(image, selection); bitwise_not(roi, roi); } if(mark) { imshow("trackpicture",trackPic); mark != mark; } imshow( "CamShift Demo", image ); imshow( "Histogram", histimg ); //显示轨迹 //每轮都要等待用户的按键控制 char c = (char)waitKey(300); if( c == 27 )//"Esc"键,直接退出 break; switch(c) { case 'b'://转换显示方式 backprojMode = !backprojMode; break; case 'c'://停止追踪 trackObject = 0; histimg = Scalar::all(0); mark = !mark; break; case 'h'://隐藏或显示直方图 showHist = !showHist; if( !showHist ) destroyWindow( "Histogram" ); else namedWindow( "Histogram", 1 ); break; case 'p'://暂停 paused = !paused;//frame停止从摄像头获取图像,只显示旧的图像 break; case 't': trackPic =Mat::zeros(backproj.size(), CV_8UC3); for (vector<Point>::iterator it = trackPointVec.begin(); it != trackPointVec.end(); it++) { if ( (it+1) != trackPointVec.end() ) { line(trackPic, *it, *(it+1), Scalar(255, 0, 0), 2); } } mark = !mark; default: ; } } return 0; }
轨迹图:
直方图:
原图: