opencv笔记(3)——模板匹配实现目标识别与跟踪

1 知识补充

1.1 回调函数

在图像处理时,如果我们需要实现实时的改变值,并重新开始程序,就需要我们自己实现回调函数,其中,对于鼠标事件的回调,需要我们重写鼠标回调函数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函数打开调用即可使用

minMaxLoc()函数找最大最小值
函数原型: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,选择可选掩膜

模板匹配函数matchTemplate()
模板匹配是从一张图片里找到与另一模板图像最匹配的部分
函数原型: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大小的解释:
比如源图尺寸为WH,待匹配图为wh,那么输出结果大小就是(W-w+1)(H-h+1),原因是匹配是拿着小图在大图上移动来计算值的,所以移动的范围就是(W-w+1)(H-h+1),计算的结果就存储在这个结果矩阵中

总体代码及注释

#include <core/core.hpp>    
#include <highgui/highgui.hpp>    
#include <imgproc/imgproc.hpp>    
#include<iostream>    
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<<endl;
    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);
    }
}

猜你喜欢

转载自blog.csdn.net/xu1129005165/article/details/84785199