zax2002
@zax2002

Как конвертировать изображение в свою палитру?

Конечная цель такова: Есть палитра из 144 цветов. Надо преобразовать RGB картинку в одномерный массив индексов цветов из этой палитры.

Например:
palette = [ [a, b, c], [a, a, b], [c, a, b], [b, a, c], [c, c, a] ]

rgb_img = [
	[ (a+3, b-2, c-1), (a, a+1, b-2), (c+2, a, b-3) ],
	[ (b, a-2, c+1), (c, c+2, a-1), (b-1, a, c-3) ],
	[ (a, a+1, b-2), (a+2, b-2, c-1), (c+1, c+1, a-2) ]
]

>> [ 0, 1, 2, 3, 4, 3, 1, 0, 4 ]

Прибавление случайных чисел означает цвета, которых нет в палитре.

Я делаю через Pillow так:
from PIL import Image
import requests

pillow_palette = [0,0,0,0,0,0,0,0,0,0,0,0,89,125,39,109,153,48,127,178,56,67,94,29,174,164,115,213,201,140,247,233,163,130,123,86,138,138,138,169,169,169,197,197,197,104,104,104,180,0,0,220,0,0,255,0,0,135,0,0,112,112,180,138,138,220,160,160,255,84,84,135,117,117,117,144,144,144,167,167,167,88,88,88,0,87,0,0,106,0,0,124,0,0,65,0,180,180,180,220,220,220,255,255,255,135,135,135,115,118,129,141,144,158,164,168,184,86,88,97,129,74,33,157,91,40,183,106,47,96,56,24,79,79,79,96,96,96,112,112,112,59,59,59,101,84,51,123,103,62,143,119,72,76,63,38,45,45,180,55,55,220,64,64,255,33,33,135,180,177,172,220,217,211,255,252,245,135,133,129,152,89,36,186,109,44,216,127,51,114,67,27,125,53,152,153,65,186,178,76,216,94,40,114,72,108,152,88,132,186,102,153,216,54,81,114,161,161,36,197,197,44,229,229,51,121,121,27,89,144,17,109,176,21,127,204,25,67,108,13,170,89,116,208,109,142,242,127,165,128,67,87,53,53,53,65,65,65,76,76,76,40,40,40,108,108,108,132,132,132,153,153,153,81,81,81,53,89,108,65,109,132,76,127,153,40,67,81,89,44,125,109,54,153,127,63,178,67,33,94,36,53,125,44,65,153,51,76,178,27,40,94,72,53,36,88,65,44,102,76,51,54,40,27,72,89,36,88,109,44,102,127,51,54,67,27,108,36,36,132,44,44,153,51,51,81,27,27,17,17,17,21,21,21,25,25,25,13,13,13,176,168,54,215,205,66,250,238,77,132,126,40,64,154,150,79,188,183,92,219,213,48,115,112,52,90,180,63,110,220,74,128,255,39,67,135,0,153,40,0,187,50,0,217,58,0,114,30,90,59,34,110,73,41,127,85,48,67,44,25,79,1,0,96,1,0,112,2,0,59,1,0]
map_palette = [(0,0,0),(0,0,0),(0,0,0),(0,0,0),(89,125,39),(109,153,48),(127,178,56),(67,94,29),(174,164,115),(213,201,140),(247,233,163),(130,123,86),(138,138,138),(169,169,169),(197,197,197),(104,104,104),(180,0,0),(220,0,0),(255,0,0),(135,0,0),(112,112,180),(138,138,220),(160,160,255),(84,84,135),(117,117,117),(144,144,144),(167,167,167),(88,88,88),(0,87,0),(0,106,0),(0,124,0),(0,65,0),(180,180,180),(220,220,220),(255,255,255),(135,135,135),(115,118,129),(141,144,158),(164,168,184),(86,88,97),(129,74,33),(157,91,40),(183,106,47),(96,56,24),(79,79,79),(96,96,96),(112,112,112),(59,59,59),(101,84,51),(123,103,62),(143,119,72),(76,63,38),(45,45,180),(55,55,220),(64,64,255),(33,33,135),(180,177,172),(220,217,211),(255,252,245),(135,133,129),(152,89,36),(186,109,44),(216,127,51),(114,67,27),(125,53,152),(153,65,186),(178,76,216),(94,40,114),(72,108,152),(88,132,186),(102,153,216),(54,81,114),(161,161,36),(197,197,44),(229,229,51),(121,121,27),(89,144,17),(109,176,21),(127,204,25),(67,108,13),(170,89,116),(208,109,142),(242,127,165),(128,67,87),(53,53,53),(65,65,65),(76,76,76),(40,40,40),(108,108,108),(132,132,132),(153,153,153),(81,81,81),(53,89,108),(65,109,132),(76,127,153),(40,67,81),(89,44,125),(109,54,153),(127,63,178),(67,33,94),(36,53,125),(44,65,153),(51,76,178),(27,40,94),(72,53,36),(88,65,44),(102,76,51),(54,40,27),(72,89,36),(88,109,44),(102,127,51),(54,67,27),(108,36,36),(132,44,44),(153,51,51),(81,27,27),(17,17,17),(21,21,21),(25,25,25),(13,13,13),(176,168,54),(215,205,66),(250,238,77),(132,126,40),(64,154,150),(79,188,183),(92,219,213),(48,115,112),(52,90,180),(63,110,220),(74,128,255),(39,67,135),(0,153,40),(0,187,50),(0,217,58),(0,114,30),(90,59,34),(110,73,41),(127,85,48),(67,44,25),(79,1,0),(96,1,0),(112,2,0),(59,1,0)]

url = "https://img.mvideo.ru/Pdb/20027979b.jpg"

img = Image.open(requests.get(url, stream=True).raw).resize((128, 128))

palette_img = Image.new("P", (128, 128))
palette_img.putpalette(pillow_palette)

palette_img.load()
img.load()

im = img.im.convert("P", 1, palette_img.im)
img = img._new(im).convert("RGB")

mx = img.load()

output = []

for x in range(128):
	for z in range(128):
		output.append(map_palette.index(mx[z, x]))

print(output)

Но получаю ошибку:
Traceback (most recent call last):
  File "map_test.py", line 26, in <module>
    output.append(map_palette.index(mx[z, x]))
ValueError: (252, 252, 252) is not in list

Я думаю, что при конвертации обратно в RGB происходит какое-то типа сжатие или что-то вроде того и цвета чуть съезжают.

В чём моя ошибка? Наверняка есть способ намного проще, подскажите пожалуйста.
  • Вопрос задан
  • 237 просмотров
Решения вопроса 1
adugin
@adugin Куратор тега Python
По сути для каждой точки изображения надо найти ближайший цвет из палитры. Вот решение "в лоб":
import numpy as np

a, b, c = np.random.randint(0, 256, 3, dtype=np.uint8)

palette = [ [a, b, c], [a, a, b], [c, a, b], [b, a, c], [c, c, a] ]

rgb_img = [
  [ (a+3, b-2, c-1), (a, a+1, b-2), (c+2, a, b-3) ],
  [ (b, a-2, c+1), (c, c+2, a-1), (b-1, a, c-3) ],
  [ (a, a+1, b-2), (a+2, b-2, c-1), (c+1, c+1, a-2) ]
]

palette = np.array(palette).reshape(1, 1, 5, 3)
rgb_img = np.array(rgb_img).reshape(3, 3, 1, 3)
indices = np.linalg.norm(palette - rgb_img, axis=-1).argmin(axis=-1).ravel()

print(indices)

Результат:
[0 1 2 3 4 3 1 0 4]

Ещё вариант:
from sklearn.cluster import KMeans

a, b, c = np.random.randint(0, 256, 3, dtype=np.uint8)

palette = [ [a, b, c], [a, a, b], [c, a, b], [b, a, c], [c, c, a] ]

rgb_img = [
  [ (a+3, b-2, c-1), (a, a+1, b-2), (c+2, a, b-3) ],
  [ (b, a-2, c+1), (c, c+2, a-1), (b-1, a, c-3) ],
  [ (a, a+1, b-2), (a+2, b-2, c-1), (c+1, c+1, a-2) ]
]

result = (
    KMeans(n_clusters=len(palette), init=np.array(palette), n_init=1)
    .fit_transform(np.array(rgb_img).reshape(-1, 3))
    .argmin(axis=-1)
)

UPD Пример оригинальной картинки и преобразованной в значения [0, 32, 64, 96, 128, 160, 192, 224, 255]:
5dcad4279a3f3938031005.jpeg
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы