图像二分后SSIM相似度异常偏低的问题排查与解决咨询
图像分割与SSIM相似度异常问题解决
问题描述
作业任务要求将图像分为两半并找出二者差异,已通过代码将图像分割为尺寸相同的左右两部分,但使用SSIM检测差异时图像相似度极低,需了解分割图像时如何避免该问题。

分割代码
import cv2 def crop_into_two_equal_parts(image): if image is None: raise ValueError("Failed to load image.") # 获取当前图像尺寸 height, width = image.shape[:2] # 计算宽度方向的分割点 split_point = width // 2 # 将图像分割为均等的两部分 left_image = image[:, :split_point] right_image = image[:, split_point:] return left_image, right_image # 图像文件路径 image_path = 'C:/Users/HP ZBOOK 15 G3/Desktop/FE_CPV/drive-download-20230629T090921Z-001/1.png' # 从文件读取图像 image = cv2.imread(image_path) try: # 将图像分割为均等的两部分 left_image, right_image = crop_into_two_equal_parts(image) # 显示原始图像及分割后的两幅图像 cv2.imshow("Original Image", image) cv2.imshow("Left Image", left_image) cv2.imshow("Right Image", right_image) cv2.waitKey(0) cv2.destroyAllWindows() left_image_path = 'left_image.jpg' right_image_path = 'right_image.jpg' cv2.imwrite(left_image_path, left_image) cv2.imwrite(right_image_path, right_image) except ValueError as e: print("Error:", str(e))
分割后的图像:

SSIM检测代码
from skimage.metrics import structural_similarity import cv2 import numpy as np # 加载图像 before = cv2.imread('left_image.jpg') after = cv2.imread('right_image.jpg') # 将图像转换为灰度图 before_gray = cv2.cvtColor(before, cv2.COLOR_BGR2GRAY) after_gray = cv2.cvtColor(after, cv2.COLOR_BGR2GRAY) # 计算两幅图像的SSIM值 (score, diff) = structural_similarity(before_gray, after_gray, full=True) print("Image Similarity: {:.4f}%".format(score * 100)) # diff图像包含两幅图像的实际差异,以[0,1]范围的浮点型数据表示 # 因此必须将数组转换为[0,255]范围的8位无符号整数才能用于OpenCV diff = (diff * 255).astype("uint8") diff_box = cv2.merge([diff, diff, diff]) # 对差异图像进行阈值处理,然后查找轮廓以获取两幅输入图像的差异区域 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(before.shape, dtype='uint8') filled_after = after.copy() for c in contours: area = cv2.contourArea(c) if area > 40: x,y,w,h = cv2.boundingRect(c) cv2.rectangle(before, (x, y), (x + w, y + h), (36,255,12), 2) cv2.rectangle(after, (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_after, [c], 0, (0,255,0), -1) cv2.imshow('before', before) cv2.imshow('after', after) cv2.imshow('diff', diff) cv2.imshow('diff_box', diff_box) cv2.imshow('mask', mask) cv2.imshow('filled after', filled_after) cv2.waitKey()
检测输出结果:
问题原因与解决方法
核心原因
- JPG有损压缩引入额外差异:分割后的图像保存为JPG格式时,有损压缩会丢失细节、引入噪声,导致重新读取的图像与原始分割数组存在偏差,拉低SSIM分数。
- SSIM参数未显式指定数据范围:计算SSIM时未指定
data_range=255,虽多数情况能自动识别,但可能因动态范围判断误差导致分数不准确。 - 从图像内容看,左右两半本身存在较多实质性差异,SSIM分数低本身是真实差异的反映,但上述两点会加剧分数偏低的情况。
解决方法
- 跳过中间图像保存步骤:直接使用分割后的numpy数组(
left_image和right_image)计算SSIM,消除压缩误差。 - 显式指定SSIM数据范围:调用
structural_similarity时添加data_range=255参数,确保计算精度。 - 若需保存图像,改用PNG格式:PNG为无损压缩,不会引入额外图像差异。
修改后的示例代码
from skimage.metrics import structural_similarity import cv2 import numpy as np # 图像文件路径 image_path = 'C:/Users/HP ZBOOK 15 G3/Desktop/FE_CPV/drive-download-20230629T090921Z-001/1.png' image = cv2.imread(image_path) def crop_into_two_equal_parts(image): if image is None: raise ValueError("Failed to load image.") height, width = image.shape[:2] split_point = width // 2 left_image = image[:, :split_point] right_image = image[:, split_point:] return left_image, right_image # 分割图像 left_image, right_image = crop_into_two_equal_parts(image) # 直接使用分割后的数组计算SSIM before_gray = cv2.cvtColor(left_image, cv2.COLOR_BGR2GRAY) after_gray = cv2.cvtColor(right_image, cv2.COLOR_BGR2GRAY) # 显式指定data_range=255 (score, diff) = structural_similarity(before_gray, after_gray, full=True, data_range=255) print("Image Similarity: {:.4f}%".format(score * 100)) # 后续差异可视化处理 diff = (diff * 255).astype("uint8") diff_box = cv2.merge([diff, diff, diff]) 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(left_image.shape, dtype='uint8') filled_after = right_image.copy() for c in contours: area = cv2.contourArea(c) if area > 40: x,y,w,h = cv2.boundingRect(c) cv2.rectangle(left_image, (x, y), (x + w, y + h), (36,255,12), 2) cv2.rectangle(right_image, (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_after, [c], 0, (0,255,0), -1) # 显示结果 cv2.imshow('Left Image', left_image) cv2.imshow('Right Image', right_image) cv2.imshow('diff', diff) cv2.imshow('diff_box', diff_box) cv2.imshow('mask', mask) cv2.imshow('filled after', filled_after) cv2.waitKey(0) cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者Peter Basaw
相关产品推荐
相关产品推荐

