连通域分割Two-Pass算法及其Python实现

文章目录

连通域Two-Pass算法

在没有任何先验知识的情况下,想完成连通域的搜索,几乎最直接的想法,就是遍历图像所有像素点,如果两个像素点相连接,便将二者视为一体,直到遍历所有的像素。但这种遍历在遇到类似下面的图像时就会出现问题。

1 2 3 \begin{matrix} &1\\ 2&3 \end{matrix} 213

上面的矩阵中,数字代表有效像素,如果扫描顺序是从左向右,从上到下,那么在扫描第二行时, 2 2 2并不和 1 1 1联通, 3 3 3尽管和 1 , 2 1,2 1,2均联通,但只能赋予1个编号,从而上面的情况最终标记如下

1 2 1 \begin{matrix} &1\\ 2&1 \end{matrix} 211

所以,原本属于一个连通域的3个像素点,被分为 1 , 3 1,3 1,3和 2 2 2两个区域,所以需要再去遍历一次,把相邻的不同编号统一,这就是Two-Pass算法的基本思想。

数据准备

在具体实现算法之前,先准备一张二值图像,并封装一个绘图函数。

python 复制代码
import matplotlib.pyplot as plt
from scipy.ndimage import binary_erosion
import numpy as np
path = r"coin.png"
img = plt.imread(path).astype(float)
img = np.mean(img, axis=2)
th = 0.513    # climb(img, 0.1, 0, 0.01)
b = img>0.4

def drawImg(im1, im2, c1='jet', c2='jet'):
    fig = plt.figure()
    ax = fig.add_subplot(121)
    plt.imshow(im1, cmap=c1)
    plt.axis('off')
    ax = fig.add_subplot(122)
    plt.imshow(im2, cmap=c2)
    plt.axis('off')
    plt.show()

b = img>0.4
bb = binary_erosion(b, np.ones([5,5]))

第一次

第一次扫描的目的是建立当前像素与左边和上边的像素之间的联通关系,同时需要一个字典保存这种映射,为后续的连通域合并做准备。

python 复制代码
def firstPass(inImg):
    L = 0       # 标记号
    outImg = inImg.astype(float)
    h, w = inImg.shape
    dct = {}
    for i,j in product(range(h), range(w)):
        if inImg[i,j] == 0:
            continue
        neighbors = []   # 记录符合要求的邻域前景
        if i-1> 0 and inImg[i-1, j]>0:
            neighbors.append(outImg[i-1, j])
        if j-1 > 0 and inImg[i, j-1] > 0:
            neighbors.append(outImg[i, j-1])
        if len(neighbors) == 0:
            L += 1
            outImg[i,j] = L
            dct[L] = [[i],[j]]
        else:
            tmpL = min(neighbors)
            outImg[i,j] = tmpL
            dct[tmpL][0].append(i)
            dct[tmpL][1].append(j)
    return outImg, dct

实验效果如下

python 复制代码
c1, d = firtPass(bb)
drawImg(bb, c1)

第二次

第二次扫描的目的是,将属于同一连通域,但编号不同的区域,赋予相同的序号。为此 ,需要再次遍历图像,并通过第一次遍历得到的映射字典,来完成连通域的合并。

python 复制代码
def secondPass(outImg, dct):
    outImg = outImg * 1
    print('version')
    for i,j in zip(*np.where(outImg!=0)):
        Ls = [outImg[i,j]]
        if i-1>0 and outImg[i-1,j] != 0:
            Ls.append(outImg[i-1,j])
        if j-1 > 0 and outImg[i, j-1] != 0:
            Ls.append(outImg[i, j-1])
        Ls = np.unique(Ls)
        if len(Ls)<2:
            continue
        minL = np.min(Ls)
        for L in Ls:
            if L == minL:
                continue
            y,x = dct[L]
            outImg[y,x] = minL
            dct[minL][0].extend(dct[L][0])
            dct[minL][1].extend(dct[L][1])
            del dct[L]
    u = np.unique(outImg)
    u = np.sort(u)  # 排序
    N = len(u) - 1  # 此为图标数
    for i in range(1, N+1):
        outImg[outImg==u[i]] = i
    N = len(np.unique(outImg))
    return N, outImg

效果如下

python 复制代码
n, c2 = secondPass(c1, d)
drawImg(c1, c2)
相关推荐
星依网络19 小时前
yolov5实现游戏图像识别与后续辅助功能
python·开源·游戏程序·骨骼绑定
大佐不会说日语~19 小时前
Spring AI Alibaba 的 ChatClient 工具注册与 Function Calling 实践
人工智能·spring boot·python·spring·封装·spring ai
2501_9216494920 小时前
如何获取美股实时行情:Python 量化交易指南
开发语言·后端·python·websocket·金融
qq_4480111620 小时前
python HTTP请求同时返回为JSON的异常处理
python·http·json
棒棒的皮皮20 小时前
【OpenCV】Python图像处理几何变换之翻转
图像处理·python·opencv·计算机视觉
im_AMBER20 小时前
Leetcode 78 识别数组中的最大异常值 | 镜像对之间最小绝对距离
笔记·学习·算法·leetcode
CodeCraft Studio20 小时前
国产化PPT处理控件Spire.Presentation教程:使用Python将图片批量转换为PPT
python·opencv·powerpoint·ppt文档开发·ppt组件库·ppt api
鼾声鼾语21 小时前
matlab的ros2发布的消息,局域网内其他设备收不到情况吗?但是matlab可以订阅其他局域网的ros2发布的消息(问题总结)
开发语言·人工智能·深度学习·算法·matlab·isaaclab
五阿哥永琪21 小时前
Spring Boot 中自定义线程池的正确使用姿势:定义、注入与最佳实践
spring boot·后端·python
Data_agent21 小时前
Python编程实战:从类与对象到设计优雅
爬虫·python