在2d数组中填充边界框

a_p*_*ida 17 python arrays numpy bounding-box numpy-ndarray

我有一个2D numpy数组,看起来像

array([[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., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 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., 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., 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., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]]) `
Run Code Online (Sandbox Code Playgroud)

我想在上面显示的1s上创建像蒙版一样的边界框.例如,它应该是这样的

array([[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., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 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., 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., 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., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 0., 0., 0.]])
Run Code Online (Sandbox Code Playgroud)

我怎么能这么做呢?另外如果存在其他类似2,3等等,但我想忽略它们并且这些组大多数是2.

yat*_*atu 12

这是解决此问题的一种方法.其背后的一般思想是使用迭代解决方案,该解决方案在每个步骤中对矩阵和一组滤波器进行2D卷积,以便检测并填充落在边界框中的单元.

通过一个例子,这将更加清晰.假设我们有以下内容ndarray:

a = np.array([[0,0,0,0],
              [0,0,0,0],
              [1,0,0,0],
              [1,1,1,0]])
Run Code Online (Sandbox Code Playgroud)

该方法背后的想法是检测具有至少两个正交邻居(在1个单元的距离处)的单元,这些邻居彼此之间具有90°的角度,其中具有非零值.

通过迭代地找到这些单元并用它们填充它们,我们将获得预期的输出.因此,对于此示例,第一次迭代后的输出将为:

a = np.array([[0,0,0,0],
              [0,0,0,0],
              [1,1,0,0],
              [1,1,1,0]])
Run Code Online (Sandbox Code Playgroud)

并在以下迭代中:

a = np.array([[0,0,0,0],
              [0,0,0,0],
              [1,1,1,0],
              [1,1,1,0]])
Run Code Online (Sandbox Code Playgroud)

如何检测这些细胞?

一种方法是通过ndarray一组预定义的过滤器对2D进行卷积,这些过滤器专门用于检测感兴趣的细胞.为此,我们可以使用scipy convolve2D.

2D卷积基本上是通过将2D滤波器移位到a ndarray并在每一步计算元素乘法的总和来进行的.使用以下动画可能更直观(图片来自):

在此输入图像描述


因此有必要提出一些过滤器以检测感兴趣的细胞.一种方法可能是:

array([[0, 1, 0],
       [1, 0, 1],
       [0, 1, 0]])
Run Code Online (Sandbox Code Playgroud)

乍一看,这个过滤器可以完成任务,因为它会检测周围的邻居.但是,这个过滤器还会考虑两个单元格之外的样本,因此,例如它会将过滤器中第一行和最后一行的值相加,如前所述,我们希望找到一个角度的邻居相互成90°.所以我们可以做的是应用一系列过滤器来考虑这种情况的所有可能性:

适用的二维过滤器

[0, 1, 0]   [0, 1, 0]   [0, 0, 0]   [0, 0, 0]
[0, 0, 1] , [1, 0, 0] , [1, 0, 0] , [0, 0, 1]
[0, 0, 0]   [0, 0, 0]   [0, 1, 0]   [0, 1, 0]
Run Code Online (Sandbox Code Playgroud)

通过应用这些过滤器中的每一个,我们可以检测哪些单元具有至少两个具有所述要求的邻居,并用它们填充它们.


一般解决方案

def fill_bounding_boxes(a):
    '''
    Detects contiguous non-zero values in a 2D array
    and fills with ones all missing values in the 
    minimal rectangular boundaries that enclose all 
    non-zero entries, or "Minimal Bounding Boxes"
    ----
    a: np.array
       2D array. All values > 0 are considered to define
       the bounding boxes
    ----       
    Returns:
       2D array with missing values filled 

    '''
    import numpy as np
    from scipy.signal import convolve2d
    # Copy of the original array so it remains unmodified
    x = np.copy(a).clip(0,1)
    # Indicator. Set to false when no additional
    # changes in x are found
    is_diff = True
    # Filter to be used for the 2D convolution
    # The other filters are obtained by rotating this one
    f = np.array([[0,1,0], [0,0,1], [0,0,0]])
    # Runs while indicator is True
    while is_diff:
        x_ = np.copy(x)
        # Convolution between x and the filters
        # Only values with sums > 1 are kept, as it will mean
        # that they had minimum 2 non-zero neighbours
        # All filters are applied by rotating the initial filter
        x += sum((convolve2d(x, np.rot90(f, i), mode='same') > 1) 
                 for i in range(4))
        # Clip values between 0 and 1
        x = x.clip(0,1)
        # Set indicator to false if matrix x is unmodified
        if (x == x_).all():
            is_diff = False
    return x
Run Code Online (Sandbox Code Playgroud)

例子

让我们看一下建议示例的结果:

print(a)
array([[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, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]])

fill_bounding_boxes(a)
array([[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, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]])
Run Code Online (Sandbox Code Playgroud)

而对于另一个例子:

print(a)
array([[0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 1, 1],
       [1, 0, 0, 0, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 0, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0],
       [0, 1, 1, 0, 0, 1],
       [0, 0, 0, 0, 1, 0]])

fill_bounding_boxes(a)
array([[0, 0, 0, 0, 1, 1],
       [0, 0, 0, 0, 1, 1],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 0, 0],
       [0, 1, 1, 0, 0, 0],
       [0, 1, 1, 0, 1, 1],
       [0, 0, 0, 0, 1, 1]])
Run Code Online (Sandbox Code Playgroud)

  • 很好,可以。无论如何,你有我的支持,所以谢谢你的陪同:) (2认同)

mrk*_*rks 5

尽管前面的回答都很好,但是您可以使用以下方法scipy.ndimage

import numpy as np
from scipy import ndimage

def fill_bboxes(x):
    x_components, _ = ndimage.measurements.label(x, np.ones((3, 3)))
    bboxes = ndimage.measurements.find_objects(x_components)

    for bbox in bboxes:
        x[bbox] = 1

    return x
Run Code Online (Sandbox Code Playgroud)

ndimage.measurements.label用3x3“ ones”矩阵标记连接的组件,以定义邻居。find_objects然后确定每个组件的边界框,然后将其用于将所有元素设置为1。

  • 您可以确定每个对象的最小封闭矩形(即每个bbox中的“ bbox”,然后填充该矩形下的区域而不是bbox下的区域。据我所知,scipy没有预先创建的功能因此,您可以自己编写它,也可以使用类似OpenCVs的“ cv.minAreaRect”,但是无论哪种情况,您都必须决定填充时如何离散化。 (2认同)