1 前提
使用Appium模拟点击微博APP并获取当前页面的网页源码时,遇到了一个问题:tapByXpath(By.xpath("//android.widget.FrameLayout[@content-desc=\"发现\"]"))
和 driver.getPageSource()
等代码运行异常;广大网友也只给出了一个很模棱两可的解释:当前页面未加载完成,调用相关方法会打断加载过程触发死循环。
无奈之下,只能点击页面坐标;可页面布局是灵活的,要点击的位置会时不时的发生变化,这就对日常生产产生了影响。
为了减少维护工作,页面坐标的确定交由代码来判断。简单暴力的做法:抠出目标图片,大图遍历坐标,RGB与目标图片一致的坐标就是目标图片的坐标,点击坐标继续后续操作。以上做法的缺点也很明显:效率低,甚至严重影响性能。
基于以上情况,我们需引入一些图像相似度算法来提高效率。
2 对像素求方差并比对
2.1 算法逻辑
缩放图片
将需要处理的图片所放到指定尺寸,缩放后图片大小由图片的信息量和复杂度决定。譬如,一些简单的图标之类图像包含的信息量少,复杂度低,可以缩放小一点。风景等复杂场景信息量大,复杂度高就不能缩放太小,容易丢失重要信息。根据自己需求,弹性的缩放。在效率和准确度之间维持平衡。
灰度处理
通常对比图像相似度和颜色关系不是很大,所以处理为灰度图,减少后期计算的复杂度。如果有特殊需求则保留图像色彩。
计算平均值
此处开始,与传统的哈希算法不同:分别依次计算图像每行像素点的平均值,记录每行像素点的平均值。每一个平均值对应着一行的特征。
计算方差
对得到的所有平均值进行计算方差,得到的方差就是图像的特征值。方差可以很好的反应每行像素特征的波动,既记录了图片的主要信息。
比较方差
经过上面的计算之后,每张图都会生成一个特征值(方差)。到此,比较图像相似度就是比较图像生成方差的接近成程度。
一组数据方差的大小可以判断稳定性,多组数据方差的接近程度可以反应数据波动的接近程度。我们不关注方差的大小,只关注两个方差的差值的大小。方差差值越小图像越相似!
3 感知哈希算法
“感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个“指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
3.1 算法步骤
缩小尺寸
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
简化色彩
将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
计算平均值
计算所有64个像素的灰度平均值
比较像素的灰度平均值
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
计算哈希值
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算“汉明距离”(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
4 模板匹配
4.1 模板匹配的定义
模板就是一幅已知的小图像,而模板匹配就是在一幅大图像中搜寻目标,已知该图中有要找的目标,且该目标与模板有相同的尺度,方向和图像元素,通过一定的算法可以在图像中找到目标。
模板匹配和卷积原理很像,模板在原图像上开始滑动,计算模板与图像被模板覆盖的地方的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图像是AB大小,而模板是ab大小,则输出结果的矩阵是(A-a+1)*(B-b+1)。
4.2 模板匹配方法
模板匹配在opencv中的函数为 cv2.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 . Here are the formulae
. for the available comparison methods ( \f$I\f$ denotes image, \f$T\f$ template, \f$R\f$ result ). 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 Mask of searched template. It must have the same datatype and size with templ. It is
. not set by default. Currently, only the #TM_SQDIFF and #TM_CCORR_NORMED methods are supported.
"""
pass
下面对模板匹配方法进行解释:
- cv2.TM_CCOEFF:系数匹配法,计算相关系数,计算出来的值越大,越相关
- cv2.TM_CCOEFF_NORMED:相关系数匹配法,计算归一化相关系数,计算出来的值越接近1,越相关
- cv2.TM_CCORR:相关匹配法,计算相关性,计算出来的值越大,越相关
- cv2.TM_CCORR_NORMED:归一化相关匹配法,计算归一化相关性,计算出来的值越接近1,越相关
- cv2.TM_SQDIFF:平方差匹配法,计算平方不同,计算出来的值越小,越相关
- cv2.TM_SQDIFF_NORMED:归一化平方差匹配法,计算归一化平方不同,计算出来的值越接近0,越相关
公式复制opencv官网,如下:
4.3 cv2.minMaxLoc()
函数用法
cv2.minMaxLoc()
函数功能:假设有一个矩阵a,现在需要求这个矩阵的最小值,最大值,并得到最大值,最小值的索引。就可以使用此函数。
cv2.minMaxLoc()
函数源码如下:
def minMaxLoc(src, mask=None): # real signature unknown; restored from __doc__
"""
minMaxLoc(src[, mask]) -> minVal, maxVal, minLoc, maxLoc
. @brief Finds the global minimum and maximum in an array.
.
. The function cv::minMaxLoc finds the minimum and maximum element values and their positions. The
. extremums are searched across the whole array or, if mask is not an empty array, in the specified
. array region.
.
. The function do not work with multi-channel arrays. If you need to find minimum or maximum
. elements across all the channels, use Mat::reshape first to reinterpret the array as
. single-channel. Or you may extract the particular channel using either extractImageCOI , or
. mixChannels , or split .
. @param src input single-channel array.
. @param minVal pointer to the returned minimum value; NULL is used if not required.
. @param maxVal pointer to the returned maximum value; NULL is used if not required.
. @param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required.
. @param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required.
. @param mask optional mask used to select a sub-array.
. @sa max, min, compare, inRange, extractImageCOI, mixChannels, split, Mat::reshape
"""
pass
函数返回值就是矩阵的最小值,最大值,最小值的索引,最大值的索引。