テンプレートを使用してリアルタイム追跡を実装しようとしています。フレームごとにテンプレートを更新したいと思います。私が行った主な変更は次のとおりです。
1)テンプレートマッチングとminmaxLocを別々のモジュール、つまりTplMatch()関数とminmax()関数にそれぞれ分離しました。
2)track()関数内では、select_flagが常にtrueに保たれるため、反復ごとに新しいテンプレートが「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)を更新しているため、なぜこれが発生しているのか理解できません。 minmax()関数からのmatch値は、毎回同じポイント(座標)を返します。テストビデオは次の場所で入手できます: http://www.youtube.com/watch?v=vpnkk7N2E0Q&feature=youtu.be 調べて、先に進んでください...どうもありがとうございました!
私はあなたの質問のこのリビジョンからあなたの元のコードを取得します: https://stackoverflow.com/revisions/20180073/
元のコードに最小限の変更を加えました。結果のコードは次のとおりです。
_#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 のビデオは、上記のプログラムのテストを示しています。
グローバル変数は問題がどこにあるのかを理解するのに役立たないと思うので、グローバル変数の使用は避けたいと思います。さらに、 1 '' が彼の answer に書いたように、OpenCVのMat
クラスの浅いコピーと深いコピーにも注意を払います。
OpenCVの
Mat
クラスは、実際の画像データの単なるヘッダーであり、そこへのポインターが含まれています。 _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()
の現在の実装は常に有効な最小値を返します。
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
関数といくつかのロジックを追加して、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;
}
このウィキペディアのページから始めて、主題の一般的な紹介をすることもできます http://en.wikipedia.org/wiki/Video_tracking