opencv_模板匹配

项目描述

最近在看opencv相关的知识,看到了模板匹配,记录下来方便日后使用
简单的模板匹配(单模板)
多模板匹配
带角度的模板匹配

创作不易,点个关注呗 ^ _ ^

函数介绍

头文件:
#include
#include

matchTemplate

模板匹配的主要函数 匹配并不是基于直方图的,
参数1:待匹配图像
参数2:模板,要求尺寸小于原图像
参数3:比较结果的映射图像
参数4:图像匹配方式 共6种 计算量不同,匹配精度也不同。

  • 平方差匹配(TM_SQDIFF):最好匹配为0,匹配越差值越大
  • 归一化平方差匹配(TM_SQDIFF_NORMED)
  • 相关匹配(TM_CCORR) 0最坏 值越大匹配越好
  • 归一化相关匹配(TM_CCORR_NORMED)
  • 系数匹配(TM_CCOEFF) 匹配相关性 1完美 -1糟糕 0无相关性
  • 化相关系数匹配(TM_CCOEFF_NORMED)

normalize

函数作用:
归一化数据。该函数分为范围归一化与数据值归一化。
其实范围归一化和数值归一化可以归为一类,一般来说数值归一化是指将数值归一到[0,1]区间上,
而范围归一化则指将数值归一到[a,b]上,a,b为任意值。由此看出,数值归一化是范围归一化的特例,包含在范围归一化中。
参数说明:

  • src 输入数组;
  • dst 输出数组,数组的大小和原数组一致;
  • alpha 用来规范值或者规范范围,并且是下限;
  • beta 只用来规范范围并且是上限,因此只在NORM_MINMAX中起作用;
  • norm_type 归一化选择的数学公式类型;
  • dtype 当为负,输出在大小深度通道数都等于输入,当为正,输出只在深度与输如不同,不同 的地方由dtype决定;
  • mark 掩码。选择感兴趣区域,选定后只能对该区域进行操作。

minMaxLoc

从一个矩阵中找出全局的最大值和最小值。

  • 参数1 src 输入单通道矩阵.
  • 参数2 minVal 返回最小值的指针; 如果不需要输入NULL.
  • 参数3 maxVal 返回最大值的指针; 如果不需要输入NULL.
  • 参数4 minLoc 返回最小值位置的指针 (二维情况下); 如果不需要输入NULL.
  • 参数5 maxLoc 返回最大值位置的指针 (二维情况下); 如果不需要输入NULL.
  • 参数6 mask 可选参数,用于选择一个子矩阵.

rectangle

  • 参数 img 输入图像。
  • 参数 pt1 矩形的顶点。
  • 参数 pt2 对应pt1的矩形的顶点。
  • 参数color 矩形颜色或亮度(灰度图像)。
  • 参数thickness 构成矩形的线条的厚度。负值,比如FILLED,意味着函数必须绘制一个填充矩形。
  • 参数lineType 线的类型. 参见 LineTypes
  • 参数shift 点坐标中的小数位数

测试代码运行时间

 QTime t = QTime::currentTime();
 GetOneMinLoc(g_srcImage, g_tempalteImage, p, TM_SQDIFF_NORMED);
 int elapse = t.msecsTo(QTime::currentTime());

代码示例

读取图片

    QString filename = QFileDialog::getOpenFileName(this, QString("读取最后结果"),
                       QString("./file"),
                       QString("File(*.*)"));
    g_srcImage = imread(filename.toStdString().c_str());
    if (!g_srcImage.data) {
        qDebug() << "原始图读取失败";
        return ;
    }
    filename = QFileDialog::getOpenFileName(this, QString("读取最后结果"),
                                            QString("./file"),
                                            QString("File(*.*)"));
    g_tempalteImage = imread(filename.toStdString().c_str());
    if (!g_tempalteImage.data) {
        qDebug() << "模板图读取失败";
        return ;
    }

单模板匹配

调用:
{
    Point p;
    GetOneMinLoc(g_srcImage, g_tempalteImage, p, TM_SQDIFF_NORMED);
    rectangle(g_srcImage, Rect(p.x, p.y, g_tempalteImage.cols, g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8);
    imshow("src Window", g_srcImage);
    waitKey(0);
}
/* GetOneMinLoc
 * 函数说明:
 * 进行单一化模板匹配
 * 参数1:待匹配图片
 * 参数2:模板图片
 * 参数3:匹配的位置
 * 参数4:匹配类型
*/
double GetOneMinLoc(Mat image, Mat tepl, Point& point, TemplateMatchModes method) {
    int result_cols =  image.cols - tepl.cols + 1;
    int result_rows = image.rows - tepl.rows + 1;
    Mat result = Mat( result_cols, result_rows, CV_32FC1 );
    matchTemplate(image, tepl, result, method );
    double minVal, maxVal, Value;
    Point minLoc, maxLoc;
    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
    if(method == TM_SQDIFF || method == TM_SQDIFF_NORMED) {
        point = minLoc;
        Value =  minVal;
    } else {
        point = maxLoc;
        Value =  maxVal;
    }
    rectangle(result, Rect(point.x, point.y, tepl.cols, tepl.rows), Scalar(0, 0, 255), 2, 8);
    imshow("resault Window", result);
    return Value;
}

多模板匹配

调用
{
    vector<Point> P;//存储所有检测目标的坐标
    GetAllMinLoc(g_srcImage, g_tempalteImage, 0.18, Scalar(0, 0, 0), &P);
    //根据获取的全部坐标数据圈出待检测目标
    qDebug() << "rect" << P.size();
    for (int k = 0; k < P.size(); k++) {
        Point loc = P[k];
        rectangle(g_srcImage, Rect(loc.x, loc.y, g_tempalteImage.cols, g_tempalteImage.rows), Scalar(0, 0, 255), 2, 8);
}
/*
/* GetAllMinLoc
 * 参数说明:
 * image:输入原图像
 * templ:输入的模板图像
 * sim:   相似度,0~1之间,越接近0越相似
 * mask: 覆盖区颜色,一般情况下直接取黑色即可Scalar(0,0,0)
 * *all_min_loc:所有匹配到的目标的坐标
 */
void GetAllMinLoc(Mat image, Mat templ, double sim, Scalar mask, vector<Point>* all_min_loc) {
    int src_Width = image.cols, src_Height = image.rows;
    int templ_Width = templ.cols, templ_Height = templ.rows;
    double min_value, max_value;
    Point min_Loc, max_Loc;
    Mat img_result;
    Mat img_clon = image.clone();
    if (templ_Width > src_Width || templ_Height > src_Height) {
        qDebug("模板尺寸大于原图像,请选择正确的模板\n");
    }
    while (1) {
        matchTemplate(img_clon, templ, img_result, TM_SQDIFF_NORMED);
        minMaxLoc(img_result, &min_value, &max_value, &min_Loc, &max_Loc);
        if (min_value < sim) {
        // 预期内
            all_min_loc->push_back(min_Loc);
            // 去掉已检测部分
            rectangle(img_clon, Rect(min_Loc.x, min_Loc.y, templ_Width, templ_Height), mask, -1);
        } else {
            qDebug() << "超出预期的匹配程度";
            break;
        }
    }
}
// 代码优化:
void GetAllMinLoc(Mat image, Mat templ, double sim, Scalar mask, vector<Point>* all_min_loc) {
    Point p;
    Mat srcImg = image.clone();
    while(1) {
        if(GetOneMinLoc(srcImg, templ, p, TM_SQDIFF_NORMED) < sim) {
            all_min_loc->push_back(p);
            rectangle(srcImg, Rect(p.x, p.y, templ.cols, templ.rows), mask, -1);//掩盖检测到的第一块区域
        } else {
            break;
        }
    }
}

识别优化:使用灰度图像进行识别代码运行速度会加快很多

进阶

在模板匹配中通常没有完全一样的图片,这样就需要匹配大小不同的图像。
这样的匹配有以下两种方式:

  1. 改变模板图像的比例来适配
  2. 准备不同比例的模板来匹配
  3. 修改原来图像的比例来适配模板
    下面将对这些进行尝试和分析。

代码流程:

  1. 载入图像与模板;
  2. 将模板图像等比例放大或缩小
  3. 将不同的比例和图像适配
  4. 得到匹配区域的图片
  5. 将得到的ROI图片与原始模板进行相似性比较
  6. 筛选出相似性最好的ROI区域
  7. 在待测图片上进行框选
  8. 输出图片

你可能感兴趣的:(opencv,qt,opencv,人工智能,计算机视觉)