deadrime
@deadrime
Fullstack web developer

Как создать палитру цветов для bmp изображения?

Допустим у меня есть 24 битное изображение, например такое
b5ca4b83eb674172a0718185e4e355fc.bmp
Я хочу переконвертировать его цветное палитровое, скажем 4 бита, 16 цветов.
Т.е получить что-то вроде
70a2a15bb4fe477b8ad5d176d28cfab6.jpg

Как мне создать такую палитру?

Я попытался сделать так - разбить изображение на 16 равных частей и для каждой части найти свой средний цвет(ну т.е среднее арифметическое для R,G,B)
Но цвета в полученной палитры вышли грязными и тусклыми что-ли. Вот такая вот палитра вышла -
65bf9687b5ed4e59a9e178a448b3deb7.png
Т.е тут нет ярко выраженных цветов , таких как черного, темно-коричневого,нормального оранжевого, зеленого..

Потом еще проблема в том - как выбрать нужный цвет в палитре.. Я отсортировал по яркости и выбирал тоже по ближайшей яркости, но скорее всего это неправильно..

Вот мой код, подскажите пожалуйста, как дополнить/переделать, чтобы получше работало)
void Image::create_color_palette(int k) {

	int stepW=BMInfoHeader.Width/sqrt(k);//ширина области, в которой буду икать цвет
	int stepH=BMInfoHeader.Height/sqrt(k);//высота области, в которой буду икать цвет

	int R=0,G=0,B=0,step=0;

	Palette=new RGBQUAD[k];//выделяю память на палитру

	for (int i=0;i<BMInfoHeader.Height;i=i+stepH)
	for (int j=0;j<BMInfoHeader.Width;j=j+stepW)
	{
		for (int i2 = i; (i2 < i+stepH) && (i2<BMInfoHeader.Height); i2++)//если stepH=stepW=60 - то цвет будет искаться в квадратике 60x60
		for(int j2 = j;(j2<j+stepW)&& (j2<BMInfoHeader.Width);j2++)//
		{
			R+=Rgbtriple[i2][j2].Red;
			G+=Rgbtriple[i2][j2].Green;
			B+=Rgbtriple[i2][j2].Blue;
		}
		R/=stepH*stepW;//среднее арифметическое
		G/=stepH*stepW;
		B/=stepH*stepW;

		Palette[step].Red=R;//добавляю нужный цвет в палитру
		Palette[step].Green=G;
		Palette[step].Blue=B;
		step++;
	}

	RGBQUAD tmp;
for (int i = 1; i < k; i++)//сортировка по яркости
	for(int j = k-1; j > i; j-- )
	{
		if(Y_px_4(Palette[j-1])>=Y_px_4(Palette[j]))//Y_px_4 - яркость пикселя RGBQUAD
		{
			tmp=Palette[j-1];
			Palette[j-1]=Palette[j];
			Palette[j]=tmp;
		}
	}
}

int Image::find_color_in_palette(int k,RGBTRIPLE px)
{
	for(int i=0;i<k;i++)
	{
		if (Y_px_4(Palette[i])>=Y_px_3(px))
		{
			return i;
		}
	}
	return k-1;
}


Вот результат работы моего кода -
13a17dd398e84a6ea55a0412b2c35978.bmp
UPD - после нормальной сортировки)
1d14ce45794446b4acda87ce2b637bf1.bmp
UPD - через сумму квадратов отклонений
973d112b696d431faeee311c61812a81.bmp
UPD - после увеличения контрастности палитры
4689a7aac8374056af0d8657a3cea949.bmp
  • Вопрос задан
  • 2379 просмотров
Пригласить эксперта
Ответы на вопрос 4
alsopub
@alsopub
Вот тут кажется много полезной информации с кодом:
www.enlight.ru/demo/faq/smth.phtml?query=alg_clr_dith
Ключевые слова: Кластеризация (Quantization), Dithering.
Можно, даже, взять усредненную (универсальную) палитру, но результат будет не всегда хорошим.

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

Поиск цвета в палитре нужно вести только не только по яркости. Скорее по сумме квадратов отклонений по каждой цветовой компоненте.

И еще - у вас не сортировка по яркости, у вас один проход с перестановкой, он не дает полную сортировку.
Скорее всего из-за этого у вас и получилось такое тусклое итоговое изображение. Но яркость - все равно не то, синий и красный цвета будут одинаково яркими, но совсем разными.
Ответ написан
Можно применить Median cut.
Суть алгоритма заключается в следующем:
  1. Создаём одну корзину (вектор) со всеми пикселами изображения
  2. Определяем цветовую компоненту (R, G или B), по которой диапазон значений (max - min) в корзине наибольший
  3. Находим медиану этого цвета и разбиваем корзину на две
  4. Повторяем шаги 2 и 3 до достижения нужного числа корзин
  5. Далее можно, например, найти в каждой корзине средний цвет и создать std::unordered_map из текущего цвета в усредненный
Ответ написан
Комментировать
@sim31r
1) Самый простой вариант, мне кажется, случайно выбирать цвета (например случайные пиксели изображения) и искать ошибку отображения (сравнивать или со всеми пикселями или с небольшой частью, 1% например), потом следующая итерация с небольшим изменением и так далее пока не будет достигнут минимум ошибки, очень просто, в цикле перебирать варианты. Мне такой подход нравится, что нет математического анализа, работает компьютер, программист отдыхает ))
2) Высокое качество картинки из 16 цветов, почти неотличимое от оригинала, достигается добавлением цифрового шума, недостающие цвета получаются слиянием цветов рядом расположенных пикселей, и градиенты пропадают, алгоритм, конечно сложнее
tigra.bmp

3) Посмотрел на картинку что дал ваш код, её можно улучшить покрутив контраст, сделав темные участки темнее, светлые светлее. Алгоритм изначально теряет контрастность, усредняя слишком большие участки изображения.
Ответ написан
Комментировать
Taraflex
@Taraflex
Ищу работу. Контакты в профиле.
https://pngquant.org/lib/
Функция liq_quantize_image
Ответ написан
Комментировать
Ваш ответ на вопрос

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

Похожие вопросы