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

白色颗粒计数与直径测算:现有OpenCV实现方案的优化问询

白色颗粒精准计数与直径测算优化方案

我正尝试对下图中的白色颗粒进行计数并测算其直径:
不同尺寸的白色颗粒组合

以下是我目前尝试的两段OpenCV实现代码,但都不够精准,寻求更优解决方案:

现有实现方案

1. 轮廓检测法

通过边缘检测提取轮廓来计数,但容易误判噪点或把粘连颗粒算成一个:

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

image = cv2.imread(r'D:\Downloads\IMG_0009.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (11, 11), 0)
canny = cv2.Canny(blur, 10, 150)
dilated = cv2.dilate(canny, (1, 1), iterations=2)
(cnt, _) = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(image, cnt, -1, (0, 0, 255), 2)
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
print('颗粒数量:', len(cnt))

2. 霍夫圆检测法

仅能识别接近完美圆形的颗粒,对不规则颗粒漏检严重,参数调整难度大:

import cv2
import numpy as np

image = cv2.imread('D:\Downloads\IMG_0009.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)

circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, dp=1.30, minDist=30,
                            param1=50, param2=30, minRadius=5, maxRadius=50)

if circles is not None:
    circles = np.round(circles[0, :]).astype("int")
    for (x, y, r) in circles:
        cv2.circle(image, (x, y), r, (0, 255, 0), 4)
        cv2.rectangle(image, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
    cv2.imwrite('D:\Downloads\detected_circles.jpg', image)
    print("检测结果已保存")
else:
    print("未检测到圆形颗粒")

优化解决方案

针对颗粒可能存在的粘连、不规则形态、噪点干扰问题,采用预处理+轮廓筛选+等效直径计算+粘连分割的组合方案:

完整优化代码

import cv2
import numpy as np

def process_particles(image_path):
    # 1. 读取图像并预处理
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 自适应阈值分割,应对光照不均
    thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                   cv2.THRESH_BINARY_INV, 11, 2)
    
    # 形态学操作:开运算去噪点,闭运算填补颗粒内部孔洞
    kernel_open = np.ones((3,3), np.uint8)
    kernel_close = np.ones((5,5), np.uint8)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel_open, iterations=1)
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel_close, iterations=2)
    
    # 2. 粘连颗粒分割(距离变换+分水岭算法)
    dist_transform = cv2.distanceTransform(thresh, cv2.DIST_L2, 5)
    ret, sure_fg = cv2.threshold(dist_transform, 0.5*dist_transform.max(), 255, 0)
    sure_fg = np.uint8(sure_fg)
    
    sure_bg = cv2.dilate(thresh, kernel_close, iterations=3)
    unknown = cv2.subtract(sure_bg, sure_fg)
    
    # 标记连通区域
    ret, markers = cv2.connectedComponents(sure_fg)
    markers += 1
    markers[unknown==255] = 0
    
    # 分水岭分割
    markers = cv2.watershed(image, markers)
    image[markers == -1] = [255,0,0]  # 分割线标记为蓝色
    
    # 3. 提取有效轮廓并筛选
    contours, _ = cv2.findContours(sure_fg.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 过滤过小的噪点轮廓,根据实际颗粒尺寸调整最小面积阈值
    min_area = 100  # 可根据图像分辨率调整
    valid_contours = []
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > min_area:
            valid_contours.append(cnt)
    
    # 4. 计算每个颗粒的等效直径(基于面积的圆形等效直径)
    particle_diameters = []
    for cnt in valid_contours:
        area = cv2.contourArea(cnt)
        # 等效直径:d = 2*sqrt(area/π)
        diameter = 2 * np.sqrt(area / np.pi)
        particle_diameters.append(diameter)
        
        # 在图像上绘制轮廓和直径信息
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(image, (x,y), (x+w,y+h), (0,255,0), 2)
        cv2.putText(image, f"{diameter:.1f}", (x, y-5), 
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
    
    # 输出结果
    print(f"检测到的有效颗粒数量:{len(valid_contours)}")
    print(f"颗粒直径列表(像素):{[round(d,1) for d in particle_diameters]}")
    
    # 显示结果
    cv2.imshow('Processed Particles', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    return len(valid_contours), particle_diameters

# 调用函数
process_particles(r'D:\Downloads\IMG_0009.jpg')

方案优势

  • 自适应阈值:解决光照不均导致的分割不准确问题
  • 形态学操作:有效去除噪点并填补颗粒内部孔洞
  • 分水岭分割:精准分割粘连在一起的颗粒
  • 轮廓筛选:通过面积过滤误判的小噪点
  • 等效直径计算:基于颗粒面积计算等效圆形直径,适配不规则形态的颗粒

注意事项

  • min_area阈值需要根据实际图像的分辨率和颗粒大小调整
  • 如果颗粒颜色不是纯白色,可调整自适应阈值的THRESH_BINARY_INV为THRESH_BINARY
  • 若需要真实物理尺寸,需通过标定物(如已知尺寸的参照物)将像素直径转换为实际长度

内容的提问来源于stack exchange,提问作者Дмитрий

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 07:17:10