#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