Numpy фильтрация с использованием массива

#python #arrays #numpy #image-processing #slice

#python #массивы #numpy #обработка изображений #срез

Вопрос:

Я знаю, что об этом спрашивали раньше, но, похоже, для моего конкретного варианта использования ничего нет.

У меня есть массив numpy obs , который представляет цветное изображение и имеет форму (252, 288, 3) .

Я хочу преобразовать каждый пиксель, который не является чисто черным, в чистый белый.

То, что я пробовал obs[obs != [0, 0, 0]] = [255, 255, 255] , но это дает следующее исключение:

ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 807 output values where the mask is true

Результат тот же с. obs[obs[:, :] != [0, 0, 0]] = [255, 255, 255] Кроме того, (obs[:, :] != [0, 0, 0]).shape есть (252, 288, 3) , и я не понимаю, почему это не просто (252, 288) (матрица bools).

Я думал об использовании obs[obs != 0] = 255 , но это не имело бы желаемого эффекта, поскольку пиксель, который является чисто зеленым ( [0, 255, 0] ), будет обрабатываться по компонентам и все равно будет [0, 255, 0] после фильтрации, вместо того, чтобы быть фактически белым ( [255, 255, 255] ) .

Почему то, что я пробовал до сих пор, не работает и как мне это сделать?

Комментарии:

1. разве это не то, чего вы хотите? obs[obs != 0] = 255

2. @Stepan я буквально объяснил это в вопросе. Я исправил несколько опечаток, теперь это должно быть более понятно.

3. Конечно, одноканального (в оттенках серого) или даже логического (True / False) результата достаточно для представления желаемого черно-белого вывода без необходимости увеличения оперативной памяти в 3 раза, как для результата RGB, содержащего только черное и белое?

Ответ №1:

Логическое индексирование, подобное obs[obs != [0, 0, 0]] возвращению одномерного массива со всеми элементами obs , удовлетворяющими заданному условию. Посмотрите на следующий пример:

 obs = np.array([
 [[88, 0,99],
  [ 0, 0, 0]],
 [[ 0, 0, 0],
  [88,77,66]]
])
 

obs != [0, 0, 0] возвращает логический массив:

 array([[[ True, False,  True],
        [False, False, False]],
       [[False, False, False],
        [ True,  True,  True]]])
 

а obs[obs != [0, 0, 0]] затем возвращает одномерный массив со всеми элементами, в которых находится маска True : array([88, 99, 88, 77, 66]) .

Итак, что вам нужно, это where проверить any , не равен ли цветовой компонент 0:

 np.where(obs.any(axis=-1, keepdims=True), 255, obs)
 

Результат:

 array([[[255, 255, 255],
        [  0,   0,   0]],
       [[  0,   0,   0],
        [255, 255, 255]]])
 

Обратите внимание, что вам необходимо keepdims=True включить трансляцию в исходную форму obs . В противном случае вам пришлось бы добавить потерянное измерение с помощью np.where(obs.any(-1)[...,np.newaxis], 255, obs) или np.where(np.atleast_3d(obs.any(-1)), 255, obs) , что менее элегантно.

Ответ №2:

Существует ряд возможностей в зависимости от того, что вы на самом деле хотите сделать. Давайте сначала сделаем установочный код (который является общим для всех возможностей), чтобы вы могли понять, что я имею в виду.

 #!/usr/bin/env python3

import numpy as np

# Make a repeatable random image
np.random.seed(764)
obs = np.random.randint(0,32,(252,288,3), dtype=np.uint8)
 

Для целей тестирования это изображение содержит чистые черные пиксели в следующих местах:

 obs[ 21, 267]
obs[ 28, 252]
obs[ 69, 127]
obs[ 98,   0]
obs[124, 210]
obs[133,  98]
obs[160,  81]
obs[167,  48]
obs[217, 237]
 

Теперь предположим, что вам нужна новая, чистая логическая маска True / False из черных пикселей, вы можете использовать:

 mask = obs.any(axis=-1)
 

Это решение обладает следующими характеристиками:

 time: 876 µs
mask.shape: (252,288)
mask.nbytes: 72576
mask.dtype: 'bool'
 

Впоследствии вы можете использовать и повторно использовать эту маску следующим образом:

 # Make masked pixels red
obs[mask,:] = [255,0,0]

# Make unmasked pixels cyan
obs[~mask,:] = [0,255,255]
 

введите описание изображения здесь


Теперь предположим, что вам нужно новое изображение в оттенках серого с черно-белыми пикселями, которое вы можете использовать:

 grey = obs.any(axis=-1) * np.uint8(255)
 

Это решение имеет следующие характеристики:

 time: 887 µs
grey.shape: (252,288)
grey.nbytes: 72576
grey.dtype: np.uint8
 

Теперь предположим, что вы хотите изменить на месте уже существующие «obs» на чисто черно-белые (но все же RGB):

 obs[obs.any(axis=-1),:] = [255,255,255]
 

Это решение имеет следующие характеристики:

 time: 1.98 ms
obs.shape: (252,288,3)
obs.nbytes: 217728
obs.dtype: np.uint8