基于OpenCV的重叠蠕虫图像计数问题求助
解决OpenCV中重叠蠕虫的计数问题
嘿,我很熟悉这种重叠目标计数的痛点,你用腐蚀没效果太正常了——毕竟腐蚀是整体缩小前景,碰到连接区域大的情况,还没分开蠕虫呢,蠕虫本身都被磨没了。给你推荐几个针对性的解决方案,尤其是分水岭算法,处理这种场景特别靠谱:
方案一:距离变换+分水岭算法(推荐)
这是处理重叠物体分割的经典方法,核心思路是先找到每个蠕虫的“核心区域”作为种子,再让算法从种子开始“填充”,自动分隔开重叠的部分。
完整代码示例
import cv2 as cv import numpy as np # 读取图像并预处理 img = cv.imread('worms.jpg') gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) blur = cv.GaussianBlur(gray, (5, 5), 1) # OTSU二值化得到黑底白虫的图像 ret, osu = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) # 1. 形态学开运算去除细小噪声(可选,但能提升后续效果) kernel = np.ones((3, 3), np.uint8) opening = cv.morphologyEx(osu, cv.MORPH_OPEN, kernel, iterations=2) # 2. 确定确定的背景区域(通过膨胀操作) sure_bg = cv.dilate(opening, kernel, iterations=3) # 3. 距离变换找到确定的前景种子 # 距离变换会计算每个前景像素到最近背景的距离,距离越大越接近物体中心 dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5) # 取距离变换最大值的70%作为阈值,提取出物体核心区域作为种子 ret, sure_fg = cv.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0) # 4. 计算未知区域(背景和前景之间的过渡区) sure_fg = np.uint8(sure_fg) unknown = cv.subtract(sure_bg, sure_fg) # 5. 标记连通域作为分水岭的初始标记 ret, markers = cv.connectedComponents(sure_fg) # 把背景标记从0改为1,让未知区域标记为0(分水岭要求未知区域是0) markers = markers + 1 markers[unknown == 255] = 0 # 6. 应用分水岭算法分割图像 markers = cv.watershed(img, markers) # 用蓝色线条标记分割边界 img[markers == -1] = [255, 0, 0] # 统计蠕虫数量:连通域的数量(ret就是sure_fg中的连通域数,也就是蠕虫数量) print(f"检测到的蠕虫数量:{ret}") # 显示结果 cv.imshow('分割结果', img) cv.waitKey(0) cv.destroyAllWindows()
原理说明
- 距离变换:帮我们定位每个蠕虫的中心区域,这些区域不会被重叠部分干扰,是可靠的分割种子。
- 分水岭算法:把图像看作地形,种子是“山峰”,算法会从山峰开始向四周“灌水”,不同山峰的水碰到一起时会形成分割线,完美分隔开重叠的蠕虫。
方案二:自适应阈值+形态学组合(针对轻度重叠)
如果你的蠕虫重叠程度不高,可以试试用自适应阈值替代全局OTSU阈值,它能根据局部区域的灰度调整阈值,可能让重叠处的边界更清晰,再结合形态学操作优化:
import cv2 as cv import numpy as np img = cv.imread('worms.jpg') gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) blur = cv.GaussianBlur(gray, (5, 5), 1) # 自适应阈值二值化,局部调整阈值 osu = cv.adaptiveThreshold(blur, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 11, 2) # 注意这里用THRESH_BINARY_INV得到白底黑虫,方便后续操作 # 形态学闭运算填补蠕虫内部的小空洞,再开运算断开细小连接 kernel = np.ones((3, 3), np.uint8) closing = cv.morphologyEx(osu, cv.MORPH_CLOSE, kernel, iterations=2) opening = cv.morphologyEx(closing, cv.MORPH_OPEN, kernel, iterations=1) # 找轮廓并计数 imsource, contours, test = cv.findContours(opening, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) cv.drawContours(img, contours, -1, (0, 0, 255), 2) print(f"检测到的蠕虫数量:{len(contours)}") cv.imshow('结果', img) cv.waitKey(0) cv.destroyAllWindows()
注意事项
- 自适应阈值的参数(比如
11是块大小,2是常数)需要根据你的图像调整,块大小必须是奇数。 - 这个方法适合轻度重叠的场景,如果重叠太严重,还是分水岭效果更好。
内容的提问来源于stack exchange,提问作者A.Cossard
相关产品推荐
相关产品推荐

