python數字圖像處理(19):骨架提取與分水嶺算法

https://www.cnblogs.com/denny402/p/5167414.htmlhtml

骨架提取與分水嶺算法也屬於形態學處理範疇,都放在morphology子模塊內。算法

一、骨架提取dom

骨架提取,也叫二值圖像細化。這種算法能將一個連通區域細化成一個像素的寬度,用於特徵提取和目標拓撲表示。函數

morphology子模塊提供了兩個函數用於骨架提取,分別是Skeletonize()函數和medial_axis()函數。咱們先來看Skeletonize()函數。測試

格式爲:skimage.morphology.skeletonize(image)spa

輸入和輸出都是一幅二值圖像。rest

例1:code

複製代碼
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()
複製代碼

生成一幅測試圖像,上面有三個目標對象,分別進行骨架提取,結果以下:orm

例2:利用系統自帶的馬圖片進行骨架提取htm

複製代碼
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(imagemask=Nonereturn_distance=False)

mask: 掩模。默認爲None, 若是給定一個掩模,則在掩模內的像素值才執行骨架算法。

return_distance: bool型值,默認爲False. 若是爲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 > 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()
複製代碼

二、分水嶺算法

分水嶺在地理學上就是指一個山脊,水一般會沿着山脊的兩邊流向不一樣的「匯水盆」。分水嶺算法是一種用於圖像分割的經典算法,是基於拓撲理論的數學形態學的分割方法。若是圖像中的目標物體是連在一塊兒的,則分割起來會更困難,分水嶺算法常常用於處理這類問題,一般會取得比較好的效果。

分水嶺算法能夠和距離變換結合,尋找「匯水盆地」和「分水嶺界限」,從而對圖像進行分割。二值圖像的距離變換就是每個像素點到最近非零值像素點的距離,咱們可使用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=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:基於梯度的分水嶺圖像分割

複製代碼
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()
複製代碼

相關文章
相關標籤/搜索