前言
通过本专栏前面两篇文章大家已对图片滑块验证码有了初步的了解,对于滑块验证的实现和校验原理有了一定的了解,通过由浅入深的实战案例可直接应用于实战,对于滑块如何在前端实现滑动或接口调用可自行查阅相关资料实现,本文主要讲解 滑块验证码 模板匹配 识别的通用解决方案。
一、简述 图片模板匹配
图片模板匹配常用于在一幅图像中寻找特定内容的任务中。模板图像的尺寸小于待匹配图像的尺寸,同时比较两张图像中的每一个像素的灰度值,因此常采用在待匹配图像中选择与模板相同尺寸的滑动窗口,通过比较滑动窗口与模板的相似程度,判断待匹配图像中是否含有与模板图像相同的内容。
图像是模板图像,每个像素中的数字是该像素的灰度值
模板匹配的流程如下:
Step1:在待匹配图像中选取与模板尺寸大小相同的滑动窗口,如图中的阴影区域所示。
Step2:比较滑动窗口中每个像素与模板中对应像素灰度值的关系,计算模板与滑动窗口的相似性。
Step3:将滑动窗口从左上角开始先向右滑动,滑动到最右边后向下滑动一行,从最左侧重新开始滑动,记录每一次移动后计算的模板与滑动窗口的相似性。
Step4:比较所有位置的相似性,选择相似性最大的滑动窗口作为备选匹配结果。
二、源码分析 图片模板匹配
2.1 源码分析 代码示例
OpenCV中提供了用于图像模板匹配的函数matchTemplate(),该函数能够实现模板匹配过程中图像与模板相似性的计算。
下述为Python OpenCV matchTemplate函数方法源码
def matchTemplate(image, templ, method, result=None, mask=None): # real signature unknown; restored from __doc__
"""
matchTemplate(image, templ, method[, result[, mask]]) -> result
. @brief Compares a template against overlapped image regions.
.
. The function slides through image , compares the overlapped patches of size \f$w \times h\f$ against
. templ using the specified method and stores the comparison results in result . #TemplateMatchModes
. describes the formulae for the available comparison methods ( \f$I\f$ denotes image, \f$T\f$
. template, \f$R\f$ result, \f$M\f$ the optional mask ). The summation is done over template and/or
. the image patch: \f$x' = 0...w-1, y' = 0...h-1\f$
.
. After the function finishes the comparison, the best matches can be found as global minimums (when
. #TM_SQDIFF was used) or maximums (when #TM_CCORR or #TM_CCOEFF was used) using the
. #minMaxLoc function. In case of a color image, template summation in the numerator and each sum in
. the denominator is done over all of the channels and separate mean values are used for each channel.
. That is, the function can take a color template and a color image. The result will still be a
. single-channel image, which is easier to analyze.
.
. @param image Image where the search is running. It must be 8-bit or 32-bit floating-point.
. @param templ Searched template. It must be not greater than the source image and have the same
. data type.
. @param result Map of comparison results. It must be single-channel 32-bit floating-point. If image
. is \f$W \times H\f$ and templ is \f$w \times h\f$ , then result is \f$(W-w+1) \times (H-h+1)\f$ .
. @param method Parameter specifying the comparison method, see #TemplateMatchModes
. @param mask Optional mask. It must have the same size as templ. It must either have the same number
. of channels as template or only one channel, which is then used for all template and
. image channels. If the data type is #CV_8U, the mask is interpreted as a binary mask,
. meaning only elements where mask is nonzero are used and are kept unchanged independent
. of the actual mask value (weight equals 1). For data tpye #CV_32F, the mask values are
. used as weights. The exact formulas are documented in #TemplateMatchModes.
"""
pass
2.2 源码分析 参数含义
image:待模板匹配的原图像,图像数据类型为CV_8U和CV_32F两者中的一个。
templ:模板图像,需要与image具有相同的数据类型,但是尺寸不能大于image。
method:模板匹配方法标志,可选择参数及含义在下文表中给出。
result:模板匹配结果输出图像,图像数据类型为CV_32F。如果image的尺寸为W×H,模板图像尺寸为w×h,则输出图像的尺寸为(W-w+1)×(H-h+1)。
mask:匹配模板的掩码,必须与模板图像具有相同的数据类型和尺寸,默认情况下不设置,目前仅支持在TM_SQDIFF和TM_CCORR_NORMED这两种匹配方法时使用。
该函数同时支持灰度图像和彩色图像两种图像的模板匹配。
函数前两个参数为输入的原图像和模板图像,由于是在原图像中搜索是否存在与模板图像相同的内容,因此需要模板图像的尺寸小于原图像,并且两者必须具有相同的数据类型。
第三个参数为相似性矩阵,滑动窗口与模板的相似性系数存放在滑动窗口左上角第一个像素处,因此输出的相似性矩阵尺寸要小于原图像的尺寸,如果image的尺寸为W×H,模板图像尺寸为w×h,则输出图像的尺寸为(W-w+1)×(H-h+1)。因为在模板匹配中原图像不需要进行尺寸的外延,所以滑动窗口左上角可以移动的范围要小于原图像的尺寸。无论输入的是彩色图像还是灰度图像,函数输出结果都是单通道矩阵。了解相似性系数记录的方式便于寻找到与模板最相似的滑动窗口,继而在原图中标记出与模板相同的位置。
函数第四个参数是滑动窗口与模板相似性系数的计算方式,OpenCV提供了多种计算方法,所有可以选择的标志参数在下文表中给出,接下来对每一种方法进行详细介绍。
2.3 OpenCV matchTemplate()函数模板匹配方法选择标志参数
了解不同的计算相似性方法时,重点需要知道在每种方法中最佳匹配结果的数值应该是较大值还是较小值,由于matchTemplate()函数的输出结果是存有相关性系数的矩阵,因此需要通过minMaxLoc()函数去寻找输入矩阵中的最大值或者最小值,进而确定模板匹配的结果。
通过寻找输出矩阵的最大值或者最小值得到的只是一个像素点,需要以该像素点为矩形区域的左上角,绘制与模板图像同尺寸的矩形框,标记出最终匹配的结果。为了了解图像模板匹配相关函数的使用方法,
标志参数 | 简记 | 作用 |
TM_SQDIFF | 0 | 平方差匹配法 |
TM_SQDIFF_NORMED | 1 | 归一化平方差匹配法 |
TM_CCORR | 2 | 相关匹配法 |
TM_CCORR_NORMED | 3 | 归一化相关匹配法 |
TM_CCOEFF | 4 | 系数匹配法 |
TM_CCOEFF_NORMED | 5 | 归一化相关系数匹配法 |
2.3.1 TM_SQDIFF
该方法名为平方差匹配法,计算的公式如下所示,这种方法利用平方差来进行匹配,当模板与滑动窗口完全匹配时计算数值为0,两者匹配度越低计算数值越大。
其中 T(x′,y′) 表示模板图像, I(x,y) 表示原图像。
2.3.2 TM_SQDIFF_NORMED
该方法名为归一化平方差匹配方法,计算公式如下所示,这种方法是将平方差方法进行归一化,使得输入结果缩放到了0到1之间,当模板与滑动窗口完全匹配时计算数值为0,两者匹配度越低计算数值越大。
2.3.3 TM_CCORR
该方法名为相关匹配法,计算公式如下所示,这类方法采用模板和图像间的乘法操作,所以数值越大表示匹配效果越好,0表示最坏的匹配结果。
2.3.4 TM_CCORR_NORMED
该方法名为归一化相关匹配法,计算公式如下所示,这种方法是将相关匹配法进行归一化,使得输入结果缩放到了0到1之间,当模板与滑动窗口完全匹配时计算数值为1,两者完全不匹配时计算结果为0。
2.3.5 TM_CCOEFF
该方法名为系数匹配法,计算公式如下所示,这种方法采用相关匹配方法对模板减去均值的结果和原图像减去均值的结果进行匹配,这种方法可以很好的解决模板图像和原图像之间由于亮度不同而产生的影响。该方法中模板与滑动窗口匹配度越高计算数值越大,匹配度越低计算数值越小,并且该方法计算结果可以为负数。
2.3.6 TM_CCOEFF_NORMED
该方法名为归一化系数匹配法,计算公式如下所示,这种方法将系数匹配方法进行归一化,使得输入结果缩放到了1到-1之间,当模板与滑动窗口完全匹配时计算数值为1,当两者完全不匹配时计算结果为-1。
三、实战演练 图片模板匹配
3.1 OpenCV 图片模板匹配 参数和匹配方法调整 示例
#!/usr/bin/env python
# -*- coding=utf-8 -*-
__author__ = 'Benjamin'
__CreateAt__ = '2023/1/1-18:03'
import cv2
def getImageXindex(bj_rgb_path='getBigImg.png', hk_rgb_path='getSmallImg.png'):
# bj_rgb = cv2.imread(bj_rgb_path) # 读取图片
# bj_gray = cv2.cvtColor(bj_rgb,cv2.COLOR_BGR2GRAY) # 图片灰度 可调整
bj_rgb = cv2.imread(bj_rgb_path,0) # 读取图片 默认图片灰度
hk_rgb = cv2.imread(hk_rgb_path,0)
res = cv2.matchTemplate(bj_rgb,hk_rgb,cv2.TM_CCORR_NORMED) # 调整method调试
lo = cv2.minMaxLoc(res)
return lo[3][0]
if __name__ == "__main__":
bj_rgb_path = "./data/getBigImg.png"
hk_rgb_path = "./data/getSmallImg.png"
getImageXindex(bj_rgb_path,hk_rgb_path)
3.2 OpenCV 图片模板匹配 测试结果展示
通过换不同的方法参数再测试统计得出适合自己项目的最佳通过率,再应用于实战并解决问题。
> Python getCode.py
2023-01-06 21:56:21 第1次校验成功,x坐标为:106,校验结果为:{"data":"success"},校验耗时:1.21秒
2023-01-06 21:56:23 第2次校验成功,x坐标为:158,校验结果为:{"data":"success"},校验耗时:1.12秒
2023-01-06 21:56:24 第3次校验成功,x坐标为:258,校验结果为:{"data":"success"},校验耗时:1.17秒
2023-01-06 21:56:25 第4次校验成功,x坐标为:96,校验结果为:{"data":"success"},校验耗时:1.11秒
2023-01-06 21:56:26 第5次校验成功,x坐标为:218,校验结果为:{"data":"success"},校验耗时:1.14秒
2023-01-06 21:56:27 第6次校验成功,x坐标为:47,校验结果为:{"data":"success"},校验耗时:1.09秒
2023-01-06 21:56:28 第7次校验成功,x坐标为:223,校验结果为:{"data":"success"},校验耗时:1.09秒
2023-01-06 21:56:29 第8次校验成功,x坐标为:133,校验结果为:{"data":"success"},校验耗时:1.2秒
2023-01-06 21:56:31 第9次校验成功,x坐标为:115,校验结果为:{"data":"success"},校验耗时:1.19秒
2023-01-06 21:56:32 第10次校验失败,x坐标为:134,校验结果为:{"data":"fail"},校验耗时:1.15秒
len(sucessList):9,len(errorList):1,result:90.0%