骨架提取与分水岭算法也属于形态学处理范畴,都放在morphology子模块内。
1、骨架提取
骨架提取,也叫二值图像细化。这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示。
morphology子模块提供了两个函数用于骨架提取,分别是skeletonize()函数和medial_axis()函数。我们先来看skeletonize()函数。
格式为:skimage.morphology.skeletonize(image)
输入和输出都是一幅二值图像。
例1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
from skimage import morphology,draw
import numpy as np
import matplotlib.pyplot as plt
#创建一个二值图像用于测试
image = np.zeros(( 400 , 400 ))
#生成目标对象1(白色u型)
image[ 10 : - 10 , 10 : 100 ] = 1
image[ - 100 : - 10 , 10 : - 10 ] = 1
image[ 10 : - 10 , - 100 : - 10 ] = 1
#生成目标对象2(x型)
rs, cs = draw.line( 250 , 150 , 10 , 280 )
for i in range ( 10 ):
image[rs + i, cs] = 1
rs, cs = draw.line( 10 , 150 , 250 , 280 )
for i in range ( 20 ):
image[rs + i, cs] = 1
#生成目标对象3(o型)
ir, ic = np.indices(image.shape)
circle1 = (ic - 135 ) * * 2 + (ir - 150 ) * * 2 < 30 * * 2
circle2 = (ic - 135 ) * * 2 + (ir - 150 ) * * 2 < 20 * * 2
image[circle1] = 1
image[circle2] = 0
#实施骨架算法
skeleton = morphology.skeletonize(image)
#显示结果
fig, (ax1, ax2) = plt.subplots(nrows = 1 , ncols = 2 , figsize = ( 8 , 4 ))
ax1.imshow(image, cmap = plt.cm.gray)
ax1.axis( 'off' )
ax1.set_title( 'original' , fontsize = 20 )
ax2.imshow(skeleton, cmap = plt.cm.gray)
ax2.axis( 'off' )
ax2.set_title( 'skeleton' , fontsize = 20 )
fig.tight_layout()
plt.show()
|
生成一幅测试图像,上面有三个目标对象,分别进行骨架提取,结果如下:
例2:利用系统自带的马图片进行骨架提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
from skimage import morphology,data,color
import matplotlib.pyplot as plt
image = color.rgb2gray(data.horse())
image = 1 - image #反相
#实施骨架算法
skeleton = morphology.skeletonize(image)
#显示结果
fig, (ax1, ax2) = plt.subplots(nrows = 1 , ncols = 2 , figsize = ( 8 , 4 ))
ax1.imshow(image, cmap = plt.cm.gray)
ax1.axis( 'off' )
ax1.set_title( 'original' , fontsize = 20 )
ax2.imshow(skeleton, cmap = plt.cm.gray)
ax2.axis( 'off' )
ax2.set_title( 'skeleton' , fontsize = 20 )
fig.tight_layout()
plt.show()
|
medial_axis就是中轴的意思,利用中轴变换方法计算前景(1值)目标对象的宽度,格式为:
skimage.morphology.medial_axis(image,mask=none,return_distance=false)
mask: 掩模。默认为none, 如果给定一个掩模,则在掩模内的像素值才执行骨架算法。
return_distance: bool型值,默认为false. 如果为true, 则除了返回骨架,还将距离变换值也同时返回。这里的距离指的是中轴线上的所有点与背景点的距离。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import numpy as np
import scipy.ndimage as ndi
from skimage import morphology
import matplotlib.pyplot as plt
#编写一个函数,生成测试图像
def microstructure(l = 256 ):
n = 5
x, y = np.ogrid[ 0 :l, 0 :l]
mask = np.zeros((l, l))
generator = np.random.randomstate( 1 )
points = l * generator.rand( 2 , n * * 2 )
mask[(points[ 0 ]).astype(np. int ), (points[ 1 ]).astype(np. int )] = 1
mask = ndi.gaussian_filter(mask, sigma = l / ( 4. * n))
return mask > mask.mean()
data = microstructure(l = 64 ) #生成测试图像
#计算中轴和距离变换值
skel, distance = morphology.medial_axis(data, return_distance = true)
#中轴上的点到背景像素点的距离
dist_on_skel = distance * skel
fig, (ax1, ax2) = plt.subplots( 1 , 2 , figsize = ( 8 , 4 ))
ax1.imshow(data, cmap = plt.cm.gray, interpolation = 'nearest' )
#用光谱色显示中轴
ax2.imshow(dist_on_skel, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax2.contour(data, [ 0.5 ], colors = 'w' ) #显示轮廓线
fig.tight_layout()
plt.show()
|
2、分水岭算法
分水岭在地理学上就是指一个山脊,水通常会沿着山脊的两边流向不同的“汇水盆”。分水岭算法是一种用于图像分割的经典算法,是基于拓扑理论的数学形态学的分割方法。如果图像中的目标物体是连在一起的,则分割起来会更困难,分水岭算法经常用于处理这类问题,通常会取得比较好的效果。
分水岭算法可以和距离变换结合,寻找“汇水盆地”和“分水岭界限”,从而对图像进行分割。二值图像的距离变换就是每一个像素点到最近非零值像素点的距离,我们可以使用scipy包来计算距离变换。
在下面的例子中,需要将两个重叠的圆分开。我们先计算圆上的这些白色像素点到黑色背景像素点的距离变换,选出距离变换中的最大值作为初始标记点(如果是反色的话,则是取最小值),从这些标记点开始的两个汇水盆越集越大,最后相交于分山岭。从分山岭处断开,我们就得到了两个分离的圆。
例1:基于距离变换的分山岭图像分割
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
import numpy as np
import matplotlib.pyplot as plt
from scipy import ndimage as ndi
from skimage import morphology,feature
#创建两个带有重叠圆的图像
x, y = np.indices(( 80 , 80 ))
x1, y1, x2, y2 = 28 , 28 , 44 , 52
r1, r2 = 16 , 20
mask_circle1 = (x - x1) * * 2 + (y - y1) * * 2 < r1 * * 2
mask_circle2 = (x - x2) * * 2 + (y - y2) * * 2 < r2 * * 2
image = np.logical_or(mask_circle1, mask_circle2)
#现在我们用分水岭算法分离两个圆
distance = ndi.distance_transform_edt(image) #距离变换
local_maxi = feature.peak_local_max(distance, indices = false, footprint = np.ones(( 3 , 3 )),
labels = image) #寻找峰值
markers = ndi.label(local_maxi)[ 0 ] #初始标记点
labels = morphology.watershed( - distance, markers, mask = image) #基于距离变换的分水岭算法
fig, axes = plt.subplots(nrows = 2 , ncols = 2 , figsize = ( 8 , 8 ))
axes = axes.ravel()
ax0, ax1, ax2, ax3 = axes
ax0.imshow(image, cmap = plt.cm.gray, interpolation = 'nearest' )
ax0.set_title( "original" )
ax1.imshow( - distance, cmap = plt.cm.jet, interpolation = 'nearest' )
ax1.set_title( "distance" )
ax2.imshow(markers, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax2.set_title( "markers" )
ax3.imshow(labels, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax3.set_title( "segmented" )
for ax in axes:
ax.axis( 'off' )
fig.tight_layout()
plt.show()
|
分水岭算法也可以和梯度相结合,来实现图像分割。一般梯度图像在边缘处有较高的像素值,而在其它地方则有较低的像素值,理想情况 下,分山岭恰好在边缘。因此,我们可以根据梯度来寻找分山岭。
例2:基于梯度的分水岭图像分割
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import matplotlib.pyplot as plt
from scipy import ndimage as ndi
from skimage import morphology,color,data, filter
image = color.rgb2gray(data.camera())
denoised = filter .rank.median(image, morphology.disk( 2 )) #过滤噪声
#将梯度值低于10的作为开始标记点
markers = filter .rank.gradient(denoised, morphology.disk( 5 )) < 10
markers = ndi.label(markers)[ 0 ]
gradient = filter .rank.gradient(denoised, morphology.disk( 2 )) #计算梯度
labels = morphology.watershed(gradient, markers, mask = image) #基于梯度的分水岭算法
fig, axes = plt.subplots(nrows = 2 , ncols = 2 , figsize = ( 6 , 6 ))
axes = axes.ravel()
ax0, ax1, ax2, ax3 = axes
ax0.imshow(image, cmap = plt.cm.gray, interpolation = 'nearest' )
ax0.set_title( "original" )
ax1.imshow(gradient, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax1.set_title( "gradient" )
ax2.imshow(markers, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax2.set_title( "markers" )
ax3.imshow(labels, cmap = plt.cm.spectral, interpolation = 'nearest' )
ax3.set_title( "segmented" )
for ax in axes:
ax.axis( 'off' )
fig.tight_layout()
plt.show()
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.cnblogs.com/denny402/p/5167414.html