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:参数相同,返回数不同,一个是返回个数(通过函数引用参数返回最佳匹配),一个是最佳匹配位置。

posted @ 2015-04-28 14:25  ScienceBug  阅读(208)  评论(0)    收藏  举报