分水岭

日期2009-07-21 (最后修改), 2009-06-28 (创建)

分水岭算法 (参见 1) 用于将图像分割成不同的组件。

假设我们有以下图像,它由三个白色圆盘 (像素值为 1) 和黑色背景 (像素值为 0) 组成。我们希望获得一个新的数组,其中每个像素都用它所属组件的索引标记,即原始数组的分割,如下面的图像所示。我们将使用 scipy.ndimage 提供的分水岭算法,scipy.ndimage.watershed_ift。

In [14]
# Create the initial black and white image
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
a = np.zeros((512, 512)).astype(np.uint8) #unsigned integer type needed by watershed
y, x = np.ogrid[0:512, 0:512]
m1 = ((y-200)**2 + (x-100)**2 < 30**2)
m2 = ((y-350)**2 + (x-400)**2 < 20**2)
m3 = ((y-260)**2 + (x-200)**2 < 20**2)
a[m1+m2+m3]=1
plt.imshow(a, cmap='gray')# left plot in the image above
Out[14]
<matplotlib.image.AxesImage at 0x7ff9c8348690>

分水岭算法依赖于不同盆地的泛洪,因此我们需要在图像中放置标记来启动泛洪。如果大致知道物体的位置,并且物体数量很少,则可以通过手动设置标记。

In [15]
markers = np.zeros_like(a).astype(np.int16)
markers[0, 0] = 1
markers[200, 100] = 2
markers[350, 400] = 3
markers[260, 200] = 4
res1 = ndimage.watershed_ift(a.astype(np.uint8), markers)
plt.imshow(res1, cmap='jet') # central plot in the image above
Out[15]
<matplotlib.image.AxesImage at 0x7ff9c827a550>
In [16]
np.unique(res1) # pixels are tagged according to the object they belong to
Out[16]
array([1, 2, 3, 4], dtype=int16)

如果您不知道在哪里放置标记,并且知道物体的最小尺寸,则可以在比物体更精细的网格上散布大量标记。

In [17]
xm, ym = np.ogrid[0:512:10, 0:512:10]
markers = np.zeros_like(a).astype(np.int16)
markers[xm, ym]= np.arange(xm.size*ym.size).reshape((xm.size,ym.size))
res2 = ndimage.watershed_ift(a.astype(np.uint8), markers)
res2[xm, ym] = res2[xm-1, ym-1] # remove the isolate seeds
plt.imshow(res2, cmap='jet')
Out[17]
<matplotlib.image.AxesImage at 0x7ff9c81a3a10>

章节作者: EmmanuelleGouillart, DavidLinke