使用OpenCV识别图片相似度评分的应用技术博客
一、架构设计
本应用采用Python语言,基于OpenCV库实现。整体架构包括以下几个模块:
图像读取与预处理模块:
- 读取输入的两张图像。
- 将图像转换为灰度图(
cv2.cvtColor()
)。 - 图像尺寸标准化(
cv2.resize()
),确保两图大小一致。
特征提取模块:
- 使用OpenCV中的特征检测算法(如SIFT, SURF, ORB等)提取关键点和描述符。
- 匹配两幅图像的关键点(通过BFMatcher或FLANN匹配器)。
相似度计算模块:
- 根据匹配结果,计算相似度评分。
- 可以使用匹配点数、距离比值测试(Lowe’s Ratio Test)等方法优化匹配精度。
可视化与输出模块:
- 显示匹配的关键点(
cv2.drawMatches()
)。 - 输出最终的相似度评分。
- 显示匹配的关键点(
用户接口模块:
- 提供命令行参数或图形界面供用户输入图像路径和选择算法。
示例代码结构:
【similarity.py】
import cv2
import numpy as np
# 返回的相似度评分是通过 cv2.compareHist 方法计算两张图像在指定颜色空间(如HSV或Lab)下的直方图相关性(Correlation),
# 其值范围通常在[0, 1]之间。以下是对该评分的详细解读:
#
# ✅ 相似度评分含义
# 评分范围
# 含义说明
# 0.9 - 1.0非常相似,颜色分布高度一致,可能是同一舌头或拍摄条件相近的照片。
# 0.7 - 0.9较为相似,颜色特征接近,可能存在轻微光照、角度或拍摄设备差异。
# 0.5 - 0.7部分相似,颜色分布有一定差异,可能舌苔颜色有变化或存在轻微遮挡。
# 0.3 - 0.5不太相似,颜色特征已有明显不同,可能为不同人或舌象差异较大。
# 0.0 - 0.3极不相似,颜色分布差异显著,基本可判断为完全不同的舌头或图像内容。
#
# ⚠️
# 注意事项
# 该评分仅反映颜色分布的相似度,并不考虑形状、纹理等其他视觉特征。
# 若图像中舌头的颜色均匀但亮度差异大,建议使用HSV空间并忽略V通道,
# 或使用Lab空间以减少亮度影响。
# 若你用于医学舌诊分析,建议结合其他特征(如舌苔厚度、裂纹、齿痕等)进行综合判断。
def preprocess_tongue_image(image_path, target_size=(256, 256)):
image = cv2.imread(image_path)
# image = cv2.resize(image, target_size)
return image
def get_color_histogram(image, color_space='hsv', bins=64):
if color_space == 'hsv':
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 只取 H 和 S 通道,忽略 V(亮度)
hist = cv2.calcHist([image], [0, 1], None, [8, 8], [0, 180, 0, 256])
elif color_space == 'lab':
image = cv2.cvtColor(image, cv2.COLOR_BGR2Lab)
# 取 a 和 b 通道
hist = cv2.calcHist([image], [1, 2], None, [8, 8], [0, 256, 0, 256])
else:
raise ValueError("Unsupported color space")
cv2.normalize(hist, hist).flatten()
return hist
def compare_tongue_color_similarity(image1_path, image2_path, color_space='hsv'):
img1 = preprocess_tongue_image(image1_path)
img2 = preprocess_tongue_image(image2_path)
hist1 = get_color_histogram(img1, color_space)
hist2 = get_color_histogram(img2, color_space)
similarity = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
return similarity
【main.py】
from match.imgresize import extract_and_resize_car
from match.similarity import compare_tongue_color_similarity
import os
import cv2
import numpy as np
def main():
# 构建图片的相对路径或绝对路径
# img1_path = '/Users/franks/项目资料/99 个人/壁纸/20231206-100802.jpeg'
# img2_path = '/Users/franks/项目资料/99 个人/壁纸/20231206-100812.jpeg'
#
# similarity_score = compare_images_sift(img1_path, img2_path)
# if similarity_score is not None:
# print(f"Similarity score: {similarity_score:.2f}")
# else:
# print("无法计算相似度,请检查输入图片内容。")
# 示例调用
# input_image = '/Users/franks/Downloads/2.jpeg'
# output_image = '/Users/franks/Downloads/resized-2.png'
# extract_and_resize_car(input_image,output_image)
# /Users/franks/workspace/AI/ModelTrain/data/images/val/1_0.jpg
image1_path = '/Users/franks/workspace/AI/ModelTrain/data/images/val/3_0.jpg'
image2_path = '/Users/franks/workspace/AI/ModelTrain/data/images/val/1_0.jpg'
similarity_score = compare_tongue_color_similarity(image1_path, image2_path, color_space='hsv')
print(f"舌头颜色相似度评分: {similarity_score}")
if __name__ == "__main__":
main()
【imgresize.py】
import numpy as np
from ultralytics import YOLO
import cv2
# 计算缩放比例并保持宽高比
def resize_with_transparent_padding(image, target_size=(256, 256)):
old_h, old_w = image.shape[:2]
target_w, target_h = target_size
# 计算缩放比例
ratio = min(target_w / old_w, target_h / old_h)
new_w = int(old_w * ratio)
new_h = int(old_h * ratio)
# 缩放图像
resized_img = cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)
# 转换为带透明通道的 BGRA 格式
if resized_img.ndim == 2:
resized_img = cv2.cvtColor(resized_img, cv2.COLOR_GRAY2BGR) # 灰度图转 BGR
resized_img = cv2.cvtColor(resized_img, cv2.COLOR_BGR2BGRA)
# 创建透明画布(全透明:alpha=0)
canvas = np.zeros((target_h, target_w, 4), dtype=np.uint8)
# 居中放置缩放后的图像
x_offset = (target_w - new_w) // 2
y_offset = (target_h - new_h) // 2
canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w, :3] = resized_img[:, :, :3] # 拷贝 RGB
canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w, 3] = 255 # 设置 Alpha 为不透明
return canvas
def extract_and_resize_car(image_path, output_path,output_size=(256, 256)):
# 加载预训练的 YOLOv8 模型
# model = YOLO('../models/yolov8s.pt') # 自动下载模型权重(首次运行)
model = YOLO('../models/yolov8_model_tongue.pt') # 自动下载模型权重(首次运行)
# 推理
results = model(image_path)
# 读取原始图像
image = cv2.imread(image_path)
for result in results:
boxes = result.boxes.cpu().numpy()
for box in boxes:
cls_id = int(box.cls[0])
if model.names[cls_id] == 'tongue': # 只提取类别为 tongue 的对象
x1, y1, x2, y2 = map(int, box.xyxy[0]) # 获取边界框坐标
# 裁剪车辆区域
car_crop = image[y1:y2, x1:x2]
# 缩放为固定尺寸
# resized_car = cv2.resize(car_crop, output_size, interpolation=cv2.INTER_AREA)
resized_car = resize_with_transparent_padding(car_crop, target_size=output_size)
# 保存结果
cv2.imwrite(output_path, resized_car)
print(f"舌头已裁剪并保存为 {output_path}")
return resized_car
print("未检测到舌头。")
return None
二、实现原理
1. 特征提取
OpenCV提供多种特征检测算法,其中SIFT(Scale-Invariant Feature Transform)和SURF(Speeded-Up Robust Features)是较为常用的两种方法。它们能够从图像中提取具有尺度不变性和旋转不变性的关键点及其对应的描述符,用于后续的匹配操作。
cv2.SIFT_create()
:创建SIFT对象,用于检测关键点并生成描述符。detectAndCompute()
:返回图像的关键点(keypoints)和描述符(descriptors)。
2. 特征匹配
特征匹配主要依赖于描述符之间的欧氏距离。OpenCV提供了两种主要的匹配器:
- Brute-Force Matcher (
cv2.BFMatcher
)- 直接比较每一对描述符的距离。
- 设置
crossCheck=True
可提高匹配准确性。
- FLANN-Based Matcher (
cv2.FlannBasedMatcher
)- 基于近似最近邻搜索,速度更快,适合大规模数据集。
3. 相似度评分
在匹配完成后,根据匹配点数量进行评分。这里采用了简单的匹配比例评分法:
score = len(good_matches) / len(matches) * 100
也可以结合Lowe’s Ratio Test来筛选更可靠的匹配点:
good_matches = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good_matches.append(m)
4. 可视化
利用cv2.drawMatches()
函数将两幅图像的关键点匹配结果显示出来,帮助用户直观地理解图像相似性。
三、使用效果分析
1. 准确度评估
- 对于内容相似的图像(如同一场景的不同拍摄角度、不同光照条件下的照片),该算法可以达到较高的相似度评分(>90%)。
- 对于内容差异较大的图像(如完全不同的物体或背景),评分较低(<30%)。
- 由于基于特征点匹配,对于图像缩放、轻微旋转等变化具有较好的鲁棒性。
2. 性能表现
- SIFT/SURF算法在CPU上运行效率较高,但对高分辨率图像可能较慢。
- 使用GPU加速(如OpenCV的CUDA模块)或降低图像分辨率可以提升性能。
- 在实际部署中,建议对图像进行标准化处理(统一尺寸)以减少计算开销。
3. 局限性
- 纹理缺失问题:若图像过于模糊或缺乏纹理细节,特征点提取会受限,导致匹配失败。
- 光照影响:强烈光照变化可能导致描述符失真,从而影响匹配质量。
- 视角变换敏感:若图像视角变化较大,即使为相同物体,也可能导致低评分。
四、应用场景
- 图像检索系统:基于特征匹配进行图像相似度排序,适用于商品识别、版权监测等。
- 重复图像检测:可用于社交媒体平台检测重复上传的内容。
- 文档扫描验证:对比扫描件与原始文件,判断是否一致。
- 视频帧比对:在视频摘要生成或去重任务中,比较相邻帧的相似度。
五、总结
本文介绍了基于OpenCV的图像相似度评分应用,从架构设计到具体实现进行了详细说明,并分析了其适用性及局限性。该方法适用于大多数基于局部特征匹配的图像相似度判断任务,在实际工程中可根据需求进一步优化算法(如引入深度学习模型提取更高级特征)以提升准确率和鲁棒性。