Сопоставление шаблонов в реальном времени — OpenCV, C++

Я пытаюсь реализовать отслеживание в реальном времени с помощью шаблонов. Я хочу обновлять шаблон с каждым кадром. Основные модификации, которые я сделал:

1) разделил сопоставление с шаблоном и minmaxLoc на отдельные модули, а именно функции TplMatch() и minmax() соответственно.

2) Внутри функции track() флаг select_flag всегда остается истинным, поэтому новый шаблон копируется в 'myTemplate' при каждой итерации.

3) Последние 3 строки функции track() предназначены для обновления шаблона (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) с каждой итерацией. Значение match из функции minmax() каждый раз возвращает одну и ту же точку (координаты). Тестовое видео доступно по адресу: http://www.youtube.com/watch?v=vpnkk7N2E0Q&feature=youtu.be Пожалуйста, изучите его и направьте вперед... большое спасибо!


person learner    schedule 24.11.2013    source источник


Ответы (3)


Я получаю исходный код из этой версии вашего вопроса: 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. показывает тест вышеуказанной программы.

Я бы избегал использования глобальных переменных, потому что я думаю, что они не помогают понять, в чем заключаются проблемы; кроме того, я бы также обратил внимание на поверхностную и глубокую копию для класса Mat OpenCV, как написал 1'' в своем ответ:

Класс Mat OpenCV — это просто заголовок для фактических данных изображения, на которые он содержит указатель. operator= копирует указатель (и другую информацию в заголовке, например размеры изображения), так что оба мата используют одни и те же данные. Это означает, что изменение данных в одном мате также изменяет их в другом. Это называется «поверхностной» копией, поскольку копируется только верхний уровень (заголовок), а не нижний уровень (данные).

Чтобы сделать копию базовых данных (называемую «глубокой копией»), используйте метод clone(). Вы можете найти информацию об этом на странице, на которую вы ссылаетесь.

Редактировать о дрейфе: В комментарии сопоставление шаблона времени - OpenCV, C++, обучаемый спрашивает о дрейфе отслеживания. Глядя на видео https://www.youtube.com/watch?v=rBCopeneCos мы видим, что в начале видео программа отслеживает правый глаз девочки, в то время как в 0:15 она начинает отслеживать брови девочки, в 0:19 она начинает отслеживать брови мальчика и больше не отслеживает глаз девочки , например, на 0:27 он отслеживает правую бровь девушки, в то время как правый глаз девушки хорошо виден на изображении.

Этот переход от отслеживания глаз к отслеживанию бровей является нормальным в простом коде, таком как тот, который я разместил, и объяснение довольно простое: см. видео по адресу https://www.youtube.com/watch?v=sGHEu3u9XvI, видео начинается с отслеживания (содержимое черного прямоугольника) игральной карты, затем я удалите игральную карту со сцены, и черный прямоугольник отслеживания «дрейфует» в левый нижний угол сцены; в конце концов, мы постоянно обновляем шаблон, и поэтому поведение правильное: программа перестает отслеживать игральную карту и начинает отслеживать белый фон, и поэтому у вас есть «дрейф» ... другими словами, ваша функция TplMatch() всегда будет вернуть действительное изображение result, и ваша текущая реализация minmax() всегда будет возвращать действительный минимум.

person Alessandro Jacopson    schedule 08.12.2013
comment
Спасибо, что уделили мне время и направили меня... это работает хорошо. Каким-то образом возникает некоторый дрейф, но я бы попытался разобраться с этим. Я знаю, что этот дрейф присущ частому обновлению шаблона... Что означает мелкая и глубокая копия для класса OpenCV Mat? - person learner; 09.12.2013
comment
@learner stackoverflow.com/search?q=shallow+deep+opencv Цитата из stackoverflow.com/a/18154264/15485 Класс OpenCV Mat - это просто заголовок для фактических данных изображения, на которые он содержит указатель. Оператор = копирует указатель, так что оба мата используют одни и те же данные. Это означает, что изменение данных в одном мате также изменяет их в другом. Это называется поверхностной копией, поскольку копируется только верхний слой (заголовок), а не нижний слой (данные). Чтобы сделать копию базовых данных (глубокую копию), используйте метод clone(). - person Alessandro Jacopson; 09.12.2013
comment
@learner Что ты имеешь в виду под дрифтом? Это видно на видео, которое я выложил? - person Alessandro Jacopson; 09.12.2013
comment
Еще раз спасибо дружище! И, да, глядя на первые 15-16 секунд видео, нарисованная на глазу рамка как будто движется ко лбу. Кроме того, я где-то читал, что этот дрейф является неотъемлемой проблемой частого обновления шаблона. Хотя причину я так и не нашел. Я думал, что это должно быть более точным, поскольку мы обновляем шаблон в каждом кадре, таким образом преодолевая динамические изменения внешнего вида. Что ты говоришь?? - person learner; 10.12.2013
comment
@learner Я обновил ответ своим объяснением дрейфа. - person Alessandro Jacopson; 10.12.2013
comment
Я хочу выбрать ROI из любого кадра, а не только из первого кадра. Как мне это сделать. Я застрял в условии для этого. - person Tariq; 06.03.2015
comment
@AlessandroJacopson: не могли бы вы дать несколько предложений по этому - person Mehul Joisar; 16.03.2015

Вы можете следовать руководству по OpenCV "Сопоставление шаблонов". Ваша функция 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 и некоторую логику для замедления видео до тех пор, пока не выберу область интереса, а затем воспроизведу видео с нормальной скоростью.

#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;
}
person Alessandro Jacopson    schedule 24.11.2013
comment
Я попробовал функцию matchTemplate() с мерой CV_TM_SQDIFF_NORMED. Но я не получаю желаемого отслеживания... - person learner; 25.11.2013
comment
@learner Я не понимаю, что ты имеешь в виду под отслеживанием? - person Alessandro Jacopson; 25.11.2013
comment
когда я попробовал функцию matchTemplate(), результатом было просто окно (размером с прямоугольник, который я перетаскиваю мышью), и оно показывает канал веб-камеры, соответствующий этому региону... это сложно объяснить так... мог бы вы, пожалуйста, предложите пример кода, как двигаться дальше? - person learner; 25.11.2013
comment
@learner, как я написал в ответе, это руководство содержит пример кода: docs.opencv.org/doc/tutorials/imgproc/histograms/ - person Alessandro Jacopson; 25.11.2013
comment
Я уже пробовал это. Я получаю изображение и шаблон. Все, что мне нужно, это отследить его ... просто застрять там :( - person learner; 25.11.2013
comment
@learner, пожалуйста, посмотрите ответ, я обновил его, чтобы показать, что я имею в виду под отслеживанием. - person Alessandro Jacopson; 25.11.2013
comment
ох! Я не заметил ответа! простите! пытаясь понять это... спасибо! - person learner; 25.11.2013
comment
эй, я пытался обновлять свой шаблон с каждым кадром. Я отредактировал приведенный выше код (я пытался выделить его жирным шрифтом, но этого не произошло). Итак, изменения внесены сразу после части if(select_flag) и перед cout‹‹matchLoc‹‹endl; это не обновление, как я думал... matchLoc значение всегда остается одним и тем же. Почему так происходит?? Что не так? Спасибо за ваше время... - person learner; 03.12.2013
comment
@learner Здравствуйте, я не видел вашего редактирования моего кода (ревизии моих ответов здесь stackoverflow.com/posts/20180256/revisions ) - person Alessandro Jacopson; 03.12.2013
comment
Эй, я обновил свой вопрос с изменениями. Пожалуйста, посмотрите на это. Я не могу понять, где я ошибаюсь... это должно работать, потому что я каждый раз обновляю шаблон. Большое спасибо! - person learner; 05.12.2013

Вы также можете получить общее представление о предмете, начиная с этой страницы Википедии http://en.wikipedia.org/wiki/Video_tracking

person Alessandro Jacopson    schedule 18.03.2014