python数字图像处理(19):⾻架提取与分⽔岭算法
⾻架提取与分⽔岭算法也属于形态学处理范畴,都放在morphology⼦模块内。
1、⾻架提取
⾻架提取,也叫⼆值图像细化。这种算法能将⼀个连通区域细化成⼀个像素的宽度,⽤于特征提取和⽬标拓扑表⽰。
morphology⼦模块提供了两个函数⽤于⾻架提取,分别是Skeletonize()函数和medial_axis()函数。我们先来看Skeletonize()函数。格式为:phology.skeletonize(image)
性的功能输⼊和输出都是⼀幅⼆值图像。
例1:
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, ay)
ax1.axis('off')
ax1.t_title('original', fontsize=20)
ax2.imshow(skeleton, ay)
ax2.axis('off')
ax2.t_title('skeleton', fontsize=20)
fig.tight_layout()
plt.show()
⽣成⼀幅测试图像,上⾯有三个⽬标对象,分别进⾏⾻架提取,结果如下:
例2:利⽤系统⾃带的马图⽚进⾏⾻架提取
from skimage import morphology,data,color
import matplotlib.pyplot as plt
b2gray(data.hor())
image=1-image #反相
#实施⾻架算法
skeleton =morphology.skeletonize(image)
#显⽰结果
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))
ax1.imshow(image, ay)
ax1.axis('off')
ax1.t_title('original', fontsize=20)
ax2.imshow(skeleton, ay)
ax2.axis('off')
ax2.t_title('skeleton', fontsize=20)
fig.tight_layout()
plt.show()
medial_axis就是中轴的意思,利⽤中轴变换⽅法计算前景(1值)⽬标对象的宽度,格式为:
mask: 掩模。默认为None, 如果给定⼀个掩模,则在掩模内的像素值才执⾏⾻架算法。
return_distance: bool型值,默认为Fal. 如果为True, 则除了返回⾻架,还将距离变换值也同时返回。这⾥的距离指的是中轴线上的所有点与背景点的距离。
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 > an()
data = microstructure(l=64) #⽣成测试图像
#计算中轴和距离变换值
skel, distance =dial_axis(data, return_distance=True)
#中轴上的点到背景像素点的距离
dist_on_skel = distance * skel
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
ax1.imshow(data, ay, interpolation='nearest')
#⽤光谱⾊显⽰中轴
ax2.imshow(dist_on_skel, spectral, interpolation='nearest')
fig.tight_layout()
plt.show()
2、分⽔岭算法
分⽔岭在地理学上就是指⼀个⼭脊,⽔通常会沿着⼭脊的两边流向不同的“汇⽔盆”。分⽔岭算法是⼀种⽤于图像分割的经典算法,是基于拓扑理论的数学形态学的分割⽅法。如果图像中的⽬标物体是连在⼀起的,则分割起来会更困难,分⽔岭算法经常⽤于处理这类问题,通常会取得⽐较好的效果。
分⽔岭算法可以和距离变换结合,寻找“汇⽔盆地”和“分⽔岭界限”,从⽽对图像进⾏分割。⼆值图像的距离变换就是每⼀个像素点到最近⾮零值像素点的距离,我们可以使⽤scipy包来计算距离变换。
在下⾯的例⼦中,需要将两个重叠的圆分开。我们先计算圆上的这些⽩⾊像素点到⿊⾊背景像素点的距离变换,选出距离变换中的最⼤值作为初始标记点(如果是反⾊的话,则是取最⼩值),从这些标记点开始的两个汇⽔盆越集越⼤,最后相交于分⼭岭。从分⼭岭处断开,我们就得到了两个分离的圆。
例1:基于距离变换的分⼭岭图像分割
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=Fal, s((3, 3)),
labels=image) #寻找峰值
markers = ndi.label(local_maxi)[0] #初始标记点
labels =morphology.watershed(-distance, markers, mask=image) #基于距离变换的分⽔岭算法
till怎么读
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 8))
axes = axes.ravel()
ax0, ax1, ax2, ax3 = axes
ax0.imshow(image, ay, interpolation='nearest')
ax0.t_title("Original")
ax1.imshow(-distance, jet, interpolation='nearest')
ax1.t_title("Distance")
ax2.imshow(markers, spectral, interpolation='nearest')
ax2.t_title("Markers")
ax3.imshow(labels, spectral, interpolation='nearest')
ax3.t_title("Segmented")
for ax in axes:
ax.axis('off')
fig.tight_layout()
plt.show()
分⽔岭算法也可以和梯度相结合,来实现图像分割。⼀般梯度图像在边缘处有较⾼的像素值,⽽在其
它地⽅则有较低的像素值,理想情况下,分⼭岭恰好在边缘。因此,我们可以根据梯度来寻找分⼭岭。
例2:基于梯度的分⽔岭图像分割
公民记者import matplotlib.pyplot as plt
from scipy import ndimage as ndi
from skimage import morphology,color,data,filter
image =b2gray(data.camera())
denoid = dian(image, morphology.disk(2)) #过滤噪声
#将梯度值低于10的作为开始标记点
markers = adient(denoid, morphology.disk(5)) <10
markers = ndi.label(markers)[0]
gradient = adient(denoid, 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, ay, interpolation='nearest')
ax0.t_title("Original")
ax1.imshow(gradient, spectral, interpolation='nearest')
ax1.t_title("Gradient")
ax2.imshow(markers, spectral, interpolation='nearest')
ax2.t_title("Markers")
ax3.imshow(labels, spectral, interpolation='nearest')
ax3.t_title("Segmented")
for ax in axes:折纸折纸
ax.axis('off')
句容山水fig.tight_layout()
plt.show()