当前位置: 首页 > news >正文

Python OpenCV 模板匹配的一些应用场景和方法思考,浅析KAZE特征匹配对比

前言

        本专栏前几篇提到OpenCV模板匹配的原理和代码实现解析,时过几年,同时伴随AI各种模型的发展,现在又遇见了类似的应用场景,基于图片模板匹配识别图片位置的UI自动化场景的一些思考,对比基于kaza特征匹配是在阅读Airtest实现源码时获取到信息,本次不做过多讲解仅对比一下优点和缺点。

文章调研问题背景

尝试解决解决 Airtest 图片模板匹配问题的有些思考,是否有其他更优解?

1、匹配慢

2、匹配失败

一、OpenCV模板匹配 简短回顾和新的猜想

1.1 识别目标 框标记

通过专栏上文获取信息,在大图中标记滑块的位置是否准确,可以本地人工先Check检查一下是否准确,调试自测用

1.2 OpenCV 模板匹配的场景方法和各自识别特点

标志参数

简记

作用

TM_SQDIFF

0

平方差匹配法

TM_SQDIFF_NORMED

1

归一化平方差匹配法

TM_CCORR

2

相关匹配法

TM_CCORR_NORMED

3

归一化相关匹配法

TM_CCOEFF

4

系数匹配法

TM_CCOEFF_NORMED

5

归一化相关系数匹配法

1.2.1 以下表格对比所有方法的特性,便于快速参考

标志参数简记最佳匹配值光照敏感性典型应用场景
TM_SQDIFF0最小值 (0)高敏感光照稳定的像素级匹配
TM_SQDIFF_NORMED1接近 0中等敏感需归一化的平方差场景
TM_CCORR2最大值不敏感 (对偏移敏感)亮度差异小的通用匹配
TM_CCORR_NORMED3接近 1低敏感复杂背景的物体识别
TM_CCOEFF4正值最大低敏感光照不均的场景
TM_CCOEFF_NORMED51 (最大值)不敏感

高鲁棒性需求,如变化光照

1.2.2 使用建议

‌算法选择‌:随着方法从简单(如TM_SQDIFF)到复杂(如TM_CCOEFF_NORMED),计算开销增加,但匹配精度和鲁棒性提高优先选择归一化版本以提高泛化性。

‌局限性‌::模板匹配仅支持平移,对旋转或缩放无效;实际应用中需结合其他技术处理变形.

性能优化‌: 在OpenCV中,使用matchTemplate函数并指定method参数,通过minMaxLoc获取最佳匹配位置。

1.3 OpenCV TM_CCOEFF_NORMED 归一化相关系数匹配法 验证预期点击图片在当前界面中的位置

自爆问题:取的相似度前10(可自定义),未去重

基于Airtest框架UI自动化识别图片位置的流程解决方法和思考

前提

1、模板截图,应该是在pycharm原图大小格式打开的,测试资源

2、原图就是手机截图

3、取的相似度前10,还没有去重

4、方法就是自带的图片模板匹配, 使用归一化相关系数法进行模板匹配

PS:

1、可能问题出在模板匹配,人工截图大小保存的问题

2、其他步骤应该都是通用的 获取手机当前屏幕图(通用耗时)、模板图、模板匹配算法(通用耗时)

既然下图示例能找到图片的位置并框,从技术层面是可以识别点击图片,至于是否经得起不同项目的考验和推敲未知,只是阐述一种解决方案,那就能找到点击坐标的中心点,后续均未处理需要自行处理并优化。

1.3.1 图例1 匹配XX页面某一个按钮图片

识别速度 0.118秒

1.3.2 图例2 匹配XX页面某一个按钮图片

识别速度 0.156秒

1.3.3 图例3 匹配XX页面某一个按钮图片

0.0936秒

1.3.4 参考部分调试代码

import cv2
import numpy as np
import osdef get_top_matches(res, top_k=5):flat = res.flatten()indices = np.argpartition(flat, -top_k)[-top_k:]indices = indices[np.argsort(-flat[indices])]return [(res.min(), flat[i],np.unravel_index(res.argmin(), res.shape),np.unravel_index(i, res.shape))for i in indices]def getImageX(bj_rgb_path, hk_rgb_path, top_k=1):# 读取图像时添加尺寸检查bj_rgb = cv2.imread(bj_rgb_path)if bj_rgb is None:raise FileNotFoundError(f"无法加载大图: {bj_rgb_path}")bj_gray = cv2.cvtColor(bj_rgb, cv2.COLOR_BGR2GRAY)hk_rgb = cv2.imread(hk_rgb_path, 0)if hk_rgb is None:raise FileNotFoundError(f"无法加载模板图: {hk_rgb_path}")res = cv2.matchTemplate(bj_gray, hk_rgb, cv2.TM_CCOEFF_NORMED)valid_matches = []matches = get_top_matches(res, top_k=top_k)for i, (min_val, max_val, min_loc, max_loc) in enumerate(matches):# 转换为实际图像坐标(考虑模板尺寸)h, w = hk_rgb.shapept1 = (max_loc[1], max_loc[0])  # OpenCV的(x,y)格式pt2 = (pt1[0] + w, pt1[1] + h)# 边界检查if pt2[0] > bj_rgb.shape[1] or pt2[1] > bj_rgb.shape[0]:print(f"警告:匹配点{i + 1}超出图像边界")continueprint(f"匹配点{i + 1}: 相似度={max_val:.3f}, 位置={pt1}->{pt2}")valid_matches.append((pt1, pt2))return valid_matchesdef draw_match_boxes(image_path, match_points, save_name="result.jpg"):img = cv2.imread(image_path)if img is None:raise FileNotFoundError(f"无法加载图像: {image_path}")for i, (pt1, pt2) in enumerate(match_points):# 确保坐标在图像范围内pt1 = (max(0, min(pt1[0], img.shape[1] - 1)),max(0, min(pt1[1], img.shape[0] - 1)))pt2 = (max(0, min(pt2[0], img.shape[1] - 1)),max(0, min(pt2[1], img.shape[0] - 1)))cv2.rectangle(img, pt1, pt2, (0, 0, 255), 2)cv2.putText(img, str(i + 1), (pt1[0] + 5, pt1[1] + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)cv2.imwrite(save_name, img)print(f"结果保存至: {os.path.abspath(save_name)}")# 使用示例
if __name__ == "__main__":import timet1 = time.time()# 获取匹配点(自动处理边界)matches = getImageX(bj_rgb_path='1-1.png', hk_rgb_path='1-6.png', top_k=4)# 绘制结果if isinstance(matches, list):  # 多匹配点模式draw_match_boxes('1-1.png', matches)else:  # 单匹配点模式print(f"最佳匹配X坐标: {matches}")t2 = time.time()print(t2 - t1)

二、阅读Airtest源码,浅析基于kaze进行图像识别,只筛选出最优区域

2.1 Airtest 测试代码简短分析,方法调用顺序

通过点击运行脚本中的方法,ctrl+鼠标单击,一步一步debug看方法调用顺序推理得出

1、touch(Template(f"{ElementsRepo}/index/查找酒店.png"))

2、Template函数类中的图片识别方法

    2.1 实际调研的find_best_result找到最优解的方法

3、find_best_result最优解的方法的具体实现

2.2 find_best_result 方法浅析

cv.py 代码片段

'''
基于kaze进行图像识别,只筛选出最优区域,对应具体方法名称
'''
MATCHING_METHODS = {"tpl": TemplateMatching,"mstpl": MultiScaleTemplateMatchingPre,"gmstpl": MultiScaleTemplateMatching,"kaze": KAZEMatching,"brisk": BRISKMatching,"akaze": AKAZEMatching,"orb": ORBMatching,"sift": SIFTMatching,"surf": SURFMatching,"brief": BRIEFMatching,
}class Template(object):"""picture as touch/swipe/wait/exists target and extra info for cv matchfilename: pic filenametarget_pos: ret which pos in the picrecord_pos: pos in screen when recordingresolution: screen resolution when recordingrgb: 识别结果是否使用rgb三通道进行校验.scale_max: 多尺度模板匹配最大范围.scale_step: 多尺度模板匹配搜索步长."""@logwrapdef _cv_match(self, screen):# in case image file not exist in current directory:ori_image = self._imread()image = self._resize_image(ori_image, screen, ST.RESIZE_METHOD)ret = Nonefor method in ST.CVSTRATEGY:# get function definition and execute:func = MATCHING_METHODS.get(method, None)if func is None:raise InvalidMatchingMethodError("Undefined method in CVSTRATEGY: '%s', try 'kaze'/'brisk'/'akaze'/'orb'/'surf'/'sift'/'brief' instead." % method)else:if method in ["mstpl", "gmstpl"]:ret = self._try_match(func, ori_image, screen, threshold=self.threshold, rgb=self.rgb, record_pos=self.record_pos,resolution=self.resolution, scale_max=self.scale_max, scale_step=self.scale_step)else:ret = self._try_match(func, image, screen, threshold=self.threshold, rgb=self.rgb)if ret:breakreturn ret@staticmethoddef _try_match(func, *args, **kwargs):G.LOGGING.debug("try match with %s" % func.__name__)try:ret = func(*args, **kwargs).find_best_result()except aircv.NoModuleError as err:G.LOGGING.warning("'surf'/'sift'/'brief' is in opencv-contrib module. You can use 'tpl'/'kaze'/'brisk'/'akaze'/'orb' in CVSTRATEGY, or reinstall opencv with the contrib module.")return Noneexcept aircv.BaseError as err:G.LOGGING.debug(repr(err))return Noneelse:return ret

settings.py 代码片段

cv.py 中 ST.CVSTRATEGY 参数,Airtest中图片特征匹配的4个方法先后顺序

CVSTRATEGY = ["mstpl", "tpl", "sift", "brisk"]

template_matching.py 代码片段

    @print_run_timedef find_best_result(self):"""基于kaze进行图像识别,只筛选出最优区域.""""""函数功能:找到最优结果."""# 第一步:校验图像输入check_source_larger_than_search(self.im_source, self.im_search)# 第二步:计算模板匹配的结果矩阵resres = self._get_template_result_matrix()# 第三步:依次获取匹配结果min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)h, w = self.im_search.shape[:2]# 求取可信度:confidence = self._get_confidence_from_matrix(max_loc, max_val, w, h)# 求取识别位置: 目标中心 + 目标区域:middle_point, rectangle = self._get_target_rectangle(max_loc, w, h)best_match = generate_result(middle_point, rectangle, confidence)LOGGING.debug("[%s] threshold=%s, result=%s" % (self.METHOD_NAME, self.threshold, best_match))return best_match if confidence >= self.threshold else None

2.3 简短小节

   从阅读源码中了解到Airtest是基于kaza优先使用"mstpl": MultiScaleTemplateMatchingPre,方法进行图片特征匹配

2.3.1 MultiScaleTemplateMatchingPre 浅析

    AKAZE(Accelerated-KAZE)图片匹配结合多尺度模板匹配(MultiScaleTemplateMatchingPre)的优点和缺点可总结如下:

优点

‌尺度不变性‌:通过非线性扩散滤波构建图像金字塔,实现多尺度特征检测,适应不同分辨率的目标匹配。
‌旋转鲁棒性‌:采用M-LDB描述子,对图像旋转和视角变化具有较强稳定性。
‌高效性‌:相比传统模板匹配,AKAZE通过加速非线性扩散优化计算效率。
‌动态场景适配‌:支持掩模处理和加权匹配,减少旋转后黑边区域的干扰。

‌缺点‌

计算复杂度高‌:多尺度金字塔构建和特征描述生成需较多计算资源。
‌光照敏感‌:与模板匹配类似,对光照变化仍有一定敏感性,需额外预处理。
‌参数调优依赖‌:需合理设置尺度层级和旋转角度步长,否则可能影响匹配精度。

该方法综合了特征匹配的鲁棒性和模板匹配的直观性,适合工业检测等动态场景。

三、Airtest中基于kaza图片匹配失败的原因分析,对比OpenCV可视化效果

3.1 Airtest中基于kaza图片匹配失败识别的场景

原因分析说明:手机屏幕截图的分辨率是原图格式大小,需要匹配的图片是不同人操作截图分辨率和原图大小图片区域不一致,从报告中看args参数未匹配到该图片,且未显示相似度

3.1.1 识别 失败案例

3.1.2 识别 成功案例

3.2 使用相同的图片采用OpenCV TM_CCOEFF_NORMED 归一化相关系数匹配法 对比

可以看出OpenCV能匹配,但是小图在大图中的位置偏小,且存在识别错误。相比kaza无识别结果有概率提高成功率。

3.2.1 对比展示效果,红框数字标记

3.3.2 图片相似度结果展示

匹配点1: 相似度=0.522, 位置=(np.int64(983), np.int64(2074))->(np.int64(1032), np.int64(2123))
匹配点2: 相似度=0.522, 位置=(np.int64(983), np.int64(707))->(np.int64(1032), np.int64(756))
匹配点3: 相似度=0.521, 位置=(np.int64(847), np.int64(2070))->(np.int64(896), np.int64(2119))
匹配点4: 相似度=0.521, 位置=(np.int64(848), np.int64(2070))->(np.int64(897), np.int64(2119))
结果保存至: D:\code_path\Python\KillBuy\testPro\result.jpg
0.11297798156738281

四、OpenCV 和 kaza 图片匹配简单对比分析

    还未深入研究,其他参考公开资料或AI大语言模型问答吧,合适的场景选择合适的工具,优缺点根据项目需要平衡。

 

相关文章:

  • Odoo 18进阶开发:打造专业级list,kanban视图Dashboard
  • Cmake入门及CMakeLists.txt 语法介绍
  • 数据库从零开始:MySQL 中的 DDL 库操作详解【Linux版】
  • 服务网格安全(Istio)从入门到实践
  • 电子电气架构 --- 软件供应商如何进入OEM体系
  • 频繁操作Json嵌套数据PostgreSQL配合JSON操作工具类+sql
  • 最小化联邦平均(FedAvg)的算法开销
  • Apipost 签约锐捷网络:AI赋能,共推 ICT 领域 API 生态智能化升级
  • 算法第38天|322.零钱兑换\139. 单词拆分
  • FTTR+软路由网络拓扑方案
  • LVS vs Nginx 负载均衡对比:全面解析
  • 【分布式】基于Dubbo实现对远程调用接口的封装
  • 数据结构第八章(六)-置换选择排序和最佳归并树
  • 企业实践 | 银河麒麟KylinOS-V10(SP3)高级服务器操作系统基础安装指南
  • cusor资源管理器缩进调整与工具条竖着摆放
  • 电子制造智能化转型:MES如何解决工艺复杂、质量追溯与供应链协同
  • 如何使用postman做接口自动化测试?
  • 魅族“换血”出牌:手机基本盘站不稳,想靠AI和汽车“改命”
  • 使用 C++/OpenCV 构建中文 OCR 系统:实现账单、发票及 PDF 读取
  • OCR大模型,破解金融文档处理困境,从文字识别到文字理解
  • 网站开发用什么开发工具好呢/智能识别图片
  • 免费织梦网站源码下载/搜了网推广效果怎么样
  • 免费做长图的网站/网络黄页平台网址有哪些
  • 淄博建设工程学校官方网站/模板建站多少钱
  • 网站设计 深圳/整合营销的案例
  • 潍坊做网站张家口/新东方在线koolearn