在C语言中使用颜色调色板数组查找最近的RGB值

4
我已经完成了部分代码,但在某些比较中,最近似的值是不正确的。 例子: 正确的:
Rgb value | Value from array
0xFFFFFD  = 0xFFFFFF

Incorrect: Fixed code

Rgb value | Value from array
0xF4F939  = 0xFF0000 (should be 0xFFFF00)

控制台输出:(正确)

C:\Users\honguito\Desktop\Bat\Game_Batch_Files\24_to_8_bitmap>24_to_8_bit_palett
e
The closest color of 0xFFFFFD is: '0xFFFFFF'

C:\Users\honguito\Desktop\Bat\Game_Batch_Files\24_to_8_bitmap>

控制台输出:(不正确)

C:\Users\honguito\Desktop\Bat\Game_Batch_Files\24_to_8_bitmap>24_to_8_bit_palett
e
The closest color of 0xF4F939 is: '0xFF0000'

C:\Users\honguito\Desktop\Bat\Game_Batch_Files\24_to_8_bitmap>

这些RGB颜色代码被列在一个数组中:
int data[] = {
    0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080,
    0xC0C0C0, 0xC0DCC0, 0xA6CAF0, 0x402000, 0x602000, 0x802000, 0xA02000,
    0xC02000, 0xE02000, 0x004000, 0x204000, 0x404000, 0x604000, 0x804000,
    0xA04000, 0xC04000, 0xE04000, 0x006000, 0x206000, 0x406000, 0x606000,
    0x806000, 0xA06000, 0xC06000, 0xE06000, 0x008000, 0x208000, 0x408000,
    0x608000, 0x808000, 0xA08000, 0xC08000, 0xE08000, 0x00A000, 0x20A000,
    0x40A000, 0x60A000, 0x80A000, 0xA0A000, 0xC0A000, 0xE0A000, 0x00C000,
    0x20C000, 0x40C000, 0x60C000, 0x80C000, 0xA0C000, 0xC0C000, 0xE0C000,
    0x00E000, 0x20E000, 0x40E000, 0x60E000, 0x80E000, 0xA0E000, 0xC0E000,
    0xE0E000, 0x000040, 0x200040, 0x400040, 0x600040, 0x800040, 0xA00040,
    0xC00040, 0xE00040, 0x002040, 0x202040, 0x402040, 0x602040, 0x802040,
    0xA02040, 0xC02040, 0xE02040, 0x004040, 0x204040, 0x404040, 0x604040,
    0x804040, 0xA04040, 0xC04040, 0xE04040, 0x006040, 0x206040, 0x406040,
    0x606040, 0x806040, 0xA06040, 0xC06040, 0xE06040, 0x008040, 0x208040,
    0x408040, 0x608040, 0x808040, 0xA08040, 0xC08040, 0xE08040, 0x00A040,
    0x20A040, 0x40A040, 0x60A040, 0x80A040, 0xA0A040, 0xC0A040, 0xE0A040,
    0x00C040, 0x20C040, 0x40C040, 0x60C040, 0x80C040, 0xA0C040, 0xC0C040,
    0xE0C040, 0x00E040, 0x20E040, 0x40E040, 0x60E040, 0x80E040, 0xA0E040,
    0xC0E040, 0xE0E040, 0x000080, 0x200080, 0x400080, 0x600080, 0x800080,
    0xA00080, 0xC00080, 0xE00080, 0x002080, 0x202080, 0x402080, 0x602080,
    0x802080, 0xA02080, 0xC02080, 0xE02080, 0x004080, 0x204080, 0x404080,
    0x604080, 0x804080, 0xA04080, 0xC04080, 0xE04080, 0x006080, 0x206080,
    0x406080, 0x606080, 0x806080, 0xA06080, 0xC06080, 0xE06080, 0x008080,
    0x208080, 0x408080, 0x608080, 0x808080, 0xA08080, 0xC08080, 0xE08080,
    0x00A080, 0x20A080, 0x40A080, 0x60A080, 0x80A080, 0xA0A080, 0xC0A080,
    0xE0A080, 0x00C080, 0x20C080, 0x40C080, 0x60C080, 0x80C080, 0xA0C080,
    0xC0C080, 0xE0C080, 0x00E080, 0x20E080, 0x40E080, 0x60E080, 0x80E080,
    0xA0E080, 0xC0E080, 0xE0E080, 0x0000C0, 0x2000C0, 0x4000C0, 0x6000C0,
    0x8000C0, 0xA000C0, 0xC000C0, 0xE000C0, 0x0020C0, 0x2020C0, 0x4020C0,
    0x6020C0, 0x8020C0, 0xA020C0, 0xC020C0, 0xE020C0, 0x0040C0, 0x2040C0,
    0x4040C0, 0x6040C0, 0x8040C0, 0xA040C0, 0xC040C0, 0xE040C0, 0x0060C0,
    0x2060C0, 0x4060C0, 0x6060C0, 0x8060C0, 0xA060C0, 0xC060C0, 0xE060C0,
    0x0080C0, 0x2080C0, 0x4080C0, 0x6080C0, 0x8080C0, 0xA080C0, 0xC080C0,
    0xE080C0, 0x00A0C0, 0x20A0C0, 0x40A0C0, 0x60A0C0, 0x80A0C0, 0xA0A0C0,
    0xC0A0C0, 0xE0A0C0, 0x00C0C0, 0x20C0C0, 0x40C0C0, 0x60C0C0, 0x80C0C0,
    0xA0C0C0, 0xFFFBF0, 0xA0A0A4, 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00,
    0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF
};

然后我需要获取最接近的数字,我会执行以下操作:
nearest = findKey(data, pcolor);

以下是完整代码:

#include <stdio.h>

int main () {

    //int pcolor = 0xF4F939;
    int pcolor = 0xFFFFFD;
    //int pcolor = 0x700000;
    //int pcolor = 0x21A0C0;
    int cmp[256];
    int cmp2[256];
    int data[] = {
        0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080,
        0xC0C0C0, 0xC0DCC0, 0xA6CAF0, 0x402000, 0x602000, 0x802000, 0xA02000,
        0xC02000, 0xE02000, 0x004000, 0x204000, 0x404000, 0x604000, 0x804000,
        0xA04000, 0xC04000, 0xE04000, 0x006000, 0x206000, 0x406000, 0x606000,
        0x806000, 0xA06000, 0xC06000, 0xE06000, 0x008000, 0x208000, 0x408000,
        0x608000, 0x808000, 0xA08000, 0xC08000, 0xE08000, 0x00A000, 0x20A000,
        0x40A000, 0x60A000, 0x80A000, 0xA0A000, 0xC0A000, 0xE0A000, 0x00C000,
        0x20C000, 0x40C000, 0x60C000, 0x80C000, 0xA0C000, 0xC0C000, 0xE0C000,
        0x00E000, 0x20E000, 0x40E000, 0x60E000, 0x80E000, 0xA0E000, 0xC0E000,
        0xE0E000, 0x000040, 0x200040, 0x400040, 0x600040, 0x800040, 0xA00040,
        0xC00040, 0xE00040, 0x002040, 0x202040, 0x402040, 0x602040, 0x802040,
        0xA02040, 0xC02040, 0xE02040, 0x004040, 0x204040, 0x404040, 0x604040,
        0x804040, 0xA04040, 0xC04040, 0xE04040, 0x006040, 0x206040, 0x406040,
        0x606040, 0x806040, 0xA06040, 0xC06040, 0xE06040, 0x008040, 0x208040,
        0x408040, 0x608040, 0x808040, 0xA08040, 0xC08040, 0xE08040, 0x00A040,
        0x20A040, 0x40A040, 0x60A040, 0x80A040, 0xA0A040, 0xC0A040, 0xE0A040,
        0x00C040, 0x20C040, 0x40C040, 0x60C040, 0x80C040, 0xA0C040, 0xC0C040,
        0xE0C040, 0x00E040, 0x20E040, 0x40E040, 0x60E040, 0x80E040, 0xA0E040,
        0xC0E040, 0xE0E040, 0x000080, 0x200080, 0x400080, 0x600080, 0x800080,
        0xA00080, 0xC00080, 0xE00080, 0x002080, 0x202080, 0x402080, 0x602080,
        0x802080, 0xA02080, 0xC02080, 0xE02080, 0x004080, 0x204080, 0x404080,
        0x604080, 0x804080, 0xA04080, 0xC04080, 0xE04080, 0x006080, 0x206080,
        0x406080, 0x606080, 0x806080, 0xA06080, 0xC06080, 0xE06080, 0x008080,
        0x208080, 0x408080, 0x608080, 0x808080, 0xA08080, 0xC08080, 0xE08080,
        0x00A080, 0x20A080, 0x40A080, 0x60A080, 0x80A080, 0xA0A080, 0xC0A080,
        0xE0A080, 0x00C080, 0x20C080, 0x40C080, 0x60C080, 0x80C080, 0xA0C080,
        0xC0C080, 0xE0C080, 0x00E080, 0x20E080, 0x40E080, 0x60E080, 0x80E080,
        0xA0E080, 0xC0E080, 0xE0E080, 0x0000C0, 0x2000C0, 0x4000C0, 0x6000C0,
        0x8000C0, 0xA000C0, 0xC000C0, 0xE000C0, 0x0020C0, 0x2020C0, 0x4020C0,
        0x6020C0, 0x8020C0, 0xA020C0, 0xC020C0, 0xE020C0, 0x0040C0, 0x2040C0,
        0x4040C0, 0x6040C0, 0x8040C0, 0xA040C0, 0xC040C0, 0xE040C0, 0x0060C0,
        0x2060C0, 0x4060C0, 0x6060C0, 0x8060C0, 0xA060C0, 0xC060C0, 0xE060C0,
        0x0080C0, 0x2080C0, 0x4080C0, 0x6080C0, 0x8080C0, 0xA080C0, 0xC080C0,
        0xE080C0, 0x00A0C0, 0x20A0C0, 0x40A0C0, 0x60A0C0, 0x80A0C0, 0xA0A0C0,
        0xC0A0C0, 0xE0A0C0, 0x00C0C0, 0x20C0C0, 0x40C0C0, 0x60C0C0, 0x80C0C0,
        0xA0C0C0, 0xFFFBF0, 0xA0A0A4, 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00,
        0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF
    };

    int nearIndex,nearest, result;

    nearest = findKey(data, pcolor);    
    printf("The closest color of 0x%X is: '0x%X'\n", pcolor, nearest);
    //system("pause");
}


int findKey(int Array1[], int key){
    int diff = abs( key - Array1[0]);
    int Num1 = 0;
    int Num2 = 0;
    for (int a = 0; a < 256; a++) {
            if (diff > abs( key - Array1[a] )) {
            diff = abs( key - Array1[a]);
            Num1 = Array1[a];
        }
    }
    return Num1;
    }

有一些未使用的变量和旧代码。
有任何想法吗?

实际上,根据您的代码对“接近度”(绝对差)的定义,该代码是正确的:0xFF0000和0x21A0C0之间的差异小于0xFFF000和相同差异之间的差异。 - cnicutar
哦,我错了,在第二个代码块中,应该是0xF4F939而不是0x21A0C0。对此感到抱歉! - Honguito98
仍然是同样的情况。用计算器做减法。 - cnicutar
2个回答

4
要找到“光学”最接近的颜色,您需要估计比较的两种颜色的颜色组件之间的差异。为了做到这一点,您需要将24位值拆分成8位颜色组件r、g、b。然后再对这些组件进行比较。一个简单的方法就是对各个组件的差异取绝对值并求和。更精确的公式可以通过搜索获得。
// two colors to compare

int c1;
int c2;

// split c1 and c2 into their respective color components

r1 = c1 / 0x010000;
g1 = (c1 % 0x010000) / 0x00100;
b1 = c1 % 0x000100;

r2 = c2 / 0x010000;
g2 = (c2 % 0x010000) / 0x00100;
b2 = c2 % 0x000100;

// color "distance"

diff = abs( r1 - r2 ) + abs( g1 - g2 ) + abs ( b1 - b2 );

最初,我尝试获取pcolor和data[]数组之间的RGB复合差异。但我从未尝试将RGB十六进制代码拆分为3个变量。附注:还有一种更有效的方法可以使用逻辑移位来拆分RGB十六进制代码。 附注2:感谢帮助我! - Honguito98
当然,有更有效的方法来拆分颜色;我的代码只是为了提供一个思路。还有可能有更准确的光学公式来比较基于组件值的颜色。但再次强调,这只是为了提供一个思路,即“先将颜色拆分成组件”。不用谢。 - Paolo
正如John Hascall在他的回答中所指出的,距离最好用欧几里得度量。 - Jongware

3

我认为使用三维距离公式会是一种更准确的计算“最近”颜色的方法(因为你只关心比较,可以在最后跳过sqrt())。

dist_r = abs(r1 - r2);
dist_g = abs(g1 - g2);
dist_b = abs(b1 - b2);
dist_3d_sqd = (dist_r * dist_r) + (dist_g * dist_g) + (dist_b * dist_b

实际上,您也可以跳过abs()调用,但是将其放在其中会使其更清晰,也许?

abs() 调用会使它变慢一点,我们可以在注释中解释 dist_ 变量的需要,而不是包含一个 abs() 调用。 - Sreenikethan I

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接