【youcans 的 OpenCV 例程 200 篇】125. 形态算法之提取连通分量
3. 形态学算法
形态学处理的主要应用是提取图像中用来表示和描述形状的元素和成分,例如提取边界、连通分量、凸壳和区域骨架。
3.3 提取连通分量
从二值图像中提取连通分量是自动图像分析的核心步骤。
约束膨胀提取连通分量:
冈萨雷斯《数字图像处理(第四版)》提供了一种提取连通分量的形态学算法,构造一个元素为 0 的阵列
X
0
X_0
X0,其中对应连通分量的像素值为 1,采用迭代过程可以得到所有的连通分量:
X
k
=
(
X
k
−
1
⊕
B
)
∩
I
,
k
=
1
,
2
,
3...
X_k = (X_{k-1} \oplus B) \cap I, \ k=1,2,3...
Xk=(Xk−1⊕B)∩I, k=1,2,3...
该算法与约束膨胀孔洞填充的思路相同,使用条件膨胀来限制膨胀的增长,但用 I I I 代替 I c I^c Ic 以寻找前景点。
对于内含多个连通分量的图像 A,从仅为连通分量 A1 内部的某个像素 B 开始,用 3*3的结构元不断进行膨胀。由于其它连通分量与 A1 之间至少有一条像素宽度的空隙,每次膨胀都不会产生位于其它连通区域内的点。用每次膨胀后的图像与原始图像 A 取交集,就把膨胀限制在 A1 内部。随着集合 B 的不断膨胀,B 的区域不断生长,但又被限制在连通分量 A1 的内部,最终就会充满整个连通分量 A1,从而实现对连通分量 A1 的提取。
提取连通分量的过程也是对连通分量的标注,通常给图像中的每个连通区分配编号,在输出图像中该连通区内的所有的像素值赋值为对应的区域编号,这样的输出图像被称为标注图像。
例程 10.13:形态算法之提取连通分量
# # 10.13 约束膨胀算法提取连通分量
# 本算法参考:冈萨雷斯《数字图像处理(第四版)》 9.5.3 提取连通分量
# 图像为二值化图像,255 白色为目标物,0 黑色为背景
imgGray = cv2.imread("../images/Fig0918a.tif", flags=0) # flags=0 读取为灰度图像
# 预处理
ret, imgThresh = cv2.threshold(imgGray, 200, 255, cv2.THRESH_BINARY_INV) # 二值化处理
kernel = np.ones((3, 3), dtype=np.uint8) # 生成盒式卷积核
imgClose = cv2.morphologyEx(imgThresh, cv2.MORPH_CLOSE, kernel) # 闭运算,消除噪点
imgErode = cv2.erode(imgClose, kernel=kernel) # 腐蚀运算,腐蚀亮点
imgBin = imgErode
imgBinCopy = imgBin.copy() # 复制 imgBin
xBinary = np.zeros(imgBin.shape, np.uint8) # 大小与 img 相同,像素值为 0
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) # 3×3结构元
count = [] # 为了记录连通分量中的像素个数
while imgBinCopy.any(): # 循环迭代,直到 imgBinCopy 中的像素值全部为0
Xa_copy, Ya_copy = np.where(imgBinCopy > 0) # imgBinCopy 中值为255的像素的坐标
xBinary[Xa_copy[0]][Ya_copy[0]] = 255 # 选取第一个点,并将 xBinary 中对应像素值改为255
# 约束膨胀,先对 xBinary 膨胀,再与 imgBin 执行与操作(取交集)
for i in range(100):
dilation_B = cv2.dilate(xBinary, kernel)
xBinary = cv2.bitwise_and(imgBin, dilation_B)
# 取 xBinary 值为255的像素坐标,并将 imgBinCopy 中对应坐标像素值变为0
Xb, Yb = np.where(xBinary > 0)
imgBinCopy[Xb, Yb] = 0
# 显示连通分量及其包含像素数量
count.append(len(Xb))
lenCount = len(count)
if lenCount == 0:
print("无连通分量")
elif lenCount == 1:
print("第1个连通分量为{}".format(count[0]))
else:
print("第{}个连通分量为{}".format(len(count), count[-1]-count[-2]))
# print(count)
plt.figure(figsize=(12, 6))
plt.subplot(231), plt.axis('off'), plt.title("origin")
plt.imshow(imgGray, cmap='gray', vmin=0, vmax=255)
plt.subplot(232), plt.title("threshold"), plt.axis('off')
plt.imshow(imgBin, cmap='gray', vmin=0, vmax=255)
plt.subplot(233), plt.title("closed image"), plt.axis('off')
plt.imshow(imgClose, cmap='gray', vmin=0, vmax=255)
plt.subplot(234), plt.title("eroded image"), plt.axis('off')
plt.imshow(imgErode, cmap='gray', vmin=0, vmax=255)
plt.subplot(235), plt.title("xBinary"), plt.axis('off')
plt.imshow(xBinary, cmap='gray', vmin=0, vmax=255)
plt.subplot(236), plt.title("binary copy"), plt.axis('off')
plt.imshow(imgBinCopy, cmap='gray', vmin=0, vmax=255)
plt.tight_layout()
plt.show()
(本节完)
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/123457062)
Copyright 2022 youcans, XUPT
Crated:2022-3-12