【问题标题】:Real-time template matching - OpenCV, C++实时模板匹配 - OpenCV、C++
【发布时间】:2013-11-24 19:46:23
【问题描述】:

我正在尝试使用模板实现实时跟踪。我希望用每一帧更新模板。我所做的主要修改是:

1) 将模板匹配和 minmaxLoc 分离为单独的模块,分别为 TplMatch() 和 minmax() 函数。

2) 在 track() 函数中,select_flag 始终保持为真,这样每次迭代都会将新模板复制到“myTemplate”。

3)函数track()的最后3行是更新模板(roiImg)。

4) 另外,我已经删除了 track() 函数的所有参数,因为 img 和 roiImg 是全局变量,因此无需将它们传递给函数。

以下是代码:

#include <iostream>
#include "opencv2/opencv.hpp"
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/objdetect/objdetect.hpp>

#include <sstream>


using namespace cv;
using namespace std;

Point point1, point2; /* vertical points of the bounding box */
int drag = 0;
Rect rect; /* bounding box */
Mat img, roiImg; /* roiImg - the part of the image in the bounding box */
int select_flag = 0;
bool go_fast = false;

Mat mytemplate;


///------- template matching -----------------------------------------------------------------------------------------------

Mat TplMatch( Mat &img, Mat &mytemplate )
{
  Mat result;

  matchTemplate( img, mytemplate, result, CV_TM_SQDIFF_NORMED );
  normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );

  return result;
}


///------- Localizing the best match with minMaxLoc ------------------------------------------------------------------------

Point minmax( Mat &result )
{
  double minVal, maxVal;
  Point  minLoc, maxLoc, matchLoc;

  minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
  matchLoc = minLoc;

  return matchLoc;
}


///------- tracking --------------------------------------------------------------------------------------------------------

void track()
{
    if (select_flag)
    {
        roiImg.copyTo(mytemplate);
//         select_flag = false;
        go_fast = true;
    }

//     imshow( "mytemplate", mytemplate ); waitKey(0);

    Mat result  =  TplMatch( img, mytemplate );
    Point match =  minmax( result ); 

    rectangle( img, match, Point( match.x + mytemplate.cols , match.y + mytemplate.rows ), CV_RGB(255, 255, 255), 0.5 );

    std::cout << "match: " << match << endl;

    /// latest match is the new template
    Rect ROI = cv::Rect( match.x, match.y, mytemplate.cols, mytemplate.rows );
    roiImg = img( ROI );
    imshow( "roiImg", roiImg ); //waitKey(0);
}


///------- MouseCallback function ------------------------------------------------------------------------------------------

void mouseHandler(int event, int x, int y, int flags, void *param)
{
    if (event == CV_EVENT_LBUTTONDOWN && !drag)
    {
        /// left button clicked. ROI selection begins
        point1 = Point(x, y);
        drag = 1;
    }

    if (event == CV_EVENT_MOUSEMOVE && drag)
    {
        /// mouse dragged. ROI being selected
        Mat img1 = img.clone();
        point2 = Point(x, y);
        rectangle(img1, point1, point2, CV_RGB(255, 0, 0), 3, 8, 0);
        imshow("image", img1);
    }

    if (event == CV_EVENT_LBUTTONUP && drag)
    {
        point2 = Point(x, y);
        rect = Rect(point1.x, point1.y, x - point1.x, y - point1.y);
        drag = 0;
        roiImg = img(rect);
//  imshow("MOUSE roiImg", roiImg); waitKey(0);
    }

    if (event == CV_EVENT_LBUTTONUP)
    {
        /// ROI selected
        select_flag = 1;
        drag = 0;
    }

}



///------- Main() ----------------------------------------------------------------------------------------------------------

int main()
{
    int k;
/*    
///open webcam
    VideoCapture cap(0);
    if (!cap.isOpened())
      return 1;*/

    ///open video file
    VideoCapture cap;
    cap.open( "Megamind.avi" );
    if ( !cap.isOpened() )
    {   cout << "Unable to open video file" << endl;    return -1;    }
/*    
    /// Set video to 320x240
     cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
     cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);*/

    cap >> img;
    GaussianBlur( img, img, Size(7,7), 3.0 );
    imshow( "image", img );

    while (1)
    {
        cap >> img;
        if ( img.empty() )
            break;

    // Flip the frame horizontally and add blur
    cv::flip( img, img, 1 );
    GaussianBlur( img, img, Size(7,7), 3.0 );

        if ( rect.width == 0 && rect.height == 0 )
            cvSetMouseCallback( "image", mouseHandler, NULL );
        else
            track();

        imshow("image", img);
//  waitKey(100);   k = waitKey(75);
    k = waitKey(go_fast ? 30 : 10000);
        if (k == 27)
            break;
    }

    return 0;
}

更新的模板没有被跟踪。我无法弄清楚为什么会发生这种情况,因为我每次迭代都会更新我的模板(roiImg)。 minmax() 函数的 ma​​tch 值每次都返回相同的点(坐标)。测试视频可在:http://www.youtube.com/watch?v=vpnkk7N2E0Q&feature=youtu.be 请仔细研究并指导...非常感谢!

【问题讨论】:

    标签: c++ opencv image-processing computer-vision template-matching


    【解决方案1】:

    我从你的问题的这个版本中得到你的原始代码:https://stackoverflow.com/revisions/20180073/3

    我对您的原始代码做了最小的更改,我的结果代码如下:

    #include <iostream>
    #include "opencv2/opencv.hpp"
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/objdetect/objdetect.hpp>
    
    #include <sstream>
    
    
    using namespace cv;
    using namespace std;
    
    Point point1, point2; /* vertical points of the bounding box */
    int drag = 0;
    Rect rect; /* bounding box */
    Mat img, roiImg; /* roiImg - the part of the image in the bounding box */
    int select_flag = 0;
    bool go_fast = false;
    
    Mat mytemplate;
    
    
    ///------- template matching -----------------------------------------------------------------------------------------------
    
    Mat TplMatch( Mat &img, Mat &mytemplate )
    {
      Mat result;
    
      matchTemplate( img, mytemplate, result, CV_TM_SQDIFF_NORMED );
      normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
    
      return result;
    }
    
    
    ///------- Localizing the best match with minMaxLoc ------------------------------------------------------------------------
    
    Point minmax( Mat &result )
    {
      double minVal, maxVal;
      Point  minLoc, maxLoc, matchLoc;
    
      minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
      matchLoc = minLoc;
    
      return matchLoc;
    }
    
    
    ///------- tracking --------------------------------------------------------------------------------------------------------
    
    void track()
    {
        if (select_flag)
        {
            //roiImg.copyTo(mytemplate);
    //         select_flag = false;
            go_fast = true;
        }
    
    //     imshow( "mytemplate", mytemplate ); waitKey(0);
    
        Mat result  =  TplMatch( img, mytemplate );
        Point match =  minmax( result ); 
    
        rectangle( img, match, Point( match.x + mytemplate.cols , match.y + mytemplate.rows ), CV_RGB(255, 255, 255), 0.5 );
    
        std::cout << "match: " << match << endl;
    
        /// latest match is the new template
        Rect ROI = cv::Rect( match.x, match.y, mytemplate.cols, mytemplate.rows );
        roiImg = img( ROI );
        roiImg.copyTo(mytemplate);
        imshow( "roiImg", roiImg ); //waitKey(0);
    }
    
    
    ///------- MouseCallback function ------------------------------------------------------------------------------------------
    
    void mouseHandler(int event, int x, int y, int flags, void *param)
    {
        if (event == CV_EVENT_LBUTTONDOWN && !drag)
        {
            /// left button clicked. ROI selection begins
            point1 = Point(x, y);
            drag = 1;
        }
    
        if (event == CV_EVENT_MOUSEMOVE && drag)
        {
            /// mouse dragged. ROI being selected
            Mat img1 = img.clone();
            point2 = Point(x, y);
            rectangle(img1, point1, point2, CV_RGB(255, 0, 0), 3, 8, 0);
            imshow("image", img1);
        }
    
        if (event == CV_EVENT_LBUTTONUP && drag)
        {
            point2 = Point(x, y);
            rect = Rect(point1.x, point1.y, x - point1.x, y - point1.y);
            drag = 0;
            roiImg = img(rect);
            roiImg.copyTo(mytemplate);
    //  imshow("MOUSE roiImg", roiImg); waitKey(0);
        }
    
        if (event == CV_EVENT_LBUTTONUP)
        {
            /// ROI selected
            select_flag = 1;
            drag = 0;
        }
    
    }
    
    
    
    ///------- Main() ----------------------------------------------------------------------------------------------------------
    
    int main()
    {
        int k;
    /*    
    ///open webcam
        VideoCapture cap(0);
        if (!cap.isOpened())
          return 1;*/
    
        ///open video file
        VideoCapture cap;
        cap.open( "Megamind.avi" );
        if ( !cap.isOpened() )
        {   cout << "Unable to open video file" << endl;    return -1;    }
    /*    
        /// Set video to 320x240
         cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
         cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);*/
    
        cap >> img;
        GaussianBlur( img, img, Size(7,7), 3.0 );
        imshow( "image", img );
    
        while (1)
        {
            cap >> img;
            if ( img.empty() )
                break;
    
        // Flip the frame horizontally and add blur
        cv::flip( img, img, 1 );
        GaussianBlur( img, img, Size(7,7), 3.0 );
    
            if ( rect.width == 0 && rect.height == 0 )
                cvSetMouseCallback( "image", mouseHandler, NULL );
            else
                track();
    
            imshow("image", img);
    //  waitKey(100);   k = waitKey(75);
        k = waitKey(go_fast ? 30 : 10000);
            if (k == 27)
                break;
        }
    
        return 0;
    }
    

    https://www.youtube.com/watch?v=rBCopeneCos 的视频展示了对上述程序的测试。

    我会避免使用全局变量,因为我认为它们无助于理解问题所在;此外,我还会注意 OpenCV 的 Mat 类的浅拷贝和深拷贝,正如 1'' 在他的 answer 中所写:

    OpenCV 的Mat 类只是实际图像数据的标头, 它包含一个指向的指针。 operator= 复制指针 (以及标题中的其他信息,如图像尺寸) 这样两个 Mats 共享相同的数据。这意味着修改 一个 Mat 中的数据也会更改另一个 Mat 中的数据。这被称为 “浅”复制,因为只复制顶层(标题),而不是 底层(数据)。

    要制作基础数据的副本(称为“深层副本”),请使用 clone() 方法。您可以在以下页面上找到有关它的信息 你链接到。

    编辑漂移: 在评论Real-time template matching - OpenCV, C++,learner 询问跟踪漂移。 看视频https://www.youtube.com/watch?v=rBCopeneCos,我们看到在视频的开头,程序正在跟踪女孩的右眼,而在 0:15 开始跟踪女孩的眉毛,在 0:19 开始跟踪男孩的眉毛,然后不再跟踪女孩的眼睛,例如在 0:27 它跟踪女孩的右眉毛,而女孩的右眼在图像中清晰可见。

    从跟踪眼睛到跟踪眉毛的这种漂移在我发布的一个简单代码中是正常的,解释很简单:参见https://www.youtube.com/watch?v=sGHEu3u9XvI的视频,视频从跟踪开始(黑色的内容矩形),然后我从场景中取出扑克牌,跟踪黑色矩形“漂移”到场景的左下角;毕竟我们不断更新模板,所以行为是正确的:程序停止跟踪扑克牌并开始跟踪白色背景,所以你有“漂移”......换句话说,你的 TplMatch() 函数将始终返回有效的 result 图像,并且您当前的 minmax() 实现将始终返回有效的最小值。

    【讨论】:

    • 感谢您的时间和指导我......它运作良好。不知何故,有一些漂移进来,但我会尝试处理它。我知道这种漂移是频繁的模板更新所固有的...... OpenCV 的 Mat 类的浅拷贝和深拷贝是什么意思?
    • @learner stackoverflow.com/search?q=shallow+deep+opencv 引用自stackoverflow.com/a/18154264/15485 OpenCV 的 Mat 类只是实际图像数据的标头,其中包含指向的指针。 = 运算符复制指针,以便两个 Mats 共享相同的数据。这意味着修改一个 Mat 中的数据也会更改另一个 Mat 中的数据。这称为“浅”复制,因为仅复制顶层(标题),而不复制较低层(数据)。要制作底层数据的副本(“深拷贝”),请使用 clone() 方法。
    • @learner “漂移”是什么意思?它在我发布的视频中可见吗?
    • 再次感谢伙计!而且,是的,看着视频的前 15-16 秒,画在眼睛上的方框似乎向前额移动。另外,我在某处读到这种漂移是频繁更新模板的固有问题。虽然,我还没有找到原因。我认为它应该更准确,因为我们在每一帧都更新模板,从而克服了动态外观变化。你说什么??
    • @learner 我用我对漂移的解释更新了答案。
    【解决方案2】:

    您可以关注 OpenCV 教程"Template Matching"。您的track 函数可能包含在当前帧中查找模板的代码;一个简单的代码基于matchTemplate 和minMaxLoc 函数。

    与问题的“实时”部分相关的有趣问题是在当前帧和下一帧之间的时间内成功找到匹配项(如果存在)。

    编辑:

    以下简单粗暴的代码和http://www.youtube.com/watch?v=vpnkk7N2E0Q&feature=youtu.be 的视频展示了我对跟踪 的含义。

    由于我没有网络摄像头,因此我稍微修改了您的代码以仅使用视频,这是https://code.ros.org/trac/opencv/export/7237/trunk/opencv/samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video/Megamind.avi

    然后我添加track 函数和一些逻辑来减慢视频的速度,直到我选择一个 ROI,然后以正常速度播放视频。

    #include <iostream>
    #include "opencv2/opencv.hpp"
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/objdetect/objdetect.hpp>
    
    #include <sstream>
    
    
    using namespace cv;
    using namespace std;
    
    Point point1, point2; /* vertical points of the bounding box */
    int drag = 0;
    Rect rect; /* bounding box */
    Mat img, roiImg; /* roiImg - the part of the image in the bounding box */
    int select_flag = 0;
    bool go_fast = false;
    
    Mat mytemplate;
    
    void track(cv::Mat &img, const cv::Mat &templ, const cv::Rect &r )
    {
        static int n = 0;
    
        if (select_flag)
        {
            templ.copyTo(mytemplate);
            select_flag = false;
            go_fast = true;
        }
    
    
        cv::Mat result;
        /// Do the Matching and Normalize
        matchTemplate( img, mytemplate, result, CV_TM_SQDIFF_NORMED );
        normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
    
        /// Localizing the best match with minMaxLoc
        double minVal; double maxVal; Point minLoc; Point maxLoc;
        Point matchLoc;
    
        minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
        matchLoc = minLoc;
    
        rectangle( img, matchLoc, Point( matchLoc.x + mytemplate.cols , matchLoc.y + mytemplate.rows ), CV_RGB(255, 255, 255), 3 );
    
        std::cout << matchLoc << "\n";
    }
    
    ///MouseCallback function
    
    void mouseHandler(int event, int x, int y, int flags, void *param)
    {
        if (event == CV_EVENT_LBUTTONDOWN && !drag)
        {
            /* left button clicked. ROI selection begins */
            point1 = Point(x, y);
            drag = 1;
        }
    
        if (event == CV_EVENT_MOUSEMOVE && drag)
        {
            /* mouse dragged. ROI being selected */
            Mat img1 = img.clone();
            point2 = Point(x, y);
            rectangle(img1, point1, point2, CV_RGB(255, 0, 0), 3, 8, 0);
            imshow("image", img1);
        }
    
        if (event == CV_EVENT_LBUTTONUP && drag)
        {
            point2 = Point(x, y);
            rect = Rect(point1.x, point1.y, x - point1.x, y - point1.y);
            drag = 0;
            roiImg = img(rect);
        }
    
        if (event == CV_EVENT_LBUTTONUP)
        {
            /* ROI selected */
            select_flag = 1;
            drag = 0;
        }
    
    }
    
    
    ///Main function
    
    int main()
    {
        int k;
        /*
            VideoCapture cap(0);
            if (!cap.isOpened())
            return 1;
        */
        VideoCapture cap;
        //cap.open("~/Downloads/opencv-2.4.4/samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video/Megamind.avi");
        cap.open("./Megamind.avi");
        if (!cap.isOpened())
        {
            printf("Unable to open video file\n");
            return -1;
        }
    
        /*
            // Set video to 320x240
            cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
            cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);
            */
    
        cap >> img;
        imshow("image", img);
    
        while (1)
        {
            cap >> img;
            if (img.empty())
                break;
    
            if (rect.width == 0 && rect.height == 0)
                cvSetMouseCallback("image", mouseHandler, NULL);
            else
                track(img, roiImg, rect);
    
            if (select_flag == 1)
                imshow("Template", roiImg);
    
            imshow("image", img);
            k = waitKey(go_fast ? 30 : 10000);
            if (k == 27)
                break;
    
        }
    
    
        return 0;
    }
    

    【讨论】:

    • 我确实尝试了使用“CV_TM_SQDIFF_NORMED”度量的 matchTemplate() 函数。但我没有得到想要的跟踪......
    • @learner 我不明白,你说的“跟踪”是什么意思?
    • 当我尝试 matchTemplate() 函数时,结果只是一个窗口(我用鼠标拖动的矩形大小),它显示了与该区域相对应的网络摄像头提要......这很难像这样解释......你能建议一个示例代码如何继续吗?
    • @learner 正如我在答案中所写,本教程包含示例代码:docs.opencv.org/doc/tutorials/imgproc/histograms/…
    • 我已经尝试过了。我正在获取图像和模板。我所需要的只是跟踪它......只是卡在那里:(
    【解决方案3】:

    您也可以从这个维基百科页面http://en.wikipedia.org/wiki/Video_tracking开始对该主题进行一般性介绍

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-06
      • 1970-01-01
      • 2015-04-20
      相关资源
      最近更新 更多