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

OpenCV花卉分割IoU评估:图像边界触达时得分异常问题求解

解决花卉分割IoU评估边界触达图像边缘时得分异常问题

我用OpenCV Python构建图像处理流水线,从植物图像数据集分割花卉,将生成图像和真值图像做IoU评估时遇到问题:当真值图像中花卉边缘触达图像边界时,评估结果仅得到1.58%的相似度得分,而边缘未触达边界的图像得分均>90%。

相关评估代码如下:

# Function to find the largest contour which is assumed to be the flower
def find_largest_contour(binary_image):
    # Find contours from the binary image
    contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    if contours:
        return max(contours, key=cv2.contourArea)
    else:
        return None

# Function to calculate the Intersection over Union
def calculate_iou(contourA, contourB, shape):
    maskA = np.zeros(shape, dtype=np.uint8)
    maskB = np.zeros(shape, dtype=np.uint8)
    cv2.drawContours(maskA, [contourA], -1, color=255, thickness=cv2.FILLED)
    cv2.drawContours(maskB, [contourB], -1, color=255, thickness=cv2.FILLED)
    intersection = np.logical_and(maskA, maskB)
    union = np.logical_or(maskA, maskB)
    iou_score = np.sum(intersection) / np.sum(union)
    return iou_score

# Function to display similarity percentage based on IoU
def display_similarity(image_name, iou_score):
    similarity_percentage = round(iou_score, 2)
    print(f"Similarity for {image_name}: {similarity_percentage}%")

# Apply the processing and calculate IoU for each image
ious = []
for input_path, ground_truth_path in zip(image_paths, ground_truth_image_paths):
    image_name = os.path.basename(input_path)
    original_image = cv2.imread(input_path)  # Read the original image again for visualization
    processed_image = process_image(input_path, image_name)
    show_binary_image(processed_image, window_name=f"Binary: {image_name}")
    ground_truth_image = cv2.imread(ground_truth_path, cv2.IMREAD_GRAYSCALE)
    show_binary_image(ground_truth_image, window_name=f"Binary: {image_name}")

    if processed_image.shape != ground_truth_image.shape:
        ground_truth_image = cv2.resize(ground_truth_image, (processed_image.shape[1], processed_image.shape[0]))

    # Find largest contours
    contour_processed = find_largest_contour(processed_image)
    contours_ground_truth = process_red_edges(ground_truth_path)  # Fix: Pass path instead of image

# Find the largest contour among the contours found
contour_ground_truth = max(contours_ground_truth, key=cv2.contourArea)

# Calculate IoU
iou_score = calculate_iou(contour_processed, contour_ground_truth, ground_truth_image.shape) * 100
ious.append(iou_score)

流水线生成的待对比图像:
待对比分割结果

真值图像:
真值图像


问题根源分析

从代码和图像来看,核心问题出在轮廓提取环节:

  • 当花卉边缘触达图像边界时,cv2.findContours使用cv2.RETR_EXTERNAL模式提取外部轮廓,会因为图像边缘的截断,无法生成完整的闭合轮廓,反而将其拆分成多个分散的小轮廓。
  • 代码中仅取最大的轮廓进行IoU计算,而真值图像的process_red_edges函数同样会因为边界问题提取出不匹配的轮廓,最终导致两个轮廓几乎无交集,IoU得分骤降。

解决方案

方案1:修复边界轮廓提取逻辑

通过给二值图像添加一圈黑边框,让触达原边界的花卉形成完整闭合轮廓,再修正轮廓坐标偏移:

def find_largest_contour(binary_image):
    # 添加1像素黑边框,让边界处的轮廓闭合
    bordered_image = cv2.copyMakeBorder(binary_image, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0)
    # 提取轮廓
    contours, _ = cv2.findContours(bordered_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    if contours:
        # 找到最大轮廓后,减去之前添加的边框偏移量
        largest_contour = max(contours, key=cv2.contourArea)
        largest_contour = largest_contour - [1, 1]  # 修正坐标,还原到原图像坐标系
        return largest_contour
    else:
        return None

同时需要对process_red_edges函数做相同的边框处理,确保真值图像的轮廓提取也能得到完整的花卉轮廓。

方案2:直接基于掩码计算IoU(推荐)

绕过轮廓提取环节,直接用二值掩码计算IoU,这是分割任务中IoU的标准计算方式,更稳定可靠:

def calculate_iou_from_masks(maskA, maskB):
    # 确保两个掩码尺寸一致
    if maskA.shape != maskB.shape:
        maskB = cv2.resize(maskB, (maskA.shape[1], maskA.shape[0]))
    # 二值化处理,确保掩码为0/255或0/1格式
    maskA = (maskA > 127).astype(np.uint8)
    maskB = (maskB > 127).astype(np.uint8)
    # 计算交集和并集
    intersection = np.logical_and(maskA, maskB).sum()
    union = np.logical_or(maskA, maskB).sum()
    # 避免除以0的情况
    if union == 0:
        return 0.0
    return intersection / union

在主循环中替换原轮廓相关的IoU计算逻辑:

# 替换原轮廓提取和IoU计算部分
# 直接用二值图计算IoU
iou_score = calculate_iou_from_masks(processed_image, ground_truth_image) * 100
ious.append(iou_score)

内容的提问来源于stack exchange,提问作者user19766923

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:43:21