You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

图像二分后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()

检测输出结果:
SSIM检测输出结果

问题原因与解决方法

核心原因

  1. JPG有损压缩引入额外差异:分割后的图像保存为JPG格式时,有损压缩会丢失细节、引入噪声,导致重新读取的图像与原始分割数组存在偏差,拉低SSIM分数。
  2. SSIM参数未显式指定数据范围:计算SSIM时未指定data_range=255,虽多数情况能自动识别,但可能因动态范围判断误差导致分数不准确。
  3. 从图像内容看,左右两半本身存在较多实质性差异,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 15:28:07