Python / numpy:删除3D数组的空(零)边框

时间:2019-02-07 07:06:55

标签: python numpy image-processing multidimensional-array

我有一个3D numpy数组。可以将其视为图像(确切地说,它是场点的值)。我想在所有尺寸中删除边框(0个值,请注意可能有负值)。限制是对于所有分子,例如,尺寸保持相同。我只想删除边框,只要该维度中的“最大”条目仍在边框内即可。因此,需要考虑整个数据集(较小,大小不成问题)。

二维示例:

0  0  0  0  0
0  1  0  0  0
0  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  1  0  0
0  0  0  1  0
0  0  0  1  0

这是第一行,应该删除最左边和最右边的列。在整个数据集中,它们仅包含0个值。

结果将在下面:

1  0  0
1  1  0
0  0  0
0  0  0

0  0  0
0  1  0
0  0  1
0  0  1

由于我不是专家,我在定义算法来满足自己的需求时遇到了麻烦。我将需要在每个维度中找到不为0的最小和最大索引,然后使用该索引修剪数组。

类似于this,但在3D模式下,裁剪必须考虑整个数据集。

我该如何实现?

2019年2月13日更新:

因此,我在这里尝试了3个答案(其中一个似乎已使用zip删除了),Martins和norok2s答案。输出尺寸是相同的,所以我假设它们都可以工作。

我选择Martins解决方案是因为我可以轻松提取边界框以将其应用于测试集。

2月25日更新:

如果仍然有人在观察这一点,我想进一步提出意见。如前所述,这些实际上不是图像,而是“字段值”,它表示浮动图像,而不是灰度图像(uint8),这意味着我至少需要使用float16,这仅需要太多内存。 (我有48GB可用空间,但即使对于50%的训练集,这也不够。)

3 个答案:

答案 0 :(得分:3)

尝试一下:-它是一种主要算法。我不清楚您要从示例中提取哪些方面,但是下面的算法应该很容易让您根据需要进行修改

注意:此算法提取所有“零值”边界都被“删除”的多维数据集。因此,在立方体的每一侧都有一些值!= 0

import numpy as np

# testing dataset
d = np.zeros(shape = [5,5,5]) 

# fill some values
d[3,2,1]=1
d[3,3,1]=1
d[1,3,1]=1
d[1,3,4]=1

# find indexes in all axis
xs,ys,zs = np.where(d!=0) 
# for 4D object
# xs,ys,zs,as = np.where(d!=0) 

# extract cube with extreme limits of where are the values != 0
result = d[min(xs):max(xs)+1,min(ys):max(ys)+1,min(zs):max(zs)+1] 
# for 4D object
# result = d[min(xs):max(xs)+1,min(ys):max(ys)+1,min(zs):max(zs)+1,min(as):max(as)+1]

>>> result.shape
(3, 2, 4)

情况1:

d = np.zeros(shape = [5,5,5])

d[3,2,1]=1
# ...  just one value

>>> result.shape # works

(1,1,1)

情况2:#错误情况-仅为零-生成的3D没有尺寸->错误

d = np.zeros(shape = [5,5,5]) # no values except zeros
>>> result.shape


Traceback (most recent call last):
  File "C:\Users\zzz\Desktop\py.py", line 7, in <module>
    result = d[min(xs):max(xs)+1,min(ys):max(ys)+1,min(zs):max(zs)+1]
ValueError: min() arg is an empty sequence

编辑:由于我的解决方案没有得到足够的热爱和理解,因此我将向第4维主体提供示例,其中第3维对于图像是免费的,第4维是用于存储图像的

import numpy as np


class ImageContainer(object):
    def __init__(self,first_image):
        self.container =  np.uint8(np.expand_dims(np.array(first_image), axis=0))

    def add_image(self,image):
        #print(image.shape)
        temp = np.uint8(np.expand_dims(np.array(image), axis=0))
        #print(temp.shape)
        self.container  = np.concatenate((self.container,temp),axis = 0)
        print('container shape',self.container.shape)

# Create image container storage

image = np.zeros(shape = [5,5,3]) # some image
image[2,2,1]=1 # put something random in it
container = ImageContainer(image)
image = np.zeros(shape = [5,5,3]) # some image
image[2,2,2]=1
container.add_image(image)
image = np.zeros(shape = [5,5,3]) # some image
image[2,3,0]=1    # if we set [2,2,0] = 1, we can expect all images will have just 1x1 pixel size
container.add_image(image)
image = np.zeros(shape = [5,5,3]) # some image
image[2,2,1]=1
container.add_image(image)
>>> container.container.shape
('container shape', (4, 5, 5, 3)) # 4 images, size 5x5, 3 channels


# remove borders to all images at once
xs,ys,zs,zzs = np.where(container.container!=0) 
# for 4D object

# extract cube with extreme limits of where are the values != 0
result = container.container[min(xs):max(xs)+1,min(ys):max(ys)+1,min(zs):max(zs)+1,min(zzs):max(zzs)+1]

>>> print('Final shape:',result.shape) 


('Final shape', (4, 1, 2, 3)) # 4 images, size: 1x2, 3 channels

答案 1 :(得分:1)

更新:

基于Martin使用min / max和np.where的解决方案,但是将其推广到任何维度,您可以通过以下方式实现:

def bounds_per_dimension(ndarray):
    return map(
        lambda e: range(e.min(), e.max() + 1),
        np.where(ndarray != 0)
    )

def zero_trim_ndarray(ndarray):
    return ndarray[np.ix_(*bounds_per_dimension(ndarray))]

d = np.array([[
    [0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0],
    [0, 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, 1, 0, 0],
    [0, 0, 0, 1, 0],
    [0, 0, 0, 1, 0],
]])

zero_trim_ndarray(d)

答案 2 :(得分:0)

您可能会看到问题是通过将所有形状放在一起形成一个数组来修剪数组中的特定边界框。

因此,如果您具有n维修剪功能,解决方案就是将其应用。

一种实现方式是:

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免责声明:我是软件包的主要作者)中提供了一种稍微更灵活的解决方案(您可以在其中指示要作用于哪个轴)。

因此,如果您有n维数组的列表(在arrs中),则可以先使用np.stack()对其进行堆叠,然后修剪结果:

import numpy as np

arr = np.stack(arrs, -1)
trimmed_arr = trim(arr, arr != 0)

然后可以使用np.split()将其分开,例如:

trimmed_list = np.split(trimmed_arr, arr.shape[-1], -1)

编辑:

我只是意识到这与其他答案使用的方法基本上相同,只是对我而言看起来更干净。

相关问题