Если цвета предопределены, то решение состоит в том, чтобы просто зациклить каждый пиксель и заменить ближайшим представителем.Как сказал carlosdc , преобразование цветового пространства может дать лучший результат, чем просто (r1-r2)**2 + (g1-g2)**2 + (b1-b2)**2
.
Чтобы ускорить процесс, возможен трюк, торгующий памятью и кэширующий результатданный триплет RGB ... т.е.
// Initialize the cache to 255
std::vector<unsigned char> cache(256*256*256, 255);
for (int y=0; y<h; y++)
{
unsigned char *pixel = img + y*w*3 + x;
for int (x=0; x<w; x++, pixel+=3)
{
int r = pixel[0], g = pixel[1], b = pixel[2];
int key = r + (g<<8) + (b<<16);
int converted = cache[key];
if (converted == 255)
{
... find closest representative ...
cache[key] = converted;
}
pixel[0] = red[converted];
pixel[1] = green[converted];
pixel[2] = blue[converted];
}
}
Если количество цветов мало, вы можете использовать меньше памяти.Например, чтобы ограничить число представителей до 15, вам нужно всего 4 бита на запись цвета (половина пробела), и что-то вроде следующего сделает это:
std::vector<unsigned char> cache(256*256*256/2, 255);
...
int converted = (key&1) ? (cache[key>>1] >> 4) : (cache[key>>1] & 0x0F);
if (converted == 15) // Empty slot
{
...
cache[key>>1] ^= (key & 1) ? ((converted << 4)^0xF0) : (converted^0x0F);
}
...
Если на противоположной стороне вы знаете, что число возможныхвходные цвета будут небольшими, а число представителей будет большим, тогда стандартная std::map
может быть допустимой альтернативой.