从 numpy 数组中获取非零 ROI

问题描述

我想从图像中提取矩形 ROI。 图像包含单个连接的非零部分。

我需要它在运行时高效。

我在想:

  1. 沿每个方向求和。
  2. 查找第一个非零和最后一个非零。
  3. 相应地对图像进行切片。

有更好的方法吗?

我的代码

First 是查找第一个和最后一个非零的函数

import numpy as np

from PIL import Image

def first_last_nonzero(boolean_vector):
    first = last = -1
    for idx,val in enumerate(boolean_vector):
        if val == True and first == -1:
            first = idx
        if val == False and first != -1:
            last = idx
            return first,last

然后创建一个图像:

np_im = np.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   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0 255 154 251  60   0   0   0]
 [  0   0   0   0   4  66   0   0 255   0   0   0]
 [  0   0   0   0   0   0   0 134  48   0   0   0]
 [  0   0   0   0   0   0 236  70   0   0   0   0]
 [  0   0   0   0   1 255   0   0   0   0   0   0]
 [  0   0   0   0 255  24  24  24   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0]])

然后在每个轴的总和上运行我们的函数

y_start,y_end = first_last_nonzero(np.sum(np_im,1)>0)
x_start,x_end = first_last_nonzero(np.sum(np_im,0)>0)
cropped_np_im = np_im[y_start:y_end,x_start:x_end]

# show the cropped image
Image.fromarray(cropped_np_im).show()

这行得通,但可能有很多不必要的计算。 有一个更好的方法吗?或者更像pythonic的方式?

解决方法

您可以使用这篇文章中的功能: Numpy: How to find first non-zero value in every column of a numpy array?

def first_nonzero(arr,axis,invalid_val=-1):
    mask = arr!=0
    return np.where(mask.any(axis=axis),mask.argmax(axis=axis),invalid_val)

def last_nonzero(arr,invalid_val=-1):
    mask = arr!=0
    val = arr.shape[axis] - np.flip(mask,axis=axis).argmax(axis=axis) - 1
    return np.where(mask.any(axis=axis),val,invalid_val)


arr = np.array([
    [0,1,1],[0,0],0] ])

y_Min,y_Max,x_Min,x_Max = (0,0) 
y_Min = first_nonzero(arr,axis = 0,invalid_val = -1) 
y_Min = (y_Min[y_Min >= 0]).min() 
x_Min = first_nonzero(arr,axis = 1,invalid_val = -1) 
x_Min = (x_Min[x_Min >= 0]).min() 
y_Max = last_nonzero(arr,invalid_val = -1) 
y_Max = (y_Max[y_Max >= 0]).max() 
x_Max = last_nonzero(arr,invalid_val = -1) 
x_Max = (x_Max[x_Max >= 0]).max() 
print(x_Min) 
print(y_Min) 
print(x_Max) 
print(y_Max)

对于我的这个例子,代码将返回 1,5,4。 作为 Python 的一般经验法则:不惜一切代价尝试避免循环。根据我自己的经验,这句话在 100 个案例中有 99 个是正确的