python-opencv 一种基于聚类的图像过分割解决办法

文章目录

  • 前言
  • 1. 过分割分析
  • 2. 聚类算法
    • 核心代码
    • 膨胀和本文聚类算法对比


前言

本文过分割解决办法,主要针对一些目标物在图像中较为分散或比较单一的情况有效。例如血管的过分割,一些杂质颗粒的过分割,较为分散矿石的过分割。如果目标物较为密集也可参考本文,自行改进聚类算法实现过分割的修复。


以下例子为杂质颗粒的过分割修复

1. 过分割分析

python-opencv 一种基于聚类的图像过分割解决办法_第1张图片原图
python-opencv 一种基于聚类的图像过分割解决办法_第2张图片对原图二值化后提取轮廓
python-opencv 一种基于聚类的图像过分割解决办法_第3张图片开运算膨胀后提取轮廓
python-opencv 一种基于聚类的图像过分割解决办法_第4张图片原图
python-opencv 一种基于聚类的图像过分割解决办法_第5张图片对原图二值化后提取轮廓
python-opencv 一种基于聚类的图像过分割解决办法_第6张图片开运算膨胀后提取轮廓

图中可以看出通过开运算、膨胀后内部一些杂点被消除,但是一些灰度值较低的连接处仍然无法连接在一起,图3中开运算及膨胀采用kernel大小,如下所示:

# 开运算
kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# 膨胀
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))

如果膨胀采用15 X 15的核,效果如下:

python-opencv 一种基于聚类的图像过分割解决办法_第7张图片
python-opencv 一种基于聚类的图像过分割解决办法_第8张图片
python-opencv 一种基于聚类的图像过分割解决办法_第9张图片
python-opencv 一种基于聚类的图像过分割解决办法_第10张图片
膨胀采用15 x 15的核,根据绿色画出的轮廓可以看出,轮廓外扩较多,如果涉及计算该目标物的面积、灰度占比、周长、直径等一些数据,会造成非常大的误差,如果不需要精确计算目标物详细信息,对于圆型目标物可以采用膨胀方式解决过分割。

2. 聚类算法

思想:遍历所有轮廓,根据每个轮廓重心点的距离减两个轮廓最小外接圆的半径,小于阈值的归为一类
聚类算法过程示意图:
python-opencv 一种基于聚类的图像过分割解决办法_第11张图片

核心代码

# 开运算膨胀,消除二值化图像中小亮点
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)

膨胀和本文聚类算法对比

采用膨胀的方法造成轮廓选取偏差较大,影响精准计算

python-opencv 一种基于聚类的图像过分割解决办法_第12张图片
python-opencv 一种基于聚类的图像过分割解决办法_第13张图片
python-opencv 一种基于聚类的图像过分割解决办法_第14张图片
python-opencv 一种基于聚类的图像过分割解决办法_第15张图片

你可能感兴趣的:(聚类,opencv,算法,python)