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

基于OpenCV实现粘连反光物体分割的技术求助

粘连番茄检测分离的反光问题解决方案

问题背景

正在开发传送带场景下的单帧图像粘连番茄检测分离算法,遇到两个核心问题:

  • 使用OpenCV的findContours无法有效分割粘连番茄;
  • 采用distance-transform+Watershed算法时,番茄表面反光会破坏掩码完整性,导致算法失效。

问题示例

  • 原始图像:传送带场景中的红色番茄,部分番茄表面存在明显高光反光,且多个番茄相互粘连
  • 掩码图像:因反光影响,掩码中部分番茄区域出现黑色孔洞,无法完整覆盖番茄主体
  • 输入图像:典型的工业传送带番茄采集图像

核心优化方案

1. 改进掩码生成逻辑(针对反光的颜色过滤)

反光区域在HSV空间表现为高亮度、低饱和度,仅用饱和度通道过滤会遗漏反光区域的干扰,因此结合色调、饱和度、亮度三通道做精准过滤:

def create_mask(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = hsv[:, :, 0], hsv[:, :, 1], hsv[:, :, 2]
    # 红色番茄的色调范围(红色跨0-10和170-180两个区间)
    h_mask1 = cv2.inRange(h, 0, 10)
    h_mask2 = cv2.inRange(h, 170, 180)
    h_mask = cv2.bitwise_or(h_mask1, h_mask2)
    # 过滤低饱和度的反光区域
    s_mask = cv2.inRange(s, 30, 255)
    # 过滤过高亮度的反光区域
    v_mask = cv2.inRange(v, 20, 230)
    # 合并三通道掩码
    combined_mask = cv2.bitwise_and(h_mask, s_mask)
    combined_mask = cv2.bitwise_and(combined_mask, v_mask)
    # 形态学操作修复掩码孔洞
    kernel = np.ones((3, 3), np.uint8)
    combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel, iterations=2)
    combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel, iterations=1)
    return combined_mask

2. 预处理消除反光(CLAHE亮度增强)

使用限制对比度自适应直方图均衡(CLAHE)对亮度通道做局部增强,抑制高光区域的同时保留番茄主体细节:

def preprocess_anti_glare(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    v_channel = hsv[:, :, 2]
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced_v = clahe.apply(v_channel)
    hsv[:, :, 2] = enhanced_v
    return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

在主函数开头添加该预处理步骤,先消除反光再做后续处理。

3. 优化Watershed前景提取

调整距离变换的阈值,并对提取的前景做闭运算修复反光导致的孔洞:

# 生成前景后修复孔洞
sure_fg = np.uint8(sure_fg)
kernel = np.ones((3,3), np.uint8)
sure_fg = cv2.morphologyEx(sure_fg, cv2.MORPH_CLOSE, kernel, iterations=2)

同时可将距离变换的阈值从0.7*max调整为0.6*max,扩大前景区域的覆盖范围。

完整优化代码

import cv2
import numpy as np
import matplotlib.pyplot as plt

def display_image(img, title="Image"):
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.title(title)
    plt.axis('off')
    plt.show()

def preprocess_anti_glare(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    v_channel = hsv[:, :, 2]
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced_v = clahe.apply(v_channel)
    hsv[:, :, 2] = enhanced_v
    return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

def create_mask(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = hsv[:, :, 0], hsv[:, :, 1], hsv[:, :, 2]
    h_mask1 = cv2.inRange(h, 0, 10)
    h_mask2 = cv2.inRange(h, 170, 180)
    h_mask = cv2.bitwise_or(h_mask1, h_mask2)
    s_mask = cv2.inRange(s, 30, 255)
    v_mask = cv2.inRange(v, 20, 230)
    combined_mask = cv2.bitwise_and(h_mask, s_mask)
    combined_mask = cv2.bitwise_and(combined_mask, v_mask)
    kernel = np.ones((3, 3), np.uint8)
    combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel, iterations=2)
    combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel, iterations=1)
    return combined_mask

def apply_watershed(image_path):
    img = cv2.imread(image_path)
    img = preprocess_anti_glare(img)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    display_image(img, "Preprocessed Image")
    
    thresh = create_mask(img)
    display_image(thresh, "Optimized Mask Image")
    
    sure_bg = cv2.dilate(thresh, np.ones((5, 5), np.uint8), iterations=1)
    display_image(sure_bg, "Background Image")

    dist_transform = cv2.distanceTransform(thresh, cv2.DIST_L2, 5)
    _, sure_fg = cv2.threshold(dist_transform, 0.6 * dist_transform.max(), 255, 0)
    sure_fg = np.uint8(sure_fg)
    kernel = np.ones((3,3), np.uint8)
    sure_fg = cv2.morphologyEx(sure_fg, cv2.MORPH_CLOSE, kernel, iterations=2)
    display_image(sure_fg, "Optimized Foreground Image")

    unknown = cv2.subtract(sure_bg, sure_fg)

    _, markers = cv2.connectedComponents(sure_fg)
    markers = markers + 1
    markers[unknown == 255] = 0
    cv2.watershed(img, markers)
    img[markers == -1] = [0, 0, 255]
    display_image(img, "Watershed Result")

    cropped_objects = []
    for label in np.unique(markers):
        if label == 0 or label == 1: 
            continue
        mask = np.zeros(gray.shape, dtype="uint8")
        mask[markers == label] = 255
        cnts, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        for c in cnts:
            x, y, w, h = cv2.boundingRect(c)
            cropped_objects.append(img[y:y+h, x:x+w])

    for i, cropped in enumerate(cropped_objects):
        display_image(cropped, f"Cropped Tomato {i + 1}")

    return cropped_objects

apply_watershed('T4.jpg')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:49:51