aircv是网易放出的小开源项目,应该也是现在做简单图像匹配被引用最多的项目了。上一篇做了如何使用aircv之find_template的描述,然而,它并不算是一个成熟的项目,里面的小坑不少,有待改进。今天先做个代码逻辑的解析。
核心函数find_template 与 find_all_template
find_template函数是返回第一个最匹配的结果(位置未必在最上面),而find_all_template是返回所有大于指定置信度的结果。
比如要在思否页面截图中找
结果如如下图所示:
我们深入进去看一下代码,就会发现find_template是这样写的:
def find_template(im_source, im_search, threshold=0.5, rgb=False, bgremove=False):
'''
@return find location
if not found; return None
'''
result = find_all_template(im_source, im_search, threshold, 1, rgb, bgremove)
return result[0] if result else None
好家伙! 直接调用find_all_template,然后取返回值的第一个。。。
所以find_all_template才是真正的核心,我们排除掉无关代码,来看一下最关键的部分:
def find_all_template(im_source, im_search, threshold=0.5, maxcnt=0, rgb=False, bgremove=False):
# 匹配算法,aircv实际上在代码里写死了用CCOEFF_NORMED,大部分测试的效果,也确实是这个算法更好
method = cv2.TM_CCOEFF_NORMED
# 获取匹配矩阵
res = cv2.matchTemplate(im_source, im_search, method)
w, h = im_search.shape[1], im_search.shape[0]
result = []
while True:
# 找到匹配最大最小值
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
if max_val < threshold:
break
# 计算中心点
middle_point = (top_left[0]+w/2, top_left[1]+h/2)
# 计算四个角的点,存入结果集
result.append(dict(
result=middle_point,
rectangle=(top_left, (top_left[0], top_left[1] + h), (top_left[0] + w, top_left[1]), (top_left[0] + w, top_left[1] + h)),
confidence=max_val
))
# 把最匹配区域填充掉,再继续查找下一个
cv2.floodFill(res, None, max_loc, (-1000,), max_val-threshold+0.1, 1, flags=cv2.FLOODFILL_FIXED_RANGE)
return result
其中cv2.matchTemplate
是opencv的方法,它的返回值是个矩阵,相当于用小图在大图上滑动,从左上角开始,每次移动一个像素,然后计算一个匹配结果,最终形成结果矩阵。
结果矩阵大小应该是: (W - w + 1) x (H - h + 1),其中W,H是大图的宽高, w和h是小图的宽高。
这个矩阵中最大值的那个点,就表示小图的左上角对在这个位置时,匹配度最高,由此得到第一个匹配结果。
最后cv2.floodFill
的作用,是把结果矩阵最大值这块区域用别的数字填充掉,这样就可以查找下一个最大值了,而且也避免了区域重叠的现象(要不然下一个最大值,有可能在刚刚找到的区域里面)。
几个小问题
- 不支持灰度图和带透明通道的图
其实opencv的matchTemplate本来只支持灰度图,但大多数情况下,我们都是查找彩色图,所以aircv封装的时候,把bgr三个彩色通道做了分离,分别调用matchTemplate,然后再合并结果。
但这个封装没有兼容本来就是灰度图的情况,竟然会出错,而且如果源图带透明通道也会出错,为此,我专门提交了一个PR,但一直没有处理,看来项目已经没人维护了。
- floodFill貌似有点兴师动众了,numpy的切片应该可以实现
这个回头写段小代码验证一下 - 不能处理图片的缩放
模板图片和原图中的内容,并不一定大小严格一致的,那么就需要做一些缩放处理,重复尝试。 - 图像匹配效果不理想
如果是完全一致的图,find_template的效果非常好,但当需要模糊匹配时,某些人眼一看就相同的图像,是无法被识别出来的,有点时候又会误识别,这可能需要从两方面改进:
-- 调整算法:采用SIFT之类的特征点检测算法,可以解决大小和角度都不一致的图像匹配问题;Halcon等商业软件,采用了shape based matching,匹配效果更佳。
-- 加入信息:有时候除了模板图像本身,我们也许知道更多的信息,比如图像可能出现的位置范围,图像周边区域的样式等等,来帮助提升识别准确度,减少误识和漏识。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。