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


前言

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


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

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)

膨胀和本文聚类算法对比

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


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五岁能抬头s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值