如何使用Python与OpenCV比较两张图像并判断是否相似?
基于Python+OpenCV的图像相似性判断实现方案
根据不同的业务场景适配不同精度、不同性能的实现方法,没有通用的“最优解”,以下是工程中常用的三类实现路径,可直接复用:
1. 像素级差值计算(适用:对齐无变形的同机位图像对比)
这类方法逻辑最简单,计算速度最快,适合固定机位截图、UI变更检测这类无视角差、无缩放平移的场景。
- 实现步骤:统一两张图的尺寸、转换为灰度图降低计算量、逐像素计算差值、将差值归一化为0-1区间的相似度得分
- 核心代码:
import cv2 import numpy as np def calc_pixel_similarity(img1_path, img2_path): # 读取两张图,强制调整为相同尺寸消除尺寸差干扰 img1 = cv2.imread(img1_path) img2 = cv2.imread(img2_path) img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0])) # 转灰度图降低计算维度 gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 逐像素计算绝对差值 diff = cv2.absdiff(gray1, gray2) # 归一化得到相似度得分,得分越高越相似(范围0-1) similarity_score = 1 - (np.sum(diff) / (diff.shape[0] * diff.shape[1] * 255)) return round(similarity_score, 4)
- 局限性:只要图像存在轻微平移、缩放、亮度变化,相似度得分就会大幅下跌,对形变鲁棒性极差。
2. 全局直方图匹配(适用:对亮度变化敏感、无严格位置要求的粗粒度对比)
这类方法不关注像素位置,只统计全局的色彩分布规律,对光照变化、小范围平移的鲁棒性比纯像素法好很多。
- 实现步骤:将图像转换为HSV色彩空间、计算双维度颜色直方图、用巴氏距离衡量直方图的匹配程度
- 核心代码:
def calc_hist_similarity(img1_path, img2_path): img1 = cv2.imread(img1_path) img2 = cv2.imread(img2_path) img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0])) # 转HSV空间,保留色彩与亮度信息,避免RGB空间的光照偏移干扰 hsv1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV) hsv2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV) # 计算H(色相)、S(饱和度)通道的联合直方图 hist1 = cv2.calcHist([hsv1], [0,1], None, [50,60], [0,180,0,256]) hist2 = cv2.calcHist([hsv2], [0,1], None, [50,60], [0,180,0,256]) # 直方图归一化到0-1区间 cv2.normalize(hist1, hist1, 0, 1, cv2.NORM_MINMAX) cv2.normalize(hist2, hist2, 0, 1, cv2.NORM_MINMAX) # 用巴氏距离计算匹配度,转换为0-1区间的得分 match_dist = cv2.compareHist(hist1, hist2, cv2.HISTCMP_BHATTACHARYYA) return round(1 - match_dist, 4)
- 局限性:仅关注全局色彩分布,完全不关注内容结构,两张毫无关联但色彩占比接近的图像,会被误判为高相似度。
3. 局部特征点匹配(适用:存在缩放、旋转、视角变化的通用图像对比)
这类方法是工程场景下最通用的鲁棒方案,对视角差、缩放、旋转、局部遮挡的抗干扰能力远高于前两类方法。
- 实现步骤:用特征检测器提取图像的局部特征点与描述子、对两张图的特征点做暴力匹配、筛选优质匹配对的占比作为相似度得分
- 核心代码:
def calc_feature_similarity(img1_path, img2_path): # 初始化ORB特征检测器,平衡检测速度与精度 orb = cv2.ORB_create(nfeatures=1000) # 读取灰度图减少计算量 img1 = cv2.imread(img1_path, 0) img2 = cv2.imread(img2_path, 0) # 提取特征点与特征描述子 kp1, des1 = orb.detectAndCompute(img1, None) kp2, des2 = orb.detectAndCompute(img2, None) # 初始化暴力匹配器,开启交叉校验过滤错误匹配 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1, des2) # 按匹配距离排序,优先保留距离更近的优质匹配对 matches = sorted(matches, key=lambda x: x.distance) # 计算前30%优质匹配对占总特征点的比例,作为相似度得分 good_ratio = len(matches[:int(len(matches)*0.3)]) / max(len(kp1), len(kp2)) return round(good_ratio, 4)
- 优化提示:如果需要更高的检测精度,可以将ORB替换为SIFT特征检测器,注意高版本OpenCV中SIFT模块需要安装opencv-contrib-python包才能正常调用,它对仿射形变的鲁棒性更强;如果两张图存在轻微的平移偏差,可以先用
cv2.findTransformECC做图像对齐,再计算相似度,能大幅降低误判率。
工程落地注意事项
- 不要直接套用网上的通用判定阈值,必须拿自身业务的样本库提前测试,比如像素法得分0.9以上判定为相似、特征法得分0.4以上判定为相似,避免不同算法的得分尺度差异导致误判
- 优先选计算量最低的匹配方案:固定机位的监控截图、静态页面对比,用纯像素法即可,没必要上特征匹配拖慢运行速度
- 对比前统一做图像预处理:包括去噪、统一亮度、裁剪无效边框,减少无关信息对相似度计算的干扰
内容的提问来源于stack exchange,提问作者RAFAEL ALEJANDRO OCHOA SANDOVA
相关产品推荐
相关产品推荐

