当前位置: 首页 > 知识库问答 >
问题:

从表示图像的数组中排除周围零的最快方法是什么?

公西英叡
2023-03-14

我有一个二维数组,包含从创建的灰度图像。png如下所示:

import cv2

img = cv2.imread("./images/test.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

我想做的是提取一个子数组,它只包含包含数据的矩形——忽略图片周围的所有零。

例如,如果输入是:

  0   0   0   0   0   0   0   0
  0   0   0   0   0   0   0   0
  0   0 175   0   0   0  71   0
  0   0   0  12   8  54   0   0
  0   0   0   0 255   0   0   0
  0   0   0   2   0   0   0   0
  0   0   0   0   0   0   0   0
  0   0   0   0   0   0   0   0

那么输出应该是:

175   0   0   0  71
  0  12   8  54   0
  0   0 255   0   0
  0   2   0   0   0

我可以向前迭代这些行以找到第一个非零行,然后向后迭代这些行以找到最后一个非零行,然后对列重复相同的操作,然后使用该数据提取子数组,但我确信有更合适的方法来做同样的事,甚至可能会出现错误NumPy函数就是为此目的而设计的。

如果我在最短的代码和最快的执行之间进行选择,我会对最快的 代码执行更感兴趣。

编辑:
我没有包括最好的例子,因为中间可能有零行/列,就像这里一样:

输入:

  
     0   0   0   0   0   0   0   0
  0   0   0   0   0   0   0   0
  0   0 175   0   0   0  71   0
  0   0   0  12   8  54   0   0
  0   0   0   0 255   0   0   0
  0   0   0   0   0   0   0   0
  0   0   0   2   0   0   0   0
  0   0   0   0   0   0   0   0

  

输出:

  
   175   0   0   0  71
  0  12   8  54   0
  0   0 255   0   0
  0   0   0   0   0
  0   2   0   0   0

  

共有3个答案

濮俊美
2023-03-14

使用opencv函数更新这个更简单的方法实际上更快,而且可能比其他答案中给出的其他方法更快。

def crop_fastest(arr):
    return cv2.boundingRect(cv2.findNonZero(arr))

这将返回边界框的x、y、宽度和高度。在我的台式电脑上,对于我的旧代码1000循环,最好的是3:562µs/循环,而对于这个新代码10000循环,最好的是3:179µs/循环

又一次更新

正如Chupo_cro指出的,简单地调用cv2.boundingRect(arr)返回相同的结果,这似乎是由于该函数中的代码在内部进行转换。

以前的回答

可能有更快的方法。这个更简单的功能稍微快一点。

from scipy import ndimage
def crop_fast(arr):
    slice_x, slice_y = ndimage.find_objects(arr>0)[0]
    return arr[slice_x, slice_y]

要比较Drooze的代码和这个代码的速度,

arr = np.zeros(shape=(50000,6), dtype=np.uint8)
arr[2] = [9,8,0,0,1,1]
arr[1] = [0,3,0,0,1,1]

然后%timeit-crop(arr)在我的笔记本电脑上返回1000个循环,每个循环最好3:1.62毫秒%timeit-crop\u-fast(arr)在我的笔记本电脑上返回1000个循环,每个循环最好3:979µs。也就是说,crop\u fast()花费了crop()大约60%的时间。

长孙阳州
2023-03-14

我们可以使用artmax来获取开始、停止行和列索引,正如本文中详细讨论的那样。我们还打算使用布尔数组/掩码进行高效处理。因此,使用这些工具/想法,我们会有一个矢量化的解决方案,就像这样-

def remove_black_border(a): 
    # Mask of non-zeros
    mask = a!=0 # Use a >tolerance for a tolerance defining black border

    # Mask of non-zero rows and columns
    mask_row = mask.any(1)
    mask_col = mask.any(0)

    # First, last indices among the non-zero rows
    sr0,sr1 = mask_row.argmax(), len(mask_row) - mask_row[::-1].argmax()

    # First, last indices among the non-zero columns
    sc0,sc1 = mask_col.argmax(), len(mask_col) - mask_col[::-1].argmax()

    # Finally slice along the rows & cols with the start and stop indices to get 
    # cropped image. Slicing helps for an efficient operation.
    return a[sr0:sr1, sc0:sc1]

样品运行-

In [56]: a # Input image array
Out[56]: 
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, 175,   0,   0,   0,  71],
       [  0,   0,   0,   0,  12,   8,  54,   0],
       [  0,   0,   0,   0,   0, 255,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   2,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0]])

In [57]: out = remove_black_border(a)

In [58]: out
Out[58]: 
array([[175,   0,   0,   0,  71],
       [  0,  12,   8,  54,   0],
       [  0,   0, 255,   0,   0],
       [  0,   0,   0,   0,   0],
       [  0,   2,   0,   0,   0]])

内存效率:

输出是输入数组的视图,因此不需要额外的内存或复制,这有助于提高内存效率。让我们验证视图部分-

In [59]: np.shares_memory(a, out)
Out[59]: True

在更大图像上使用所有建议方法的计时

In [105]: # Setup for 1000x1000 2D image and 100 offsetted boundaries all across
     ...: np.random.seed(0)
     ...: a = np.zeros((1000,1000),dtype=np.uint8)
     ...: a[100:-100,100:-100] = np.random.randint(0,255,(800,800),dtype=np.uint8)

In [106]: %timeit crop_fast(a) # @fireant's soln
     ...: %timeit crop(a)      # @droooze's soln
     ...: %timeit remove_black_border(a) # from this post
100 loops, best of 3: 4.58 ms per loop
10 loops, best of 3: 127 ms per loop
10000 loops, best of 3: 155 µs per loop

徐俊人
2023-03-14

注意,不是一个OpenCV解决方案-这将适用于n维NumPySciPy数组。

(基于Divakar的回答,扩展到n维)

def crop_new(arr):

    mask = arr != 0
    n = mask.ndim
    dims = range(n)
    slices = [None]*n

    for i in dims:
        mask_i = mask.any(tuple(dims[:i] + dims[i+1:]))
        slices[i] = (mask_i.argmax(), len(mask_i) - mask_i[::-1].argmax())

    return arr[[slice(*s) for s in slices]]

速度测试:

In [42]: np.random.seed(0)

In [43]: a = np.zeros((30, 30, 30, 20),dtype=np.uint8)

In [44]: a[2:-2, 2:-2, 2:-2, 2:-2] = np.random.randint(0,255,(26,26,26,16),dtype
=np.uint8)

In [45]: timeit crop(a) # Old solution
1 loop, best of 3: 181 ms per loop

In [46]: timeit crop_fast(a) # modified fireant's solution for n-dimensions
100 loops, best of 3: 5 ms per loop

In [48]: timeit crop_new(a) # modified Divakar's solution for n-dimensions
100 loops, best of 3: 1.91 ms per loop

旧的解决方案

可以使用np.nonzero获取数组的索引。然后,该数组的边界框完全包含在索引的最大值和最小值中。

def _get_slice_bbox(arr):
    nonzero = np.nonzero(arr)
    return [(min(a), max(a)+1) for a in nonzero]

def crop(arr):
    slice_bbox = _get_slice_bbox(arr)
    return arr[[slice(*a) for a in slice_bbox]]

例如。

>>> img = np.array([[  0,   0,   0,   0,   0,   0,   0,   0],
                    [  0,   0,   0,   0,   0,   0,   0,   0],
                    [  0,   0, 175,   0,   0,   0,  71,   0],
                    [  0,   0,   0,  12,   8,  54,   0,   0],
                    [  0,   0,   0,   0, 255,   0,   0,   0],
                    [  0,   0,   0,   2,   0,   0,   0,   0],
                    [  0,   0,   0,   0,   0,   0,   0,   0],
                    [  0,   0,   0,   0,   0,   0,   0,   0]],  dtype='uint8')
>>> print crop(img)
[[175   0   0   0  71]
 [  0  12   8  54   0]
 [  0   0 255   0   0]
 [  0   2   0   0   0]]
 类似资料:
  • 问题内容: 我希望根据计算出的像素值绘制图像,以可视化某些数据。本质上,我希望采用彩色三元组的二维矩阵并将其渲染。 请注意,这不是图像处理,因为我既不变换现有图像也不做任何形式的全图像变换,它也不是矢量图形,因为我要渲染的图像没有预定的结构,我可能一次要产生一个像素的无定形斑点。 我现在需要渲染大约1kx1k像素的图像,但是可伸缩的东西会很有用。最终目标格式为PNG或任何其他无损格式。 目前,我一

  • 问题内容: 我有一个600万行的SQLite表。 从表中删除是很慢的。 删除表然后重新创建它似乎更快。 我正在使用它进行数据库导入。 删除表会是更好的方法还是有办法快速删除所有数据? 问题答案: 一个很大的不同是DML和DDL。对于数据库事务,这非常重要。最后的结果可能是相同的,但是这些操作有很大的不同。 如果您只是要了解性能,那么可以删除并重新创建表。如果您需要在导入中进行事务处理,则必须注意,

  • 问题内容: 我从书中得知,您应该为循环编写这样的代码: 因此不会每次都计算。 其他人则说编译器会对此做一些优化,因此您可以编写: 我只想知道哪种是最佳实践? 问题答案: 在使用大多数现代浏览器执行此测试之后… http://jsben.ch/dyM52 当前,最快的循环形式(我认为在语法上最明显)。 具有长度缓存的循环的标准 我想肯定的是,我为JavaScript引擎开发人员鼓掌。应该优化运行时间

  • 问题内容: 我正在用Python建立照片库,希望能够快速为高分辨率图像生成缩略图。 为各种图像源生成高质量缩略图的最快方法是什么? 我应该使用像imagemagick这样的外部库,还是有一种有效的内部方法来做到这一点? 调整大小后的图像的尺寸为(最大尺寸): 质量是一个问题,因为我想保留尽可能多的原始颜色并最大程度地减少压缩伪影。 谢谢。 问题答案: 您想要PIL轻松做到这一点 如果您迫切需要速度

  • 问题内容: 我有一个,并且想知道最快的方法是检查所有值是否均为零? 有没有比做更快的方法: 问题答案: 我先将所有字节加总后就重写了这个答案,但是这是不正确的,因为Java已经对字节进行了签名,因此我需要or。 另外,我已将JVM预热更改为正确。 最好的选择实际上是简单地遍历所有值。 我想您有三种主要选择: 或所有元素并检查总和。 进行无分支比较。 与分支进行比较。 我不知道使用Java添加字节的

  • 问题内容: 我有一些具有数据类型的列,我想预览(或浏览)那些表中的数据。在SQL Server Management Studio中使用时,图像列的值以十六进制显示。因为十六进制值对我没有用,最简单的预览这些图像的方法是什么? PS .:数据库不在我的控制之下,因此不能选择更改数据类型。 问题答案: 我将编写一个proc(或查询;请参见下文)以将二进制文件导出到文件系统中,然后使用任何旧的现成的照