主要参考:(美)拉斐尔·C.冈萨雷斯. 数字图像处理 第3版[M]. 阮秋琦,译. 北京:电子工业出版社, 2017: 633.
形态学重建
形态学重建涉及两幅图像和一个结构元:
- Marker 图像:包含变换的起点,将被连续膨胀,直至收敛
- Mask 图像:用来约束膨胀结果,即 Mask >= Marker
- 结构单元(Structuring Element,SE):定义连通性
数学迭代式:
Marker=(Marker⊕SE)∩Mask
代码示例:
def imreconstruct(marker, mask, SE=np.ones([3,3])):
""" 描述:以mask为约束,连续膨胀marker,实现形态学重建,其中mask >= marker 参数: - marker 标记图像,单通道/三通道图像 - mask 模板图像,与marker同型 - conn 联通性重建结构元,参照matlab::imreconstruct::conn参数,默认为8联通 """
while True:
marker_pre = marker
dilation = cv.dilate(marker, kernel=SE)
marker = np.min((dilation, mask), axis=0)
if (marker_pre == marker).all():
break
return marker
孔洞填充
孔洞定义:被前景(白色)连通域包围的封闭的背景(黑***域,不限于圆形。如图所示。
原理:
以原图像的补集作为Mask,用来限制膨胀结果;以带有白色边框的黑色图像为初始Marker,用SE对其进行连续膨胀,直至收敛;最后对Marker取补即得到最终图像,与原图相减可得到填充图像。
Marker(x,y)={2550,,(x,y)∈Boundaryothers
代码示例:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread("text.jpg")
# 二值化
imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
imgray[imgray < 100] = 0
imgray[imgray >= 100] = 255
# 原图取补得到MASK图像
mask = 255 - imgray
# 构造Marker图像
marker = np.zeros_like(imgray)
marker[0, :] = 255
marker[-1, :] = 255
marker[:, 0] = 255
marker[:, -1] = 255
marker_0 = marker.copy()
# 形态学重建
SE = cv.getStructuringElement(shape=cv.MORPH_CROSS, ksize=(3, 3))
while True:
marker_pre = marker
dilation = cv.dilate(marker, kernel=SE)
marker = np.min((dilation, mask), axis=0)
if (marker_pre == marker).all():
break
dst = 255 - marker
filling = dst - imgray
# 显示
plt.figure(figsize=(12, 6)) # width * height
plt.subplot(2, 3, 1), plt.imshow(imgray, cmap='gray'), plt.title('src'), plt.axis("off")
plt.subplot(2, 3, 2), plt.imshow(mask, cmap='gray'), plt.title('Mask'), plt.axis("off")
plt.subplot(2, 3, 3), plt.imshow(marker_0, cmap='gray'), plt.title('Marker 0'), plt.axis("off")
plt.subplot(2, 3, 4), plt.imshow(marker, cmap='gray'), plt.title('Marker'), plt.axis("off")
plt.subplot(2, 3, 5), plt.imshow(dst, cmap='gray'), plt.title('dst'), plt.axis("off")
plt.subplot(2, 3, 6), plt.imshow(filling, cmap='gray'), plt.title('Holes'), plt.axis("off")
plt.show()
结果展示:
原图 text.jpg
:
选用不同尺寸的SE,进行填充对比:ksize=3x3(左),ksize=7x7(右)
由此可见, 如果选择的结构单元过大,膨胀操作会越过边界,膨胀到孔洞中,导致部分孔洞填充失败。就本例来看3x3的SE可以做到准确填充。
并且,对于大小不一的孔洞,3x3的SE同样可以做到准确填充。如图所示:
注: 关于孔洞填充的其他问题,可以参考博文 形态学填充孔洞的几个问题