Перейти к содержимому
Compvision.ru
thequelby

Уменьшение объема памяти посредством уменьшения кол-ва цветов

Recommended Posts

Добрый день!

Есть определенная программа, которая определяет доминирующие цвета на изображении и выводит новое изображение в найденных цветах (из RGB в HSV). Теперь к самой проблеме: не пойму как изменить программу так, чтобы изменить количество доминирующих цветов (то есть использовать, например, только 20% доминирующих цветов) и привязать это к кнопкам. Программа используется для уменьшения объема памяти, занимаемого изображением. Заранее спасибо за помощь!

//
// определение преобладающих цветов на изображении
// при помощи k-Means
//

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/imgcodecs.hpp"

#include "opencv2/highgui/highgui.hpp"

#include <stdlib.h>

#include <stdio.h>

#include <vector>
#include <algorithm>

//
// определение преобладающих цветов на изображении
// через пространство HSV
// + накопление данных по соответствующим цветам из RGB

#define RECT_COLORS_SIZE 10

// получение пикселя изображения (по типу картинки и координатам)
#define CV_PIXEL(type,img,x,y) (((type*)((img)->imageData+(y)*(img)->widthStep))+(x)*(img)->nChannels)

// Various color types
//	    0			1	  2		 3		4		 5		  6		7		8		9			10
enum { cBLACK = 0, cWHITE, cGREY, cRED, cORANGE, cYELLOW, cGREEN, cAQUA, cBLUE, cPURPLE, NUM_COLOR_TYPES };
char* sCTypes[NUM_COLOR_TYPES] = { "Black", "White","Grey","Red","Orange","Yellow","Green","Aqua","Blue","Purple" };
uchar cCTHue[NUM_COLOR_TYPES] = { 0,       0,      0,     0,     20,      30,      60,    85,   120,    138 };
uchar cCTSat[NUM_COLOR_TYPES] = { 0,       0,      0,    255,   255,     255,     255,   255,   255,    255 };
uchar cCTVal[NUM_COLOR_TYPES] = { 0,      255,    120,   255,   255,     255,     255,   255,   255,    255 };

typedef unsigned int uint;

// число пикселей данного цвета на изображении 
uint colorCount[NUM_COLOR_TYPES] = { 0,		0,		0,		0,		0,		0,		0,		0,		0,		0 };



// определяем тип пикселя
int getPixelColorType(int H, int S, int V)
{
	int color = cBLACK;

#if 1
	if (V < 75)
		color = cBLACK;
	else if (V > 190 && S < 27)
		color = cWHITE;
	else if (S < 53 && V < 185)
		color = cGREY;
	else
#endif
	{
		if (H < 7)
			color = cRED;
		else if (H < 25)
			color = cORANGE;
		else if (H < 34)
			color = cYELLOW;
		else if (H < 73)
			color = cGREEN;
		else if (H < 102)
			color = cAQUA;
		else if (H < 140)
			color = cBLUE;
		else if (H < 170)
			color = cPURPLE;
		else	// полный круг
			color = cRED;	// обратно к красному
	}
	return color;
}

// сортировка цветов по количеству
bool colors_sort(std::pair< int, uint > a, std::pair< int, uint > b)
{
	return (a.second > b.second);
}

int main(int argc, char* argv[])
{
	// для хранения изображения
	IplImage* image = 0, *hsv = 0, *dst = 0, *dst2 = 0, *color_indexes = 0, *dst3 = 0;
	IplImage* image1 = 0, *hsv1 = 0, *dst4 = 0, *dst5 = 0, *color_indexes1 = 0, *dst6 = 0;
	//
	// загрузка изображения
	//

	char img_name[] = "Image0.jpg";
	
	
	// имя картинки задаётся первым параметром
	char* image_filename = argc >= 2 ? argv[1] : img_name;

	// получаем картинку
	image = cvLoadImage(image_filename, 1);

	printf("[i] image: %s\n", image_filename);
	if (!image) {
		printf("[!] Error: cant load test image: %s\n", image_filename);
		return -1;
	}

	// показываем картинку
	cvNamedWindow("image");
	cvShowImage("image", image);
	printf("[i] image size:  %d bytes\n", image->imageSize);
	//cvCreateButton("button6", callbackButton2, (void*) "test", CV_PUSH_BUTTON, 0);  /* <--- pass the string literal "test" as third argument */
	//
	// преобразуем изображение в HSV 
	//
	hsv = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 3);
	cvCvtColor(image, hsv, CV_BGR2HSV);

	// картинки для хранения результатов
	dst = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 3);
	dst2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 3);
	color_indexes = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1); //для хранения индексов цвета

																	  // для хранения RGB-х цветов
	CvScalar rgb_colors[NUM_COLOR_TYPES];

	int i = 0, j = 0, x = 0, y = 0;

	// обнуляем цвета
	for (i = 0; i<NUM_COLOR_TYPES; i++) {
		rgb_colors[i] = cvScalarAll(0);
	}

	for (y = 0; y<hsv->height; y++) {
		for (x = 0; x<hsv->width; x++) {

			// получаем HSV-компоненты пикселя
			uchar H = CV_PIXEL(uchar, hsv, x, y)[0];	// Hue
			uchar S = CV_PIXEL(uchar, hsv, x, y)[1];	// Saturation
			uchar V = CV_PIXEL(uchar, hsv, x, y)[2];	// Value (Brightness)

														// определяем к какому цвету можно отнести данные значения
			int ctype = getPixelColorType(H, S, V);

			// устанавливаем этот цвет у отладочной картинки
			CV_PIXEL(uchar, dst, x, y)[0] = cCTHue[ctype];	// Hue
			CV_PIXEL(uchar, dst, x, y)[1] = cCTSat[ctype];	// Saturation
			CV_PIXEL(uchar, dst, x, y)[2] = cCTVal[ctype];	// Value

															// собираем RGB-составляющие
			rgb_colors[ctype].val[0] += CV_PIXEL(uchar, image, x, y)[0]; // B
			rgb_colors[ctype].val[1] += CV_PIXEL(uchar, image, x, y)[1]; // G
			rgb_colors[ctype].val[2] += CV_PIXEL(uchar, image, x, y)[2]; // R

																		 // сохраняем к какому типу относится цвет
			CV_PIXEL(uchar, color_indexes, x, y)[0] = ctype;

			// подсчитываем :)
			colorCount[ctype]++;
		}
	}

	// усреднение RGB-составляющих
	for (i = 0; i<NUM_COLOR_TYPES; i++) {
		rgb_colors[i].val[0] /= colorCount[i];
		rgb_colors[i].val[1] /= colorCount[i];
		rgb_colors[i].val[2] /= colorCount[i];
	}

	// теперь загоним массив в вектор и отсортируем
	std::vector< std::pair< int, uint > > colors;
	colors.reserve(NUM_COLOR_TYPES);

	for (i = 0; i<NUM_COLOR_TYPES; i++) {
		std::pair< int, uint > color;
		color.first = i;
		color.second = colorCount[i];
		colors.push_back(color);
	}
	// сортируем
	std::sort(colors.begin(), colors.end(), colors_sort);

	// для отладки - выводим коды, названия цветов и их количество
	for (i = 0; i<colors.size(); i++) {
		printf("[i] color %d (%s) - %d\n", colors[i].first, sCTypes[colors[i].first], colors[i].second);
	}

	// выдаём код первых цветов
	printf("[i] color code: \n");
	for (i = 0; i<NUM_COLOR_TYPES; i++)
		printf("%02d ", colors[i].first);
	printf("\n");
	printf("[i] color names: \n");
	for (i = 0; i<NUM_COLOR_TYPES; i++)
		printf("%s ", sCTypes[colors[i].first]);
	printf("\n");

	// покажем цвета
	cvZero(dst2);
	int h = dst2->height / RECT_COLORS_SIZE;
	int w = dst2->width;
	for (i = 0; i<RECT_COLORS_SIZE; i++) {
		cvRectangle(dst2, cvPoint(0, i*h), cvPoint(w, i*h + h), rgb_colors[colors[i].first], -1);
	}
	cvShowImage("colors", dst2);
	cvSaveImage("dominate_colors_table.png", dst2);

	// покажем картинку в найденных цветах
	dst3 = cvCloneImage(image);
	for (y = 0; y<dst3->height; y++) {
		for (x = 0; x<dst3->width; x++) {
			int color_index = CV_PIXEL(uchar, color_indexes, x, y)[0];

			CV_PIXEL(uchar, dst3, x, y)[0] = rgb_colors[color_index].val[0];
			CV_PIXEL(uchar, dst3, x, y)[1] = rgb_colors[color_index].val[1];
			CV_PIXEL(uchar, dst3, x, y)[2] = rgb_colors[color_index].val[2];
		}
	}

	cvNamedWindow("dst3");
	cvShowImage("dst3", dst3);
	cvSaveImage("dominate_colors.png", dst3);
	image1 = cvLoadImage("dominate_colors.png", 1);
	printf("[i] image size:  %d bytes\n", image1->imageSize);

	


	// конвертируем отладочную картинку обратно в RGB
	cvCvtColor(dst, dst, CV_HSV2BGR);

	// показываем результат
	cvNamedWindow("color");
	cvShowImage("color", dst);




	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseImage(&image);
	cvReleaseImage(&hsv);
	cvReleaseImage(&dst);
	cvReleaseImage(&dst2);
	cvReleaseImage(&color_indexes);
	cvReleaseImage(&dst3);

	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Непонятна терминология. Что означает выражение "только 20% доминирующих цветов"?

Например, у тебя есть идельно красное изображение, т.е. один цвет определяет всю картинку. Где тут 20%?

Теперь возьмём флаг России, триколор. Пусть он также будет идеальным, три цвета, каждый из которых занимает 33.(3)%. Как планируется сделать раздел в этом случае? А для двухцветного флага?

 

Думаю, что рассмотрев разные крайние случаи и формализовав требования, ты сам выведешь формулу и поймёшь, что именно ты хочешь получить и как.

P.S. Так же предлагаю вручную сделать такие идеальные тестовые картинки и на них оттестировать крайние случаи. Для понимания сути проблемы очень полезно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Есть еще вариант dithering : https://en.wikipedia.org/wiki/Dither

Но там, как правило, фиксированная палитра.

Можно еще PCA применить и извлечь основные цветовые оси, сформировать палитру, и работать с ними.

Можно провести кластеризацию цвета, гауссовыми смесями, или разбить цветовое пространство методом Вороного, но это всё варианты постеризации.  

Сколько там экономится на цвете, надо считать для каждого отдельного изображения.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

То есть имеется в виду сколько оттенков надо уменьшить на изображении, чтобы объем памяти, занимаемого изображения, уменьшился в 2, 3 и так далее раза.

Я не могу сейчас полностью написать заново программу, так как нехватка времени. Если несложно, то подскажите, пожалуйста, как подкорректировать код?
Заранее спасибо!

И да, картинки работы программы:

dst3 - результат работы
image - исходная картинка

Мне нужно как-то контролировать на сколько уменьшится объем памяти, сейчас это делается хаотично(

Снимок1.PNG

Снимок.PNG

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Если изображение не сжимается, то объём памяти никак не изменится.

Если сжимается, то каким алгоритмом? Может, тебе его надо просто уменьшить во сколько-то раз? А после увеличить и резкости добавить. Всё таки формулировка важна. Бить картинку на фиксированное число цветов смысла нет, лучше применить PCA, как советует Smorodov.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×