您的位置:首页 > 运维架构

OpenCV 2 学习笔记(26): 自选区域进行模板匹配

2013-12-17 14:21 716 查看
          在定位中通常需要根据目标区域特征在图像中找出其正确的位置。使用比较多的方法还是模板匹配,模板匹配是根据区域的灰度值进行匹配,将结果存储在矩阵中,然后根据模板匹配所使用方法的不同寻找矩阵中最大值或者最小值就是最佳匹配。在opencv中使用cv::matchTemplate(src,
temp, result, CV_TM_CCORR_NORMED);算子进行匹配。具体相关的解释见:http://blog.csdn.net/yang_xian521/article/details/6942194。

         模板匹配中第一步就需要寻找模板,一般情况下先需要自己手动寻找感兴趣区域进行寻找。可以使用opencv的highgui头文件进行可视化选取,然后再在图像中进行匹配。

但是模板匹配有明显的缺陷。其一鲁棒性较差,对光线或者灰度值比较敏感,所以才出来了许多归一化的模板匹配算法,用来减小光照对模板匹配算法的影响,在前面几节所讲解的直方图反向投影中也可以进行灰度匹配,但是其计算时间比较长。其次,不支持旋转与缩放匹配。这一节首先解决前面的问题,即自选取区域,然后模板匹配。首先看一下opencv中怎样根据鼠标动作自定义区域。

首先鼠标有三个动作,点击、滑动、松开。程序设计为当右键点击并且滑动时选定区域。鼠标监听响应函数为cv::SetMouseCallback().

C++: void setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata=0 )

winname – 监听鼠标的窗口,这要求需要使用cv::namedWindow声明一个窗口。
onMouse – 鼠标操作响应函数.可以看例子http://code.opencv.org/projects/opencv/repository/revisions/master/entry/samples/cpp/ffilldemo.
cpp
userdata – 传递给响应函数的参数.

响应函数应该为一下形式

void onMouse(int Event,int x,int y,int flags,void* param ) 

Event
– 事件判定参数,有以下几种宏定义

#define
CV_EVENT_MOUSEMOVE 0              鼠标点击
#define CV_EVENT_LBUTTONDOWN 1          左击
#define CV_EVENT_RBUTTONDOWN 2          右击
#define CV_EVENT_MBUTTONDOWN 3          滚轮点击
#define CV_EVENT_LBUTTONUP 4                 松开左键
#define CV_EVENT_RBUTTONUP 5                 松开右键
#define CV_EVENT_MBUTTONUP 6                 松开中键
#define CV_EVENT_LBUTTONDBLCLK 7       双击左键
#define CV_EVENT_RBUTTONDBLCLK 8       双击右键
#define CV_EVENT_MBUTTONDBLCLK 9       双击中键

x,y--
光标键的坐标

flags
-- 代表拖拽事件,有一下几种宏定义

#define
CV_EVENT_FLAG_LBUTTON 1         左键滑动
#define CV_EVENT_FLAG_RBUTTON 2         右键拖动
#define CV_EVENT_FLAG_MBUTTON 4         中键拖动
#define CV_EVENT_FLAG_CTRLKEY 8     (8~15)按Ctrl不放
#define CV_EVENT_FLAG_SHIFTKEY 16   (16~31)按Shift不放
#define CV_EVENT_FLAG_ALTKEY 32       (32~39)按Alt不放

param
-- 鼠标事件的名称

自选取区域参考:http://blog.csdn.net/glb562000520/article/details/8941810

#include <opencv2/opencv.hpp>
#include<opencv2\core\core.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\highgui\highgui.hpp>
using namespace cv;

//原始图像
cv::Mat srcImg;
//临时图像
cv::Mat tempImg;
//截取图像
cv::Mat partImg;
//原始图像,固定不变
cv::Mat realImg;

//点击鼠标触发事件
void on_mouse( int event, int x, int y, int flags, void* ustc)
{
//刚开始点击鼠标原始坐标
static Point pre_pt = (-1,-1);
//点击鼠标时坐标
static Point cur_pt = (-1,-1);
//右上角坐标
static Point cur_pt1 = (-1,-1);
//左下角坐标
static Point cur_pt2 = (-1,-1);
//定义字体并初始化
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);
//字体最大长度
char temp[100];
//获得当前图像的灰度值
Vec3b intensity = srcImg.at<Vec3b>(Point(x, y));
//点击右键事件
if( event == CV_EVENT_RBUTTONDOWN )
{
tempImg.copyTo(srcImg);
sprintf(temp,"(%d,%d,%d,%d,%d)",x,y,intensity.val[0],intensity.val[1],intensity.val[2]);
pre_pt = cvPoint(x,y);
putText(srcImg,temp, pre_pt, FONT_HERSHEY_SIMPLEX, 0.5,cvScalar(0,0, 0, 255),1,8);
circle( srcImg, pre_pt, 3,cvScalar(255,0,0,0) ,CV_FILLED, CV_AA, 0 );
imshow( "srcImg", srcImg );
srcImg.copyTo(tempImg);
}
//点击右键滑动鼠标事件
else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_RBUTTON))
{
tempImg.copyTo(srcImg);
sprintf(temp,"(%d,%d,%d,%d,%d)",x,y,intensity.val[0],intensity.val[1],intensity.val[2]);
cur_pt = cvPoint(x,y);
cur_pt1 = cvPoint(x,pre_pt.y);
cur_pt2 = cvPoint(pre_pt.x,y);
putText(srcImg,temp, cur_pt,FONT_HERSHEY_SIMPLEX, 0.5,cvScalar(0,0, 0, 255),1,8);
//rectangle(src, pre_pt, cur_pt, cvScalar(0,255,0,0), 1, 8, 0 );
line(srcImg, pre_pt, cur_pt1, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, pre_pt, cur_pt2, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, cur_pt1, cur_pt, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, cur_pt2, cur_pt, cvScalar(0,255,0,0), 1, CV_AA, 0 );
imshow( "srcImg", srcImg );
}
//松开右键事件
else if( event == CV_EVENT_RBUTTONUP )
{
tempImg.copyTo(srcImg);
sprintf(temp,"(%d,%d,%d,%d,%d)",x,y,intensity.val[0],intensity.val[1],intensity.val[2]);
cur_pt = cvPoint(x,y);
cur_pt1 = cvPoint(x,pre_pt.y);
cur_pt2 = cvPoint(pre_pt.x,y);
putText(srcImg,temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5,cvScalar(0,0, 0, 255),1,8);
circle( srcImg, cur_pt, 3,cvScalar(255,0,0,0) ,CV_FILLED, CV_AA, 0 );
//rectangle( src, pre_pt, cur_pt, cvScalar(0,255,0,0), 1, 8, 0 );
line(srcImg, pre_pt, cur_pt1, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, pre_pt, cur_pt2, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, cur_pt1, cur_pt, cvScalar(0,255,0,0), 1, CV_AA, 0 );
line(srcImg, cur_pt2, cur_pt, cvScalar(0,255,0,0), 1, CV_AA, 0 );
imshow( "srcImg", srcImg );
srcImg.copyTo(tempImg);
int width=abs(pre_pt.x-cur_pt.x);
int height=abs(pre_pt.y-cur_pt.y);
Rect rect(pre_pt.x, pre_pt.y, width, height);
partImg = realImg(rect);
imwrite("partImg.jpg", partImg);//把程序中的Mat类型的矩阵保存为图像到指定位置。
imshow("partImg", partImg);
}
}

int main()
{
srcImg = imread("Lena.jpg");
srcImg.copyTo(tempImg);
srcImg.copyTo(realImg);
cvNamedWindow("srcImg",1);
cvSetMouseCallback( "srcImg", on_mouse, 0 );
imshow("srcImg",srcImg);
cvWaitKey(0);
cvDestroyAllWindows();
return 0;
}


然后根据截取的模板图像进行模板匹配:
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv.hpp>
using namespace std;

int main()
{
cv::Mat src; //原始图像
cv::Mat temp; //模板图像
cv::Mat result; //结果矩阵

//图像或者矩阵的长和宽
int imgWidth, imgHeight;
int tempWidth, tempHeight;
int resultWidth, resultHeight;

//储存图像最小值和最大值,以及位置
double minVal, maxVal;
cv::Point minLoc, maxLoc;

//读取图像
src = cv::imread("Lena.jpg");
temp =cv::imread("partImg.jpg");

//判断是否正确读取图像
if(!src.data ||!temp.data)
{
cout<<"没有读取到图像"<<endl;
return -1;
}
//计算出图像的长和宽
imgWidth = src.cols;
imgHeight = src.rows;
tempWidth = temp.cols;
tempHeight = temp.rows;
//计算出结果矩阵的长和宽
resultWidth = imgWidth - tempWidth -1;
resultHeight = imgHeight - tempHeight -1;
//创建结果矩阵
result.create(cv::Size(resultWidth,resultHeight),CV_8U);
//使用归一化模板匹配方法CV_TM_CCORR_NORMED归一化相关匹配法,结果矩阵中值最大为最佳配位位置
cv::matchTemplate(src, temp, result, CV_TM_CCORR_NORMED);
//找出最小值点
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc,cv::Mat());
//在原图中画出匹配矩形框
cv::rectangle(src, cv::Rect(maxLoc.x, maxLoc.y, tempWidth, tempHeight),cv::Scalar(0,0,0));
cv::imshow("Result",src);
cv::waitKey(0);
return 0;
}

其中左边是原始图像与截取的模板图像,右边是结果图像,可以看出第二张图片模板匹配位置与第一张模板取得位置相同。如果在同一张图片里面匹配还不相同的话就完了!(在图像中写入字符可以参考http://blog.csdn.net/carson2005/article/details/7206037)


                       
                                
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  OpenCV VC++ 图像处理