Преобразование изображения RGB в индексное изображение
Я хочу преобразовать 3-канальное изображение RGB в индексное изображение с помощью Python. Он используется для обработки меток обучения глубокой сети для семантической сегментации. Под индексным изображением я подразумеваю, что у него один канал, и каждый пиксель является индексом, который должен начинаться с нуля. И, конечно, они должны иметь одинаковый размер. Преобразование основано на следующем отображении в Python dict:
color2index = {
(255, 255, 255) : 0,
(0, 0, 255) : 1,
(0, 255, 255) : 2,
(0, 255, 0) : 3,
(255, 255, 0) : 4,
(255, 0, 0) : 5
}
Я реализовал наивную функцию:
def im2index(im):
"""
turn a 3 channel RGB image to 1 channel index image
"""
assert len(im.shape) == 3
height, width, ch = im.shape
assert ch == 3
m_lable = np.zeros((height, width, 1), dtype=np.uint8)
for w in range(width):
for h in range(height):
b, g, r = im[h, w, :]
m_lable[h, w, :] = color2index[(r, g, b)]
return m_lable
Вход im
массив NumPy, созданный cv2.imread()
, Однако этот код очень медленный. Так как im
находится в массиве NumPy, я сначала попробовал ufunc
из NumPy с чем-то вроде этого:
RGB2index = np.frompyfunc(lambda x: color2index(tuple(x)))
indices = RGB2index(im)
Но оказывается, что ufunc
занимает только один элемент каждый раз. Мне удалось дать функции три аргумента (значение RGB) один раз.
Так есть ли другие способы сделать оптимизацию? Отображение не должно быть таким, если существует более эффективная структура данных. Я заметил, что доступ к Python dict не требует больших затрат времени, но приведение из массива к кортежу(который можно хэшировать) делает.
PS: одна идея, которую я получил, - реализовать ядро в CUDA. Но это было бы сложнее.
UPDATA1: Ответ Дана Машека работает отлично. Но сначала мы должны преобразовать изображение RGB в оттенки серого. Это может быть проблематично, когда два цвета имеют одинаковое значение в градациях серого.
Я вставляю рабочий код здесь. Надеюсь, что это может помочь другим.
lut = np.ones(256, dtype=np.uint8) * 255
lut[[255,29,179,150,226,76]] = np.arange(6, dtype=np.uint8)
im_out = cv2.LUT(cv2.cvtColor(im, cv2.COLOR_BGR2GRAY), lut)
7 ответов
Как насчет этого?
color2index = {
(255, 255, 255) : 0,
(0, 0, 255) : 1,
(0, 255, 255) : 2,
(0, 255, 0) : 3,
(255, 255, 0) : 4,
(255, 0, 0) : 5
}
def rgb2mask(img):
assert len(img.shape) == 3
height, width, ch = img.shape
assert ch == 3
W = np.power(256, [[0],[1],[2]])
img_id = img.dot(W).squeeze(-1)
values = np.unique(img_id)
mask = np.zeros(img_id.shape)
for i, c in enumerate(values):
try:
mask[img_id==c] = color2index[tuple(img[img_id==c][0])]
except:
pass
return mask
Тогда просто позвоните:
mask = rgb2mask(ing)
Подобно тому, что предлагали Армали и Мендрика, мне как-то пришлось немного подправить его, чтобы заставить его работать (возможно, полностью моя вина). Поэтому я просто хотел поделиться фрагментом, который работает.
COLORS = np.array([
[0, 0, 0],
[0, 0, 255],
[255, 0, 0]
])
W = np.power(255, [0, 1, 2])
HASHES = np.sum(W * COLORS, axis=-1)
HASH2COLOR = {h : c for h, c in zip(HASHES, COLORS)}
HASH2IDX = {h: i for i, h in enumerate(HASHES)}
def rgb2index(segmentation_rgb):
"""
turn a 3 channel RGB color to 1 channel index color
"""
s_shape = segmentation_rgb.shape
s_hashes = np.sum(W * segmentation_rgb, axis=-1)
func = lambda x: HASH2IDX[int(x)]
segmentation_idx = np.apply_along_axis(func, 0, s_hashes.reshape((1, -1)))
segmentation_idx = segmentation_idx.reshape(s_shape[:2])
return segmentation_idx
segmentation = np.array([[0, 0, 0], [0, 0, 255], [255, 0, 0]] * 3).reshape((3, 3, 3))
rgb2index(segmentation)
Код также доступен здесь: https://github.com/theRealSuperMario/supermariopy/blob/dev/scripts/rgb2labels.py
На самом деле цикл for занимает много времени.
binary_mask = (im_array[:,:,0] == 255) & (im_array[:,:,1] == 255) & (im_array[:,:,2] == 0)
может быть, приведенный выше код может помочь вам
Я реализовал наивную функцию: … Сначала попробовал
ufunc
of numpy на что-то вроде этого: …
Я предлагаю использовать еще более простую функцию, которая преобразует всего один пиксель:
def rgb2index(rgb):
"""
turn a 3 channel RGB color to 1 channel index color
"""
return color2index[tuple(rgb)]
Тогда использование подпрограммы numpy - хорошая идея, но нам не нужнаufunc
:
np.apply_along_axis(rgb2index, 2, im)
Вот numpy.apply_along_axis()
используется для применения наших rgb2index()
функция для срезов RGB по последней из трех осей (0, 1, 2) для всего изображения im
.
Можно было бы даже обойтись без функции и просто написать:
np.apply_along_axis(lambda rgb: color2index[tuple(rgb)], 2, im)
Вот небольшая служебная функция для преобразования изображений (np.array) в метки (индексы) для каждого пикселя, которые также могут быть в горячем кодировании:
def rgb2label(img, color_codes = None, one_hot_encode=False):
if color_codes is None:
color_codes = {val:i for i,val in enumerate(set( tuple(v) for m2d in img for v in m2d ))}
n_labels = len(color_codes)
result = np.ndarray(shape=img.shape[:2], dtype=int)
result[:,:] = -1
for rgb, idx in color_codes.items():
result[(img==rgb).all(2)] = idx
if one_hot_encode:
one_hot_labels = np.zeros((img.shape[0],img.shape[1],n_labels))
# one-hot encoding
for c in range(n_labels):
one_hot_labels[: , : , c ] = (result == c ).astype(int)
result = one_hot_labels
return result, color_codes
img = cv2.imread("input_rgb_for_labels.png")
img_labels, color_codes = rgb2label(img)
print(color_codes) # e.g. to see what the codebook is
img1 = cv2.imread("another_rgb_for_labels.png")
img1_labels, _ = rgb2label(img1, color_codes) # use the same codebook
Он вычисляет (и возвращает) кодовую книгу цветов, если None
в комплект поставки.
Если вы довольны использованием MATLAB, возможно, сохраните результат как
*.mat
и загрузка с
scipy.io.loadmat
- Здесь
rgb2ind
функция в MATLAB, которая делает именно то, что вы просите. Если нет, то его можно использовать в качестве вдохновения для аналогичной реализации на Python.
Вы проверяли библиотеку подушек https://python-pillow.org/? Насколько я помню, в нем есть несколько классов и методов для преобразования цветов. См.: https://pillow.readthedocs.io/en/4.0.x/reference/Image.html