白色颗粒计数与直径测算:现有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,提问作者Дмитрий
相关产品推荐
相关产品推荐

