@[OpenCV|C++]
在图像处理时,如果我们需要实现实时的改变值,并重新开始程序,就需要我们自己实现回调函数,其中,对于鼠标事件的回调,需要我们重写鼠标回调函数void onMouse(int event, int x, int y, int flags, void* ustc); //鼠标回调函数
函数中的主要参数:
- event对应于鼠标事件
- x,y鼠标的位置参数
- flags标志位
- ustc(不常用)
其中,int型event事件分别有:
- EVENT_MOUSEMOVE 滑动
- EVENT_LBUTTONDOWN 左击
- EVENT_RBUTTONDOWN 右击
- EVENT_MBUTTONDOWN 中键点击
- EVENT_LBUTTONUP 左键放开
- EVENT_RBUTTONUP 右键放开
- EVENT_MBUTTONUP 中键放开
- EVENT_LBUTTONDBLCLK 左键双击
- EVENT_RBUTTONDBLCLK 右键双击
- EVENT_MBUTTONDBLCLK 中键双击
重写回调函数后,我们只要使用setMouseCallback
函数打开调用即可使用
函数原型:void minMaxLoc(InputArray src,double * minVal=0,double* maxVal=0,Point* minLoc=0,Point* maxLoc=0,InputArray mask=noArray())
参数说明:
- InputArray型的src,是单通道的输入阵列
- double * 型的minVal,返回最小值的指针,无返回则是NULL
- double * 型的maxVal,返回最大值的指针,无返回则是NULL
- Point * 型的minLoc,返回最小值的指针(二维),无返回则是NULL
- Point * 型的miaxLoc,返回最大值的指针(二维),无返回则是NULL
- InputArray型的mask,选择可选掩膜
模板匹配是从一张图片里找到与另一模板图像最匹配的部分
函数原型:void matchTemplate(InputArray image,InputArray templ,OutputArray result,int method)
参数说明:
- 第一个参数,是待搜索的图片,为8位或32位浮点图
- 第二个参数,是带匹配的图片,格式应与源图相同
- 第三个参数,输出结果图片,单通道,32位浮点型
- 第四个参数,匹配方式,共六种
六种匹配方式及参数:
- 平方差匹配 method=TM_SQDIFF
- 归一化平方差匹配 method=TM_SQDIFF_NORMED
- 相关匹配 method=TM_CCORR
- 归一化相关匹配method=TM_CCORR_NORMED
- 系数匹配 method=TM_CCOEFF
- 化相关系数匹配method=TM_CCOEFF_NORMED
注意:
对于1和2两种匹配方式,最小值为最好匹配,其余的最大值为最好匹配
首先读取视频,并且提取帧图像,使用回调函数选中模板图像, 开始在下一帧中匹配,每次匹配完,都截取相应位置的图,作为下一次匹配的模板图,并且用框框框选,输出到输出文件中
对于matchTemplate函数中result大小的解释:
比如源图尺寸为W*H,待匹配图为w*h,那么输出结果大小就是(W-w+1)(H-h+1),原因是匹配是拿着小图在大图上移动来计算值的,所以移动的范围就是(W-w+1)(H-h+1),计算的结果就存储在这个结果矩阵中
#include
#include
#include
#include
using namespace cv;
using namespace std;
Mat image; //当前帧图像
Mat imageCopy; //用于拷贝的当前帧图像
Mat rectImage; //子图像
bool leftButtonDownFlag = false; //左键单击后视频暂停标志位
Point beginPoint; //矩形框起点
Point endPoint; //矩形框终点
int resultRows; //模板匹配result的行
int resultcols; //模板匹配result的列
Mat ImageResult; //模板匹配result
double minValue; //模板匹配result最小值
double maxValude; //模板匹配result最大值
Point minPoint; //模板匹配result最小值位置
Point maxPoint; //模板匹配result最大值位置
int frameCount = 0; //帧数统计
void onMouse(int event, int x, int y, int flags, void* ustc); //鼠标回调函数
void Tips() {
cout << "请选择要使用的匹配规则:" << endl;
cout << " 1.平方差匹配" << endl;
cout << " 2.归一化平方差匹配" << endl;
cout << " 3.相关匹配" << endl;
cout << " 4.归一化相关匹配" << endl;
cout << " 5.系数匹配" << endl;
cout << " 6.化相关系数匹配" << endl;
cout << " 0.退出" << endl;
}
int main(int argc, char*argv[])
{
VideoCapture capture("C:\\Users\\14527\\Desktop\\Video\\cut.AVI");
int capture_fps = capture.get(CV_CAP_PROP_FPS); //获取视频帧率
int capture_count = capture.get(CV_CAP_PROP_FRAME_COUNT);
int capture_width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
int capture_height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
cout << "视频帧率:" << capture_fps<cout << "视频帧数:" << capture_count << endl;
cout << "视频宽度:" << capture_width << endl;
cout << "视频高度:" << capture_height << endl;
int pauseTime = 1000 / capture_fps; //两幅画面中间间隔
VideoWriter writer("C:\\Users\\14527\\Desktop\\Video\\out.avi", CV_FOURCC('X', 'V', 'I', 'D'), capture_fps, Size(capture_width, capture_height));
Tips();
int choice = 0;
cin >> choice;
namedWindow("Video");
setMouseCallback("Video", onMouse); //设置鼠标回调函数
while (choice)
{
if (!leftButtonDownFlag) //鼠标左键按下绘制矩形时,视频暂停播放
{
capture >> image;
frameCount++; //帧数
}
if (!image.data || waitKey(pauseTime + 30) == 27) //图像为空或Esc键按下退出播放
{
break;
}
if (rectImage.data)
{
ImageResult = Mat::zeros(resultRows, resultcols, CV_32FC1);//建立结果矩阵,注意是单通道的32位浮点型
switch (choice) { //根据一开始的选择使用对应的匹配模式
case 1:
matchTemplate(image, rectImage, ImageResult, TM_SQDIFF);
break;
case 2:
matchTemplate(image, rectImage, ImageResult, TM_SQDIFF_NORMED);
break;
case 3:
matchTemplate(image, rectImage, ImageResult, TM_CCORR);
break;
case 4:
matchTemplate(image, rectImage, ImageResult, TM_CCORR_NORMED);
break;
case 5:
matchTemplate(image, rectImage, ImageResult, TM_CCOEFF);
break;
case 6:
matchTemplate(image, rectImage, ImageResult, TM_CCOEFF_NORMED);
break;
}
minMaxLoc(ImageResult, &minValue, &maxValude, &minPoint, &maxPoint, Mat()); //最小值最大值获取
Point point;
switch (choice) {//为了统一处理,所以先把Point取出来
case 1:
point = minPoint;
break;
case 2:
point = minPoint;
break;
case 3:
point = maxPoint;
break;
case 4:
point = maxPoint;
break;
case 5:
point = maxPoint;
break;
case 6:
point = maxPoint;
break;
}
rectangle(image, point, Point(point.x + rectImage.cols, point.y + rectImage.rows), Scalar(0, 0, 255), 2); //绘制
//更新当前模板匹配的模板
Mat resultImage = image(Rect(point, Point(point.x + rectImage.cols, point.y + rectImage.rows)));
rectImage = resultImage.clone();
//当前帧数输出到视频流
writer << image;
}
imshow("Video", image);
}
return 0;
}
//鼠标回调函数
void onMouse(int event, int x, int y, int flags, void *ustc)
{
if (event == CV_EVENT_LBUTTONDOWN) //检测到左键按下时
{
leftButtonDownFlag = true; //标志位为true,也就是停止读取下一帧图像
beginPoint = Point(x, y); //设置左键按下点的矩形起点
endPoint = beginPoint;
}
if (event == CV_EVENT_MOUSEMOVE && leftButtonDownFlag)
{ //当鼠标移动且之前左键有按下的话
imageCopy = image.clone();
endPoint = Point(x, y);
if (beginPoint != endPoint)
{
//在复制的图像上绘制矩形
rectangle(imageCopy, beginPoint, endPoint, Scalar(0, 0, 255), 2);
}
//imshow("Video", imageCopy);
}
if (event == CV_EVENT_LBUTTONUP) //左键放开时,开始匹配
{
leftButtonDownFlag = false;
Mat subImage = image(Rect(beginPoint, endPoint)); //截取图像
rectImage = subImage.clone(); //给全局的待匹配图像
resultRows = image.rows - rectImage.rows + 1; //输出结果图像的行数及列数
resultcols = image.cols - rectImage.rows + 1;
//imshow("Sub Image", rectImage);
}
}
大家自己去跑一下代码就好啦,或者可以私聊联系我要结果的文件