形态学重建之孔洞填充
白菜苗
如果你不小心又亦或是专门寻找形态学相关知识,那么很高兴能和你们分享这篇文章。
首先,如果想了解形态学重建之孔洞填充原理,那么必须先了解什么是膨胀、什么是孔洞填充、什么是形态学重建、什么是测地膨胀,只有具备相关知识,我们才能把形态学重建之孔洞填充原理吃透。
话不多说!!我们看看
1、什么是膨胀(如果已经了解,请往下看)

这是来源于冈萨雷斯数字图像处理(第三版)的公式,如果光看字面晦涩难懂,那看看例子:

- 左边,是原图,一个边长 d d d的正方形,至于左上角的小正方形是用来膨胀画上去的。
- 右边,变为膨胀后的一个边长 ( 1 8 + 1 + 1 8 ) d (\frac{1}{8} +1+\frac{1}{8})d (81+1+81)d的正方形。
那么怎么会这样呢?你可以这样理解,小正方形(即边长 d / 4 d/4 d/4的正方形)的中心可以在大的正方形里面运动,但是起不到膨胀效果,只有当它的中心点(即那个黑点)在边界运动时,是不是有 d / 8 d/8 d/8露在大正方形外面(看左边那个图),沿着边界绕一圈,是不是整个边长都会多了 d / 8 d/8 d/8,其实中心思想就是取并集,只要我的中心点在大正方形以内(包括边界),凡是能包含的地方,就是我俩共有的地方(即膨胀后的图形)。

同理可得:

2、什么是孔洞填充(如果已经了解,请往下看)
孔洞:一个孔洞可以被定义为由前景像素相连接的边界所包围的一个背景区域。(左到右依次是未填充,填充一部分、填充完毕)


有点抽象是不是,其实它的意思就是先找孔洞的一个点,用结构元去膨胀,然后用原图像的补集进行约束(就是求个交集),不断重复膨胀,约束直至图形不改变(即收敛)就停止,与原图求个交集,孔洞就填上了,那我们看看如何填充的:(
A
A
A原图,
A
c
A^c
Ac补集,
B
B
B结构元,先在原图需要填充部分找一个点,进行膨胀)















到此,填充完毕,如果你还纠结,那么我们思路理一下,例如
X
1
X_1
X1为什么从第一张图变成第二张,是因为图1,是膨胀后的,还需要
A
c
A^c
Ac补集约束一下,就是求个交集才是最后图形,其它以此类推,为什么到了
X
8
X_8
X8停下了呢,因为到这里已经收敛了,再膨胀也是
X
8
X_8
X8这张图形。


3、什么是形态学重建(如果已经了解,请往下看)


4、什么是测地膨胀(如果已经了解,请往下看)

测地膨胀,说白了,和前面的膨胀思想有点区别,前面的膨胀其实就是直接膨胀,测地膨胀呢?其实就是有条件的膨胀,怎么说,我们看看图:

一开始膨胀了是3*3个格子,中间那图,然后再跟模板
I
c
I^c
Ic求个交集,那么就是测地膨胀的思想:先膨胀后约束(即求交集)。

哈哈哈哈,是不是以为测地膨胀结束了,其实不是,这只是第一步,后面不断重复迭代,直至收敛,就是前面说到为什么到
X
8
X_8
X8就停止是一样的,前一跟后一个一样就没必要继续了。


5、什么是形态学重建之孔洞填充(终于到正题了)

首先,
F
(
x
,
y
)
F(x,y)
F(x,y)呢,其实求标记图像的方法,很简单,就是减去原始预想的边界值,其它地方为0,H则是我们要的结果。(看下图,对比下,是不是清楚点,边界取反,其它地方为0)

如果懂了,继续往下:用3*3的结构元
B
B
B去膨胀F。



- 左1图为膨胀一次的结果,我们不是说了吗,测地膨胀的精髓在于,先膨胀,后约束,那么我们用 I c I^c Ic(原始图像 I I I的补集)约束下,得到最右边图。
然后多次迭代。


可以发现,其实图形迭代一次就收敛了(这图刚好凑巧,其它可能多次)

H是我们要的结果,那么左1图,就是H公式里面的函数,
[
]
c
[]^c
[]c里面的其实就是补集的意思,所以对左1图取个补集就是H,细看,是不是填充好了。到此形态学重建之孔洞填充完成。
6、实验
代码:(非我所写:参见链接)
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()
结果:

我解释下,(从上开始数)src图为原始图,Mask为它的补集,Marker 0 为标记图像F,其实应该有白边框,估计像素小看不见,Marker为膨胀约束后的最后结果,dst为Marker补集图(即H)Holes图,为
d
s
t
∩
M
a
s
k
\color{purple}{dst \cap Mask}
dst∩Mask,就是填充部分。
对于结构元的选择不宜过大,不然填充不上。

明显发现33的结构元优于77的,(看下图)因为结构元过大,两个图像一样,取个补集就是原图
I
I
I,所以孔洞没填充上,所以选择结构元得适中。

到 此 结 束 , 希 望 能 帮 助 到 你 \color{maroon}{到此结束,希望能帮助到你} 到此结束,希望能帮助到你
智能推荐
形态学转换
文章目录 1.腐蚀 2.膨胀 3.开运算 4.闭运算 5.形态学梯度 6.礼帽 7.黑帽 8.结构化元素 形态学操作时根据图像形状进行的简单操作。对 二值化 图像进行的操作 准备工作: 1.腐蚀 输出结果: 2.膨胀 输出结果: 3.开运算 输出结果: 4.闭运算 输出结果: 5.形态学梯度 输出结果: 6.礼帽 输出结果: 7.黑帽 输出结果: 8.结构化元素 输出结果: 矩形: [[1 1 1...
形态学转换
原始图像: 函数:cv2.erode(),cv2.dilate(),cv2.morphotogyEx() 形态学转换原理:一般情况下对二值化图像进行操作。需要两个参数,一个是原始图像,第二个被称为结构化元素或者核,它是用来决定操作的性质的。基本操作为腐蚀和膨胀,他们的变体构成了开运算,闭运算,梯度等。 1.腐蚀 把前景物体的边界腐蚀掉,但是前景仍然是白色的。卷积核沿着图像滑动,如果与卷积核对应的原...
小程序图片转Base64,方法总结。
小程序图片转base64 前言:看了很多博客,小程序社区也逛了个遍,依然找不到小程序图片在本地转base64的方法,接下来就把所有方法做个总结,作为一个野生程序员,好东西绝对要分享。 第一种方法 先上代码 代码看起来似乎没有问题,but~~~~~,人家微信根本就没提供 FileReader()这个方法,所以,这个方法在小程序这里,直接pass掉就行了,不可行。 第二种方法 这个方法很好,代码也是最...
LC 剑指 Offer 36. 二叉搜索树与双向链表
LC 剑指 Offer 36 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。 特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱...
HDU5758(Explorer Bo 最小链覆盖的 所有链的长度总和最小 树上链dp)
题目 题意: 给你1颗树。起点可以在n个点之间任意选择。每次走到头了,转方向或切换到另一个点都花费1个魔法。它想把n-1条道路都经过一遍,但是想花费魔法的次数最少。问最少情况下它走路最短是多少。 思路: 首先,一棵树的叶子节点的个数不管以谁为根都是一样的。假如这颗树的叶子节点个数是num。1.num是偶数 那么它使用的魔法次数最少就是num/2,叶子节点两两结合形成链。 2.num是奇数。使用的魔...
猜你喜欢
redis集群
redis集群 需求来源:一台服务器不能满足开发; 注意:集群往往都伴随着分布式 集群:简单理解就是很多台服务器共同实现同一个业务 集群和分布式概述 分布式:将不同的业务分布在不同的地方;比如按照服务类型就有 web应用和数据库;按照功能模块分为 xx(采购)模块和xx(订单)模块; 集群提供了以下两个关键特性: 1、可扩展性--集群的性能不限于单一的服务实体,新的服务实体可以动态地加入到集群,从...
Express初始化一个项目&git提交一个项目
安装Express Express 是node.js上最流行的web开发框架,正如他的名字一样,使用他我们可以快速的开发一个web应用,我们用exxpress来搭建: 安装express命令行工具,使用它我们可以初始化一个express项目 创建一个项目: 然后根据提示(如上图),我们输入如下命令: 他会下载项目所依赖的包 然后接着输入: 最后访问一下: 提交一个项目 打开项目所在目录然后进入sh...
linux服务器某些第三方接口可以ping 通但是没有返回数据的原因
今天测试提了一个bug,查询第三方接口的天气接口一直没有数据返回、我当时一直以为是自己的代码问题。在代码的地方打了断点 , 服务器日志一直是开始发送请求,却没有报错,也没有返回数据 接下来是个get请求 在本地,可以有返回的数据,但是在服务器上面就是没有返回 这个是在服务器ping 的状态,是可以ping通的 一直卡着没有数据返回。。。。。 但是使用curl -v 去请求的时候一直没有返回 但是其...
第九课 常见的二叉树:平衡二叉树之红黑树
第九章 常见的二叉树:平衡二叉树之红黑树 1 背景 2 定义 3 红黑树与AVL树对比 4 树结构调整 5 代码实现 5.1 红黑树的插入 5.2 红黑树的删除 5.3 具体代码实现 1 背景 对于二叉排序树,时间复杂度最差时候会是 O(n),比如插入的元素是有序的,生成的二叉排序树就是一个链表,这种情况下,需要遍历全部元素才行。为了改变排序二叉树存在的不足,Rudolf Bayer 在 1972...
rocketmq quick start
rocketmq里面的角色: rocketmq里面有nameserver,master,broker三种角色 producer(生产者,生产者代码需要自己编写)和consumer(消费者,消费者代码需要自己编写)都需要访问 nameserver,已得知消息存放在的位置,nameserver就相当于分发的作用 master其实也是broker,只是他比较特别,一台master下面可以连着多台brok...
