CamShift_meanShift
CamShift
实现了“Computer Vision Face Tracking For Use in a Perceptual User Interface”论文算法。首先利用meanshift寻找物体的中心,然后调整窗口大小和寻找最佳位置的的旋转矩形。
学习OpenCV提供的例子:总体来说就是首先要获得目标物体的直方图,再要搜寻的图像上进行反向投影,获得投影结果,在利用核心函数进行迭代的寻找最佳匹配位置。
#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;
int trackObject = 0; bool showHist = true;
Point origin; Rect selection; bool selectObject = false;
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 = 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;
}
}
static 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"; }
void drawHist1D(cv::Mat& hist,int style = 1,int DRAW_H=400,int DRAW_W=400)
{
Mat histImage = Mat::zeros(DRAW_W, DRAW_W, CV_8U);
int histsize = hist.rows;
normalize(hist, hist, 0, DRAW_H, CV_MINMAX, CV_32F);
int DRAW_BIN_W = cvRound((double)histImage.cols/histsize);
switch (style) {
case 1:
{
for( int i = 0; i < histsize; i++ )
rectangle( histImage, Point(i*DRAW_BIN_W, histImage.rows),Point((i+1)*DRAW_BIN_W, histImage.rows - cvRound(hist.at<float>(i))), Scalar::all(255), -1, 8, 0 ); break; }
case 2:
{
for(int i=1;i<histsize;i++)
{
line(histImage,Point(DRAW_BIN_W*(i-1),DRAW_H-cvRound(hist.at<float>((i-1)))),
Point(DRAW_BIN_W*(i),DRAW_H-cvRound(hist.at<float>(i))),Scalar(255),2,8,0);
}
}
}
std::string title = "histPicture_num"; title += style; imshow(title,histImage); }
const char* keys = { "{1| | 0 | camera number}" };
int main( int argc, const char** argv ) {
help();
// 打开摄像头
VideoCapture cap;
CommandLineParser parser(argc, argv, keys);
int camNum = parser.get<int>("1"); cap.open(camNum);
if( !cap.isOpened() )
{ help();
cout << "***Could not initialize capturing...***\n";
cout << "Current parameter's value: \n"; parser.printParams();
return -1; }
// 设置直方图的参数
int hsize = 16;
float hranges[] = {0,180};
const float* phranges = hranges;
namedWindow( "Histogram", 0 );
// 添加窗口,并且为窗口增加鼠标响应和添加滑动按钮
namedWindow( "CamShift Demo", 0 );
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;
Rect trackWindow;
for(;;) {
if( !paused ) { cap >> frame; if( frame.empty() ) break; }
frame.copyTo(image);
if( !paused ) {
cvtColor(image, hsv, COLOR_BGR2HSV);
if( trackObject ) {
int _vmin = vmin, _vmax = vmax;
inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
Scalar(180, 256, MAX(_vmin, _vmax)),
mask);//输出mask图像是一个淹没图像,255/0;
int ch[] = {0, 0};
hue.create(hsv.size(), hsv.depth());
mixChannels(&hsv, 1, &hue, 1, ch, 1);//仅复制h通道到hue,hue只有一个通道
if( trackObject < 0 ) {
Mat roi(hue, selection), maskroi(mask, selection);//对hue 图像和掩模图像设置ROI区域,
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
normalize(hist, hist, 0, 255, CV_MINMAX);//用最大最小值归一化到0~255
trackWindow = selection; trackObject = 1;
drawHist1D(hist,1);
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
backproj &= mask;
imshow("backproj",backproj);
//RotatedRect trackBox = CamShift(backproj, trackWindow,TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ));
meanShift(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, COLOR_GRAY2BGR );
//ellipse( image, trackBox, Scalar(0,0,255), 3, CV_AA );
rectangle(image,Point(trackWindow.x,trackWindow.y),
Point(trackWindow.x+trackWindow.width,trackWindow.y+trackWindow.height),Scalar(0,0,255),-1,CV_AA);
}
}
else if( trackObject < 0 ) paused = false;
if( selectObject && selection.width > 0 && selection.height > 0 ) {
Mat roi(image, selection);
bitwise_not(roi, roi);//因为这里,在进行选择的时候图像颜色反转
}
imshow( "CamShift Demo", image ); imshow( "Histogram", histimg );
char c = (char)waitKey(10);
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;
}
例子中主要函数:
inRange:利用函数生成模板,在这个程序中通过把图像转换到HSV颜色空间后,分别对HSV三个分量进行判断,如果在两个Scalar的范围内,再输入参数矩阵中就是255,否则是0。这样得到的模板是跟颜色很大关系的。
mixChannels:函数要求输出矩阵必须是提前分配好内存的数据区,所以在调用之前可以用create函数进行生成;参数说明,fromTo,就是源和目的地的不同通道之间复制数据的有序对。
calcHist:计算图像的直方图,经常用normalize进行归一化,
calcBackProject:计算反向投影,就是首先得到一个目标物体的直方图,然后利用此函数在图像寻找与目标直方图匹配的位置(具体原理需要看论文),得到反向投影矩阵(这个矩阵就是meanshift或者CamShift需要的反向投影矩阵),如果要是想追踪某个物体,在获取直方图时候应该使得物体能够沾满整个屏幕,以使得针对这个颜色的bin能够概率很大,计算出的反向投影矩阵也就是越好。
bitwise_not(roi,roi):不算是重点函数,就是一个技巧,
meanshift和CamShift:参数相同,返回数不同,一个是返回个数(通过函数引用参数返回最佳匹配),一个是最佳匹配位置。

浙公网安备 33010602011771号