numpy 检测并删除图像数组中的零填充?

sycxhyv7  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(79)

有没有一种方法可以检测和删除图像数组中的零填充?在某种程度上,我的问题与this非常相似,除了图像已经旋转,我不知道Angular 。
我基本上是从一个较大的图像中裁剪出一个盒子,它的边缘可能没有填充(由于平移或旋转)。现在,作物可能包含一些这种填充物。但是,在这种情况下,我想在填充边缘开始的地方剪裁长方体。图像在CHW中(可以很容易地更改为HWC)。
在这种情况下,所有通道中的填充都为0。然而,由于旋转,有时可能0并不总是在数组中完全水平或垂直的条带中。有没有一种方法来检测是否有0一直到数组中的边缘,以及边缘从哪里开始?
示例1,其中arr是具有3个通道的图像,宽度和高度为4(3,4,4),并且裁剪在最右边缘包含垂直填充:

array([[[1., 1., 1., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 0.]],

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

       [[1., 1., 1., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 0.]]])

在这个例子中,我会像这样对数组进行切片,以去除零填充:arr[:, :, :-1]
示例2,我们在右上角有一些填充:

array([[[1., 1., 0., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]],

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

       [[1., 1., 0., 0.],
        [1., 1., 1., 0.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]]])

在这个例子中,我将通过返回arr2[:, 1:, :-1]来裁剪图像以去除任何填充。
我想在Tensorflow中做这件事,所以Tensor运算会很棒,但我正在试图找出任何算法,例如使用numpy,可以实现这个结果。

pokxtpni

pokxtpni1#

如果你不介意扔掉一些图像,并且只要它不包含填充,你就可以接受自由裁剪,你可以得到一个非常有效的解决方案:

pad_value = 0.0
arr = <test_image>
arr_masked = np.all(arr != pad_value , axis=0)
y_low = np.max(np.argmax(arr_masked, axis=0))
x_low = np.max(np.argmax(arr_masked, axis=1))
y_high = np.min(arr_masked.shape[0] - np.argmax(arr_masked[::-1, :], axis=0))
x_high = np.min(arr_masked.shape[1] - np.argmax(arr_masked[:, ::-1], axis=1))
arr[:, y_low:y_high, x_low:x_high]

如果它必须是最大的可能作物,那么需要更多的工作。本质上,我们必须检查每个相邻的子图像,如果它包含填充,然后比较它们的大小。

主旨:假设无填充子图像的左上角位于(x1,y1),右下角位于(x2, y2),那么我们可以将子阵列中的像素数量理解为维度为[y1, x1, y2, x2]的秩4Tensor。如果组合不是有效的子图像,即如果它具有负的宽度或高度,或者它包含填充像素,则我们将像素数设置为0。

pad_value = 0.0
arr = <test_image>

# indices for sub-image tensor
y = np.arange(arr_masked.shape[0])
x = np.arange(arr_masked.shape[1])
y1 = y[:, None, None, None]
y2 = y[None, None, :, None]
x1 = x[None, :, None, None]
x2 = x[None, None, None, :]

# coordinates of padded pixels
arr_masked = np.all(arr != pad_value , axis=0)
pad_north = np.argmax(arr_masked, axis=0)
pad_west = np.argmax(arr_masked, axis=1)
pad_south = arr_masked.shape[0] - np.argmax(arr_masked[::-1, :], axis=0)
pad_east = arr_masked.shape[1] - np.argmax(arr_masked[:, ::-1], axis=1)

is_padded = np.zeros_like(arr_masked)
is_padded[y[:, None] < pad_north[None, :]] = True
is_padded[y[:, None] >= pad_south[None, :]] = True
is_padded[x[None, :] < pad_west[:, None]] = True
is_padded[x[None, :] >= pad_east[:, None]] = True

y_padded, x_padded = np.where(is_padded)
y_padded = y_padded[None, None, None, None, :]
x_padded = x_padded[None, None, None, None, :]

# size of the sub-image
height = np.clip(y2 - y1 + 1, 0, None)
width = np.clip(x2 - x1 + 1, 0, None)
img_size = width * height

# sub-image contains at least one padded pixel
y_inside = np.logical_and(y1[..., None] <= y_padded, y_padded<= y2[..., None])
x_inside = np.logical_and(x1[..., None] <= x_padded, x_padded<= x2[..., None])
contains_border = np.any(np.logical_and(y_inside, x_inside), axis=-1)

# ignore sub-images containing padded pixels
img_size[contains_border] = 0

# find all largest sub-images
tmp = np.where(img_size == np.max(img_size))
rectangles = (tmp[0], tmp[1], tmp[2]+1, tmp[3]+1)

现在rectangles包含子图像的所有角点,这些角点具有最大数量的像素,而不包含任何边界像素。它已经相当矢量化了,所以你应该能够将它从numpy迁移到tensorflow。

rekjcdws

rekjcdws2#

请尝试此解决方案:

def remove_zero_pad(image):
    dummy = np.argwhere(image != 0) # assume blackground is zero
    max_y = dummy[:, 0].max()
    min_y = dummy[:, 0].min()
    min_x = dummy[:, 1].min()
    max_x = dummy[:, 1].max()
    crop_image = image[min_y:max_y, min_x:max_x]

    return crop_image
11dmarpk

11dmarpk3#

我的解决方案采用了不同的方法,假设填充部分是黑色或白色:
1.浏览图像中的每一行和每一列,看看平均值是0还是255
1.如果正好是任一数字,则从图像中完全删除该行或列。
1.这样做是为了整个画面
我使用Google Colab,所以可能有点不同:

import numpy as np
import cv2
from google.colab.patches import cv2_imshow

pic = cv2.imread(your image)
cv2_imshow(pic)
print(pic.shape)

x = pic.shape[0]
y = pic.shape[1]
dpadim = pic

#get rid of y padding
for i in range(x):
  row = pic[i,:,:]
  if(np.average(row)==0):
    dpadim = np.delete(dpadim,0,0)

#get rid of y padding
for j in range(y):
  col = pic[:,j,:]
  if(np.average(col)==0):
    dpadim = np.delete(dpadim,-1,1)

#delete row ignore by range function
x2 = dpadim.shape[0]
if((x-x2)>1):
  dpadim = np.delete(dpadim,0,0)
  print(x,x2)
cv2_imshow(dpadim)

这就是beforeafter的样子。

相关问题