最近《旅行青蛙》这款收集类手游十分火爆,大家在手机中养着自己的青蛙“儿子”“女儿”,看着它们去旅行,寄回许多照片,带回各种特产、朋友,这让博主萌生了写这篇博文的想法:

这是一个非常好的探索模板匹配的机会!

现象级的戳中玩家内心的手游

为什么这么说呢?因为《旅行青蛙》中的照片除了少数是整张手绘,大部分是由背景、青蛙形态以及青蛙朋友的形态贴图合成的。从数字图像的角度上看,那些照片都是500*350*3的立体矩阵,而青蛙是糊上去的一团料。这也就意味着以青蛙形态贴图为模板,可以找到寄回的照片中“蛙儿子”的位置,顺便学习OpenCV。

想从寄回的照片中找到“蛙儿子”的位置?你也许需要使用OpenCV3中的模板匹配功能。模板匹配是图像识别中比较常用的一种方法,能从待识别图像中找出与模板图像最相似的部分,相比于经典的特征提取、当下火热的深度学习等方法,模板匹配简单、效率较高,但模板匹配也在旋转不变性、尺度不变性上有所欠缺。因此模板匹配一般用于对与模板完全相同的对象进行识别,并作为一种目标跟踪(Tracking)的方法,与目标识别相结合提高运行效率、识别准确度。博主未来将对基于模板匹配的目标跟踪方法专门写一篇博文做以讨论。

OpenCV3中提供了matchTemplate函数minMaxLoc函数共同实现模板匹配的功能。
      根据OpenCV3.3.1的官方文档中所述:
      1、matchTemplate 函数:将模板与匹配图像区域进行比较,函数将遍历匹配图像,根据设定的运算方法对匹配图像矩阵进行运算。

官方定义为

void cv::matchTemplate ( InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
)

其中image为输入的匹配图像(待识别图像),templ为输入的模板图像,result为输出的处理图像,method为设定的运算方法,mask为搜索模板的掩模(仅限TM_SQDIFF与TM_CCORR_NORMED使用,默认为空数组)。设定的运算方法分为六种:TM_SQDIFF、TM_CCORR或TM_CCOEFF及它们的归一化衍生型TM_SQDIFF_NORMED、TM_CCORR_NORMED与TM_CCOEFF_NORMED,在OpenCV中对应六个int型数字,使用时用英文名即可。六种方法的解释如下:

(1)cv::TM_SQDIFF:该方法使用平方差进行匹配,因此最佳的匹配结果在结果为0处,值越大匹配结果越差。

(2)cv::TM_SQDIFF_NORMED:该方法使用归一化的平方差进行匹配,最佳匹配也在结果为0处。
    (3)cv::TM_CCORR:相关性匹配方法,该方法使用源图像与模板图像的卷积结果进行匹配,因此,最佳匹配位置在值最大处,值越小匹配结果越差。
    (4)cv::TM_CCORR_NORMED:归一化的相关性匹配方法,与相关性匹配方法类似,最佳匹配位置也是在值最大处。
    (5)cv::TM_CCOEFF:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配,最佳匹配结果在值等于1处,最差匹配结果在值等于-1处,值等于0直接表示二者不相关。
    (6)cv::TM_CCOEFF_NORMED:归一化的相关性系数匹配方法,正值表示匹配的结果较好,负值则表示匹配的效果较差,也是值越大,匹配效果也好。

具体的函数数学形式与解释可见liyuanbhu博主
      OpenCV 学习笔记(模板匹配)

2、minMaxLoc 函数:在数组(或矩阵)中寻找全局最大值与最小值,并给出它们的位置。

官方定义为

void cv::minMaxLoc ( InputArray src,
double * minVal,
double * maxVal = 0,
Point * minLoc = 0,
Point * maxLoc = 0,
InputArray mask = noArray()
)

因为模板匹配的最优结果常在matchTemplate函数的全局最小值(设定方法为TM_SQDIFF时),或全局最大值(设定方法为TM_CCORR或TM_CCOEFF时)处,将matchTemplate函数输出的处理图像作为minMaxLoc的输入即可找到和模板最相似的目标的位置。

此处要注意两点:

(1)当matchTemplate函数中设定方法为TM_SQDIFF时,目标的中心点在Point(minLoc.x + image_template.cols/2, minLoc.y + image_template.rows/2);当设定方法为TM_CCORR或TM_CCOEFF时,目标的中心点在Point(maxLoc.x + image_template.cols/2, maxLoc.y + image_template.rows/2)。

(2)matchTemple中输入的待测图像、模板图像既可是彩色图像,也可是灰度图像。模板匹配并不会单纯把彩色图像转换为灰度图像,而是会对彩色图像的各个通道进行独立平均运算后再对整体进行处理,最终仍是处理成一个单通道的图像。因此将彩色图像转换成灰度图像后再进行模板匹配,和直接输入彩色图像进行模板匹配是不同的。

原理都叙述完了,让我们来看看OpenCV3实现模板匹配的简单实例:

待测图片(匹配图片)为:

1.jpg

模板图片为:

Template1.jpg

本文中的代码都基于OpenCV3.3.1版,使用OpenCV2的小伙伴可能需要微调程序。代码如下:

#include <iostream>
#include <stdio.h>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main()
{//加载源图像和模板图像Mat image_source = imread("1.jpg");Mat image_template = imread("Template1.jpg");Mat image_matched;double minVal, maxVal;Point minLoc, maxLoc;//模板匹配matchTemplate(image_source, image_template, image_matched, TM_CCOEFF_NORMED);/*1、cv::TM_SQDIFF:该方法使用平方差进行匹配,因此最佳的匹配结果在结果为0处,值越大匹配结果越差。2、cv::TM_SQDIFF_NORMED:该方法使用归一化的平方差进行匹配,最佳匹配也在结果为0处。3、cv::TM_CCORR:相关性匹配方法,该方法使用源图像与模板图像的卷积结果进行匹配,因此,最佳匹配位置在值最大处,值越小匹配结果越差。4、cv::TM_CCORR_NORMED:归一化的相关性匹配方法,与相关性匹配方法类似,最佳匹配位置也是在值最大处。5、cv::TM_CCOEFF:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配,最佳匹配结果在值等于1处,最差匹配结果在值等于-1处,值等于0直接表示二者不相关。6、cv::TM_CCOEFF_NORMED:归一化的相关性系数匹配方法,正值表示匹配的结果较好,负值则表示匹配的效果较差,也是值越大,匹配效果也好。*///寻找最佳匹配位置minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);circle(image_source,Point(maxLoc.x + image_template.cols/2, maxLoc.y + image_template.rows/2),//使用cv::TM_SQDIFF与cv::TM_SQDIFF_NORMED时maxLoc改为minLoc20,Scalar(0, 0, 255),2,8,0);imshow("match result", image_matched);imshow("target", image_source);imwrite("Target1.jpg", image_source);waitKey(0);return 0;
}

代码保存的图像如下:

该实例中以TM_CCOEFF_NORMED(归一化相关性系数匹配法)成功地确定出了青蛙在图片中的位置。代码输出图像截图如下:


      图中match_result是matchTemplate函数处理生成的中间图像,惊悚地提取出了原图像中的特征。

博主还做了另外几组的模板匹配,分别选择了合适的匹配方法:

1、模板为

匹配方法为TM_CCOEFF_NORMED

2、模板为

匹配方法为TM_CCOEFF

3、模板同2,匹配方法为TM_CCOEFF(这图的匹配方法似乎都不大合适)

4、模板同2,匹配方法为TM_CCOEFF_NORMED

5、模板同2,匹配方法为TM_CCOEFF_NORMED

上述的六张图片主要都用了TM_CCOEFF与TM_CCOEFF_NORMED两种方法。然而另外几种方法运行起来效果如何,方法之间的运行效率又如何呢?博主分别用matchTemplate的六种方法,基于上述5中的模板、图像进行了测试:

对上述模板

Template3.jpg

匹配图像

34.png

用六种方法进行匹配,输出100次matchTemplate、minMaxLoc函数语句循环所花的时间:

程序如下(主要多了个循环,以及计时函数):

#include <iostream>
#include <stdio.h>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main()
{//加载源图像和模板图像Mat image_source = imread("34.png");Mat image_template = imread("Template3.jpg");Mat image_matched;double maxvalue = 0;double minVal, maxVal;Point minLoc, maxLoc;double startime = cv::getTickCount();for(int i = 0;i<100;i++){//模板匹配matchTemplate(image_source, image_template, image_matched, TM_CCOEFF_NORMED);/*1、cv::TM_SQDIFF:该方法使用平方差进行匹配,因此最佳的匹配结果在结果为0处,值越大匹配结果越差。2、cv::TM_SQDIFF_NORMED:该方法使用归一化的平方差进行匹配,最佳匹配也在结果为0处。3、cv::TM_CCORR:相关性匹配方法,该方法使用源图像与模板图像的卷积结果进行匹配,因此,最佳匹配位置在值最大处,值越小匹配结果越差。4、cv::TM_CCORR_NORMED:归一化的相关性匹配方法,与相关性匹配方法类似,最佳匹配位置也是在值最大处。5、cv::TM_CCOEFF:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配,最佳匹配结果在值等于1处,最差匹配结果在值等于-1处,值等于0直接表示二者不相关。6、cv::TM_CCOEFF_NORMED:归一化的相关性系数匹配方法,正值表示匹配的结果较好,负值则表示匹配的效果较差,也是值越大,匹配效果也好。*///寻找最佳匹配位置minMaxLoc(image_matched, &minVal, &maxVal, &minLoc, &maxLoc);}maxvalue = (cv::getTickCount() - startime)/cv::getTickFrequency();cout << maxvalue << endl;circle(image_source,Point(maxLoc.x + image_template.cols/2, maxLoc.y + image_template.rows/2), //使用cv::TM_SQDIFF与cv::TM_SQDIFF_NORMED时maxLoc改为minLoc20,Scalar(0, 0, 255),2,8,0);imshow("match result", image_matched);imshow("target", image_source);imwrite("Target_TM_CCOEFF_NORMED.png", image_source);waitKey(0);return 0;
}

六种方法的输出分别如下:

1、cv::TM_SQDIFF:1.86322秒,成功识别

2、cv::TM_SQDIFF_NORMED:1.66382秒,成功识别

3、cv::TM_CCORR:1.06706秒,未能识别

4、cv::TM_CCORR_NORMED:1.64302秒,成功识别(和3差了一个归一化居然差别这么大……)

5、cv::TM_CCOEFF:1.25885秒,成功识别(当日最佳)

6、cv::TM_CCOEFF_NORMED:1.72507秒,成功识别

综上可见,用cv::TM_CCOEFF找青蛙,兼具高效与准确。当然对不同的图像方法各有优势,在实际使用中最好六种方法都进行尝试再做判断。归一化步骤能将十分接近的数据区分开,从而避免特征遗漏(比如上文中的3和4),但同时也增加了程序运行的时间,是否使用要根据具体的图片、视频特征判断。

以上便是模板匹配在OpenCV中的实现方法,模板匹配也可作为图像跟踪的一种方法,与图像识别配合能将目标捕捉的运行效率、捕捉准确度提高一个层级!这些将在之后的博文中详谈。

欢迎交流讨论!

后文链接:要点初见:OpenCV3中基于模板匹配的目标跟踪思路

要点初见:从旅行青蛙开始的OpenCV3模板匹配功能探索相关推荐

  1. 为什么佛系青蛙住进了阿里淘宝——访《旅行青蛙》制作人

    点击上方"CSDN",选择"置顶公众号" 关键时刻,第一时间送达! 眼下,年初曾火爆一时的佛系手游<旅行青蛙>以新的方式与中国的玩家打了声招呼. 其 ...

  2. 转载 程序员上帝视角解读“旅行青蛙”,你的呱真的在旅行嘛? (手机游戏)...

    程序员上帝视角解读"旅行青蛙",你的呱真的在旅行嘛? 2018-02-05 黄小秋 数据与算法之美 来源:知乎 作者:黄小秋 原文链接:https://www.zhihu.com/ ...

  3. 你们的蛙儿子成马云儿子了 阿里巴巴获得《旅行青蛙》独家代理权

    大家还记得前段时间火遍大江南北的<旅行青蛙>吗?有多少盆友还在为青蛙儿子操心呢?今天,这只青蛙成了马云爸爸的亲儿子了. 阿里巴巴于今天宣布与日本游戏公司Hit-Point达成战略合作,获得 ...

  4. 旅行青蛙服务器维护时间,旅行青蛙多久出门一次?青蛙旅行出门时间一览

    旅行青蛙一般多久出一次门呢?青蛙旅行出门时间是怎么算的?相信许多玩家都还不是很清楚,那么接下来就随小编一起来看看具体解答吧! 小编推荐: 旅行青蛙出门时间介绍 有一些养娃族发现自己的小青蛙老是待在家, ...

  5. 旅行青蛙游戏背后所存在的情感。

    我滴娃儿砸你在哪儿?很多养娃的小姐姐都喜欢这样表达自己的诉求.我们解读一下这句话,首先她把青蛙称呼为儿子,她的需求是想要知道娃儿子在哪儿.为什么用户会有这样的想法?为什么从一开始养青蛙变成了养儿子? ...

  6. 利用python玩旅行青蛙

    旅行青蛙,刷三叶草脚本 链接:https://github.com/fisherboys/frog === 思路 === 将系统时间设置到很久之前,每隔三个小时收割一次三叶草,直至当前真正时间 === ...

  7. Egret引擎制作旅行青蛙

    项目成品试玩链接:http://www.6edu.top/190503095046/index.html 因为本人已经开始考研了,时间不是特别充足,项目制作的比较简陋,还请见谅.(本人利用五一假期进行 ...

  8. 写一个旅行青蛙攻略APP

    界面效果如下 不要忘了我们的所有数据和界面都可以在github的json文件里面配置有需要的小伙伴自行拿去用吧!! 项目github地址 Apk下载地址 设计意图 最近旅行青蛙很火,我自己也在玩,然后 ...

  9. 安卓版旅行青蛙三叶草修改教程

    安卓版旅行青蛙三叶草修改教程 1. 提取2次游戏存档(三叶草数量改动一下并记录数值) 位置:手机存储卡/Android/data/jp.co.hit_point.tabikaeru/files/Gam ...

最新文章

  1. strcmp可以比较数组么_6.3 C语言字符数组
  2. Js Chars应用
  3. Python入门100题 | 第045题
  4. 【转载】字段符号在ABAP OOP中的应用
  5. php mysql设计中常问_PHP开发中常见的设计模式
  6. 入职体检——项目列表(11项)
  7. [原创]换一份工作要考虑什么?
  8. 机器学习算法 Python R速查表
  9. esxi 安装网卡驱动
  10. XRD测试的68个问题(三)
  11. 火星开发板_数据科学家来自火星,软件开发人员来自金星
  12. 回顾来路,不忘初心;心之所向,砥砺前行
  13. F轮融资3.6亿美元,Keep能撑起20亿美元的估值吗?
  14. python:实现恩尼格玛密码机算法(附完整源码)
  15. 基于Zynq的光流法软硬件协同实现
  16. 管理远程团队的4种方法
  17. php嵌入图片代码,php如何添加图片
  18. python3代码换行与不换行问题
  19. 时间序列信号处理(四)——傅里叶变换和短时傅里叶变换python实现
  20. 韦东山嵌入式第一期学习笔记DAY_1——3_2_shell命令解析器功能说明

热门文章

  1. 第二季度UWA STAR —只有不拒绝问题,才能进一步解决问题
  2. 【git】使用git链接远程gitee仓库并提交
  3. 每次压力大到爆,驾校教练总爱跑敬老院干这件事
  4. 漫游测试之性能测试(5.4-执行计划)
  5. Sanity测试(健全测试)vs回归测试
  6. 微信小程序:全新趣味测试
  7. 布赖恩·克尼根位计数算法说明及简单使用
  8. MacBook:如何拆卸或安装内存
  9. nginx打包文件以及解压
  10. 政府补贴政策这么多,ITSS信息技术服务标准到底是什么?