python – Scipy标签侵蚀

如何在numpy数组中的标记区域周围保留一圈像素?

在一个简单的例子中,我会减去侵蚀.当标签触摸时,该方法不起作用.我如何从A获得B?

A = array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

B = array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0],
           [0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0],
           [0, 0, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0],
           [0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

我正在使用带有许多标签的大型阵列,因此每个标签上的单独腐蚀都不是一种选择.

最佳答案 新答案

实际上,我只想到了一个更好的方法:

B = A * (np.abs(scipy.ndimage.laplace(A)) > 0)

作为一个完整的例子:

import numpy as np
import scipy.ndimage

A = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

B = A * (np.abs(scipy.ndimage.laplace(A)) > 0)

我认为这应该适用于所有情况(无论如何都是像A这样的“标记”数组……).

如果您担心性能,可以将其拆分为几个部分以减少内存开销:

B = scipy.ndimage.laplace(A)
B = np.abs(B, B) # Preform abs in-place
B /= B  # This will produce a divide by zero warning that you can safely ignore
B *= A  

这个版本更详细,但应该使用更少的内存.

老答案

我想不出用一般的scipy.ndimage函数一步完成它的好方法. (我觉得像一个顶帽过滤器应该做你想要的,但我无法弄明白.)

但是,正如您所提到的,做几个单独的侵蚀是一种选择.

如果使用find_objects来提取每个标签的子区域,那么即使在非常大的数组上也应该获得合理的性能,然后只对子区域进行侵蚀.

例如:

import numpy as np
import scipy.ndimage

A = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
              [0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

regions = scipy.ndimage.find_objects(A)

mask = np.zeros_like(A).astype(np.bool)

for val, region in enumerate(regions, start=1):
    if region is not None:
        subregion = A[region]
        mask[region] = scipy.ndimage.binary_erosion(subregion == val)

B = A.copy()
B[mask] = 0

这会产生:

array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0],
       [0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0],
       [0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0],
       [0, 0, 2, 0, 0, 2, 2, 2, 2, 0, 0, 0],
       [0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

对于大型阵列,性能应该是合理的,但是它将强烈依赖于不同标记对象跨越的区域大小以及您拥有的标记对象的数量….

点赞