如何用Python检测并对比两张图像中的点状目标?
问题描述
两名儿童在尺寸相近的白纸上绘制了类似点的物体,需要完成以下任务:
- 检测图像中的点目标
- 统计两张图中处于同一X、Y轴位置的目标数量
- 度量两张图的一致性
尝试过SSIM等相似性方法,但针对这类小目标效果不佳;当前用OpenCV编写的代码会把多个点归为一个轮廓,无法实现每个点单独生成轮廓的需求。
输入图像(缩放版本)


当前使用的代码
from skimage.metrics import structural_similarity import numpy as np import cv2 image1=cv2.imread("E:/image1.png") image2=cv2.imread("E:/image2.png") image1gray=cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY) image2gray=cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY) # Compute SSIM between the two images (score, diff) = structural_similarity(image1gray, image2gray, full=True) print("Image Similarity: {:.4f}%".format(score * 100)) # The diff image contains the actual image differences between the two images # and is represented as a floating point data type in the range [0,1] # so we must convert the array to 8-bit unsigned integers in the range # [0,255] before we can use it with OpenCV diff = (diff * 255).astype("uint8") diff_box = cv2.merge([diff, diff, diff]) # Threshold the difference image, followed by finding contours to # obtain the regions of the two input images that differ thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1] contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len(contours) == 2 else contours[1] mask = np.zeros(image1.shape, dtype='uint8') filled_image2 = image2.copy() filled_image1=image1.copy() for c in contours: area = cv2.contourArea(c) if area > 40: x,y,w,h = cv2.boundingRect(c) cv2.rectangle(image1, (x, y), (x + w, y + h), (36,255,12), 2) cv2.rectangle(image2, (x, y), (x + w, y + h), (36,255,12), 2) cv2.rectangle(diff_box, (x, y), (x + w, y + h), (36,255,12), 2) cv2.drawContours(mask, [c], 0, (255,255,255), -1) cv2.drawContours(filled_image2, [c], 0, (0,255,0), -1) cv2.drawContours(filled_image1, [c], 0, (0,255,0), -1) cv2.imwrite('E:/Outout/image11.png', image1) cv2.imwrite('E:/Outout/image22.png', image2) cv2.imwrite('E:/Outout/diff_img.png', diff) cv2.imwrite('E:/Outout/diff_box.png', diff_box) cv2.imwrite('E:/Outout/mask.png', mask) cv2.imwrite('E:/Outout/filled image2.png', filled_image2)
当前输出结果(不准确)






解决方案
不需要用机器学习,用OpenCV的基础图像处理就能解决,核心思路是先分别检测两张图里的每个点,再匹配坐标,而非先做全局差异对比。
步骤1:单独检测每张图的点目标
针对单张图,先做二值化,再用轮廓检测提取每个点,通过调整轮廓面积阈值确保每个点单独成轮廓:
import cv2 import numpy as np def detect_points(image_path): img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化:白底黑点,用反相阈值 _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV) # 提取最外层轮廓,简化轮廓点 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) points = [] for cnt in contours: area = cv2.contourArea(cnt) # 根据点的实际大小调整阈值(示例为10-50像素) if 10 < area < 50: # 计算轮廓中心作为点的坐标 M = cv2.moments(cnt) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) points.append((cx, cy)) # 可视化标记点 cv2.circle(img, (cx, cy), 3, (0, 255, 0), -1) return points, img # 检测两张图的点 points1, img1_marked = detect_points("E:/image1.png") points2, img2_marked = detect_points("E:/image2.png") # 保存标记后的图像 cv2.imwrite("E:/Outout/marked_image1.png", img1_marked) cv2.imwrite("E:/Outout/marked_image2.png", img2_marked)
步骤2:匹配同位置的点
考虑到纸张可能存在微小偏移,设置距离阈值(如5像素),通过欧氏距离判断点是否处于同一位置:
def match_points(points_a, points_b, threshold=5): matched_count = 0 unmatched_a = [] unmatched_b = [] # 标记已匹配的点 matched_b = [False]*len(points_b) for pa in points_a: found = False for idx, pb in enumerate(points_b): if not matched_b[idx]: distance = np.sqrt((pa[0]-pb[0])**2 + (pa[1]-pb[1])**2) if distance < threshold: matched_count +=1 matched_b[idx] = True found = True break if not found: unmatched_a.append(pa) # 收集图2中未匹配的点 for idx, pb in enumerate(points_b): if not matched_b[idx]: unmatched_b.append(pb) return matched_count, unmatched_a, unmatched_b # 执行匹配 matched_num, unmatched1, unmatched2 = match_points(points1, points2)
步骤3:计算一致性指标
用以下指标量化两张图的一致性:
- 匹配率:匹配点数量 / 两张图中较小的总点数
- Jaccard系数:匹配点数量 / (图1总点数 + 图2总点数 - 匹配点数量)
示例计算代码:
match_rate = matched_num / min(len(points1), len(points2)) jaccard = matched_num / (len(points1) + len(points2) - matched_num) print(f"匹配的点数量:{matched_num}") print(f"图1未匹配点数量:{len(unmatched1)}") print(f"图2未匹配点数量:{len(unmatched2)}") print(f"匹配率:{match_rate:.2%}") print(f"Jaccard系数:{jaccard:.2%}")
原方法失效原因
之前用SSIM生成差异图再找轮廓,会把邻近的差异区域合并成大轮廓——因为SSIM基于局部区域计算差异,邻近点的差异会连在一起。而先单独检测每张图的点再匹配坐标,能精准定位每个点,避免轮廓合并问题。
内容的提问来源于stack exchange,提问作者diana
相关产品推荐
相关产品推荐

