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

如何使用OpenCV准确分割图像中重叠的水稻籽粒

如何使用OpenCV准确分割图像中重叠的水稻籽粒

我太懂你这种困扰了——处理重叠的水稻籽粒分割确实是个棘手的活儿,分水岭算法看似是标配,但遇到紧密挤在一起的颗粒时,很容易出现边界识别不准的情况。结合你给出的代码和当前的效果,我整理了几个实用的调整方向和补充技巧,应该能帮你提升分割精度:

一、优化背景与前景的初始分割

你目前用HSV阈值抠除蓝色背景的思路没问题,但可以再细化一下,让前景(水稻籽粒)的轮廓更清晰:

  • 调整HSV阈值范围:有时候固定的HSV区间可能会因为光照、拍摄角度漏掉部分籽粒,你可以用cv2.createTrackbar做一个交互式的阈值调整工具,找到最适合当前图像的lower_blue和upper_blue范围,确保所有籽粒都被包含在mask_grains里。
  • 结合Otsu阈值二次优化:在得到mask_grains后,可以再对灰度图做一次Otsu阈值分割,过滤掉籽粒上的杂色或阴影,让前景更纯净:
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, otsu_mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    mask_grains = cv2.bitwise_and(mask_grains, otsu_mask)
    

二、调整分水岭算法的核心参数

分水岭的效果很大程度上取决于sure_fg(确定前景)和sure_bg(确定背景)的准确性,你可以尝试这些调整:

  • 优化距离变换与前景阈值:你当前用0.3 * dist_transform.max()作为前景阈值,这个值偏保守,容易把重叠区域的前景也归为未知区域。可以尝试提高这个比例到0.5~0.7,或者根据距离变换的直方图来动态确定阈值,确保每个独立籽粒的中心都被标记为确定前景。
  • 调整形态学操作的核与次数:你用的是3x3的椭圆核,可以试试更大的核(比如5x5)或者矩形核,针对不同的重叠程度调整膨胀/腐蚀的次数。比如在生成sure_bg时,膨胀次数可以减少到2次,避免背景过度扩张吃掉部分籽粒边缘;而sure_fg的形态学操作可以增加一次腐蚀,让前景标记更精准。
  • 改进未知区域的计算:可以尝试用cv2.morphologyEx对sure_fg做一次细化(多次cv2.MORPH_ERODE),再计算未知区域,这样能让分水岭的边界更贴合籽粒实际轮廓。

三、尝试互补的分割方法

如果单纯调整分水岭效果有限,可以结合其他算法来增强:

  • 基于轮廓的标记补充:在得到sure_fg后,用cv2.findContours找到所有前景轮廓,然后对每个轮廓内的区域计算距离变换的局部最大值,把这些最大值点作为额外的标记点,添加到markers中,这样能帮助分水岭识别出重叠籽粒的边界。
  • 尝试U-Net等深度学习模型:如果你的数据集足够大,训练一个轻量级的语义分割模型(比如U-Net)会比传统算法更擅长处理重叠物体的分割,尤其是复杂场景下的籽粒边界识别。

四、后处理优化分割结果

不管用哪种算法,后处理都能帮你修正小问题:

  • 过滤小噪声区域:用cv2.contourArea计算每个分割区域的面积,过滤掉面积远小于正常籽粒的噪声区域。
  • 边界平滑:对分割后的轮廓用cv2.approxPolyDP做多边形近似,或者用cv2.GaussianBlur模糊后再提取轮廓,让边界更平滑自然。

调整后的参考代码

这里给你修改了部分关键参数的代码版本,你可以试试:

import cv2
import numpy as np

image = cv2.imread('images/155.png')

# 优化背景分割:交互式调整HSV阈值(可选,先手动找到合适范围再固定)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower_blue = np.array([105, 40, 40])
upper_blue = np.array([135, 255, 255])
mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
mask_grains = cv2.bitwise_not(mask_blue)

# 结合Otsu阈值优化前景
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, otsu_mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
mask_grains = cv2.bitwise_and(mask_grains, otsu_mask)

# 形态学操作:调整核大小与迭代次数
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
mask_grains = cv2.morphologyEx(mask_grains, cv2.MORPH_OPEN, kernel, iterations=2)

# 距离变换与前景阈值调整
sure_bg = cv2.dilate(mask_grains, kernel, iterations=2)
dist_transform = cv2.distanceTransform(mask_grains, cv2.DIST_L2, 5)
# 提高前景阈值比例
_, sure_fg = cv2.threshold(dist_transform, 0.6 * dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
# 增加一次腐蚀让前景标记更精准
sure_fg = cv2.erode(sure_fg, kernel, iterations=1)
unknown = cv2.subtract(sure_bg, sure_fg)

# 连通分量与分水岭
num_markers, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0
markers = cv2.watershed(image, markers)
image[markers == -1] = [0, 0, 255]  # 标记分水岭边界

# 后处理:过滤小区域
contours, _ = cv2.findContours(cv2.bitwise_not(mask_blue), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    if cv2.contourArea(cnt) < 100:  # 根据实际籽粒大小调整阈值
        cv2.drawContours(image, [cnt], 0, (255, 255, 255), -1)

cv2.imshow('segment', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

备注:内容来源于stack exchange,提问作者mj01

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:27:57