【问题标题】:Remove empty 'rows' and 'columns' from 3D numpy pixel array从 3D numpy 像素数组中删除空的“行”和“列”
【发布时间】:2016-01-11 20:45:36
【问题描述】:

我基本上想用 numpy 裁剪图像——我有一个 3 维 numpy.ndarray 对象,即:

[ [0,0,0,0], [255,255,255,255], ....]
  [0,0,0,0], [255,255,255,255], ....] ]

我要删除空格的位置,在上下文中,已知空格是[0,0,0,0] 的整行或整列。

在这个例子中,让每个像素只是一个数字,我试图从本质上做到这一点:

鉴于此:*编辑:选择一个稍微复杂一点的例子来说明

[ [0,0,0,0,0,0] [0,0,1,1,1,0] [0,1,1,0,1,0] [0,0,0,1,1,0] [0,0,0,0,0,0]]

我正在尝试创建这个:

[ [0,1,1,1], [1,1,0,1], [0,0,1,1] ]

我可以通过循环来强制执行此操作,但直觉上我觉得 numpy 有更好的方法来执行此操作。

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    一般来说,您需要查看scipy.ndimage.labelscipy.ndimage.find_objects 以提取满足条件的连续区域的边界框。

    但是,在这种情况下,您可以使用“普通”numpy 轻松完成。

    我假设你在这里有一个nrows x ncols x nbands 数组。 nbands x nrows x ncols 的另一个约定也很常见,所以看看你的数组的形状。

    考虑到这一点,您可能会做类似的事情:

    mask = im == 0
    all_white = mask.sum(axis=2) == 0
    rows = np.flatnonzero((~all_white).sum(axis=1))
    cols = np.flatnonzero((~all_white).sum(axis=0))
    
    crop = im[rows.min():rows.max()+1, cols.min():cols.max()+1, :]
    

    对于您的 2D 示例,它看起来像:

    import numpy as np
    
    im = np.array([[0,0,0,0,0,0],
                   [0,0,1,1,1,0],
                   [0,1,1,0,1,0],
                   [0,0,0,1,1,0],
                   [0,0,0,0,0,0]])
    
    mask = im == 0
    rows = np.flatnonzero((~mask).sum(axis=1))
    cols = np.flatnonzero((~mask).sum(axis=0))
    
    crop = im[rows.min():rows.max()+1, cols.min():cols.max()+1]
    print crop
    

    让我们稍微分解一下 2D 示例。

    In [1]: import numpy as np
    
    In [2]: im = np.array([[0,0,0,0,0,0],
       ...:                [0,0,1,1,1,0],
       ...:                [0,1,1,0,1,0],
       ...:                [0,0,0,1,1,0],
       ...:                [0,0,0,0,0,0]])
    

    好的,现在让我们创建一个满足我们条件的布尔数组:

    In [3]: mask = im == 0
    
    In [4]: mask
    Out[4]:
    array([[ True,  True,  True,  True,  True,  True],
           [ True,  True, False, False, False,  True],
           [ True, False, False,  True, False,  True],
           [ True,  True,  True, False, False,  True],
           [ True,  True,  True,  True,  True,  True]], dtype=bool)
    

    另外,请注意 ~ 运算符在布尔数组上的作用类似于 logical_not

    In [5]: ~mask
    Out[5]:
    array([[False, False, False, False, False, False],
           [False, False,  True,  True,  True, False],
           [False,  True,  True, False,  True, False],
           [False, False, False,  True,  True, False],
           [False, False, False, False, False, False]], dtype=bool)
    

    考虑到这一点,要找到所有元素都为假的行,我们可以跨列求和:

    In [6]: (~mask).sum(axis=1)
    Out[6]: array([0, 3, 3, 2, 0])
    

    如果没有元素为真,我们将得到一个 0。

    类似地,要找到所有元素都为假的列,我们可以跨行求和:

    In [7]: (~mask).sum(axis=0)
    Out[7]: array([0, 1, 2, 2, 3, 0])
    

    现在我们需要做的就是找到不为零的第一个和最后一个。 np.flatnonzerononzero 简单一点,在这种情况下:

    In [8]: np.flatnonzero((~mask).sum(axis=1))
    Out[8]: array([1, 2, 3])
    
    In [9]: np.flatnonzero((~mask).sum(axis=0))
    Out[9]: array([1, 2, 3, 4])
    

    然后,您可以轻松地根据最小/最大非零元素切出区域:

    In [10]: rows = np.flatnonzero((~mask).sum(axis=1))
    
    In [11]: cols = np.flatnonzero((~mask).sum(axis=0))
    
    In [12]: im[rows.min():rows.max()+1, cols.min():cols.max()+1]
    Out[12]:
    array([[0, 1, 1, 1],
           [1, 1, 0, 1],
           [0, 0, 1, 1]])
    

    【讨论】:

    • 如果空行和列位于使用此代码的数据之间,是否可以删除它们。在我看来,代码似乎只是在修剪零的外部“边缘”。
    【解决方案2】:

    对任意维度实现此功能的一种方法是:

    import numpy as np
    
    def trim(arr, mask):
        bounding_box = tuple(
            slice(np.min(indexes), np.max(indexes) + 1)
            for indexes in np.where(mask))
        return arr[bounding_box]
    

    FlyingCircus 提供了一个稍微灵活的解决方案(您可以在其中指明要作用于哪个轴)(免责声明:我是该软件包的主要作者)。

    【讨论】:

    • 制作制作使用:mask = arr != 0
    【解决方案3】:

    您可以使用np.nonzero 函数找到您的零值,然后从原始数组中切出非零元素并重新整形为您想要的:

    import numpy as np
    n = np.array([ [0,0,0,0,0,0],
       [0,0,1,1,1,0],
       [0,0,1,1,1,0],
       [0,0,1,1,1,0],
       [0,0,0,0,0,0]])
    
    elems = n[n.nonzero()]
    
    In [415]: elems
    Out[415]: array([1, 1, 1, 1, 1, 1, 1, 1, 1])
    
    In [416]: elems.reshape(3,3)
    Out[416]: 
    array([[1, 1, 1],
           [1, 1, 1],
           [1, 1, 1]])
    

    【讨论】:

    • 假设区域是已知的矩形。
    • 有些零条目的列呢?
    • 好吧,它是一个矩形,因为我只想删除完全空的行/列。
    • @Jonline - 是的,但您可能在要裁剪的区域内有一些白色区域。在这种情况下这不起作用。您还必须事先知道尺寸。
    • @JoeKington 你的意思是我必须知道我打算保留的区域的大小吗?因为这确实是一个两难境地。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多