文章目录
- 前言
- 1. 过分割分析
- 2. 聚类算法
- 核心代码
- 膨胀和本文聚类算法对比
前言
本文过分割解决办法,主要针对一些目标物在图像中较为分散或比较单一的情况有效。例如血管的过分割,一些杂质颗粒的过分割,较为分散矿石的过分割。如果目标物较为密集也可参考本文,自行改进聚类算法实现过分割的修复。
以下例子为杂质颗粒的过分割修复
1. 过分割分析
原图 | 对原图二值化后提取轮廓 | 开运算膨胀后提取轮廓 |
原图 | 对原图二值化后提取轮廓 | 开运算膨胀后提取轮廓 |
图中可以看出通过开运算、膨胀后内部一些杂点被消除,但是一些灰度值较低的连接处仍然无法连接在一起,图3中开运算及膨胀采用kernel大小,如下所示:
# 开运算
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# 膨胀
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
如果膨胀采用15 X 15的核,效果如下:
膨胀采用15 x 15的核,根据绿色画出的轮廓可以看出,轮廓外扩较多,如果涉及计算该目标物的面积、灰度占比、周长、直径等一些数据,会造成非常大的误差,如果不需要精确计算目标物详细信息,对于圆型目标物可以采用膨胀方式解决过分割。
2. 聚类算法
思想:遍历所有轮廓,根据每个轮廓重心点的距离减两个轮廓最小外接圆的半径,小于阈值的归为一类
聚类算法过程示意图:
核心代码
# 开运算膨胀,消除二值化图像中小亮点
kernel1 = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
kernel2 = cv.getStructuringElement(cv.MORPH_ELLIPSE, (3, 3))
image_open = cv.morphologyEx(image_thre, cv.MORPH_OPEN, kernel2, iterations=1)
image_dil = cv.dilate(image_open,kernel1)
_ , contours, hierarchy = cv.findContours(image_dil, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
if len(contours) != 0 :
# 距离相近的轮廓聚类,输出轮廓index
cnt_idxs = [[idx] for idx in range(len(contours))] # 轮廓index
pre_len = len(cnt_idxs)
while len(cnt_idxs) > 1:
tmp_cnt_idxs = []
flags = [False] * len(cnt_idxs)
for first in range(len(cnt_idxs)):
for second in range(len(cnt_idxs)):
if first == second or flags[first] or flags[second]:
continue
is_find = False
for fidx in cnt_idxs[first]:
cnt = contours[fidx]
_, radius1 = cv.minEnclosingCircle(cnt)
# 获取轮空间矩、中心矩、归一化矩
M = cv.moments(cnt)
# 获取目标重心坐标
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
for sidx in cnt_idxs[second]:
new_cnt = contours[sidx]
M2 = cv.moments(new_cnt)
# 获取目标重心坐标
cx2 = int(M2["m10"] / M2["m00"])
cy2 = int(M2["m01"] / M2["m00"])
_, radius2 = cv.minEnclosingCircle(new_cnt)
sq1 = (cx2 - cx) * (cx2 - cx)
sq2 = (cy2 - cy) * (cy2 - cy)
# 两轮廓中心点距离
dist = math.sqrt(sq1 + sq2)
# print("dist:{0:.2f}".format(dist))
if dist - radius2 - radius1 < 30:
is_find = True
tmp_cnt_idxs.append(cnt_idxs[first] + cnt_idxs[second])
flags[first] = flags[second] = True
break
if is_find:
break
for idx, flag in enumerate(flags):
if not flag:
tmp_cnt_idxs.append(cnt_idxs[idx])
cnt_idxs = tmp_cnt_idxs
if pre_len == len(cnt_idxs):
break
else:
pre_len = len(cnt_idxs)
膨胀和本文聚类算法对比
采用膨胀的方法造成轮廓选取偏差较大,影响精准计算