Обнаружить и удалить нулевое заполнение в массиве изображений? - PullRequest
0 голосов
/ 04 августа 2020

Есть ли способ обнаружить и удалить нулевое заполнение в массиве изображений? В некотором смысле мой вопрос очень похож на это , за исключением того, что изображение уже повернуто, и я не знаю угол.

Я в основном обрезаю квадрат из большего изображения, которое может иметь нулевые отступы по краям (из-за смещения или поворота). Теперь возможно, что обрезка может содержать некоторые из этих отступов. Однако в таких случаях я хочу обрезать поле, в котором начинается край заполнения. Изображения находятся в формате CHW (может быть легко изменен на HW C).

Заполнение в этом случае будет равно 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, поэтому тензорные операции были бы отличными, но я пытаюсь выяснить любой алгоритм, например, используя numpy, который может достичь такого результата.

1 Ответ

0 голосов
/ 06 августа 2020

Если вы не против выбросить часть изображения и согласны с обильным кадрированием, если оно не содержит отступов, вы можете получить довольно эффективное решение:

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_max, x_low:x_max]

Если это должен быть максимально возможный урожай, тогда потребуется больше работы. По сути, мы должны проверять каждое смежное изображение, если оно содержит отступы, а затем сравнивать их все по размеру. изображение находится в (x1,y1), а нижний правый угол - в (x2, y2), тогда мы можем понять количество пикселей в подмассиве как тензор ранга 4 с размерами [y1, x1, y2, x2]. Мы устанавливаем количество пикселей равным 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 в tenorflow.

...