如何在固定的色彩显示器中找到最红的颜色?

4

我正在为一台名为Amiga的传统计算机编写用户界面(你们中的一些人可能还记得这些计算机)。它们有固定数量颜色的显示模式-2、4、8、16等,可以使用0到255的RGB设置任何其中之一。

所以在4种颜色的显示器上,您可以有:

  • 颜色0 R = 0,G = 0,B = 0(黑色)
  • 颜色1 R = 255,G = 255,B = 255(白色)
  • 颜色2 R = 128,G = 128,B = 128(中灰色)
  • 颜��3 R = 255,G = 0,B = 0(红色)

'普通'显示器上最多可以有255种颜色(有一些特殊的显示模式可以使此数字更高,但我不需要担心这些)。

我正在尝试编写一些C代码,该代码将浏览显示器的颜色列表,并查找最红、最绿和最蓝的颜色,但我无法理解涉及的比较。

如果我有3个变量-red、green和blue-所有int都保存当前颜色的RGB值,还有3个-stored_red、stored_green和stored_blue。

如何编写一个函数,以记住相对于其他颜色拥有最多红色的颜色编号?

我已经尝试过:

If ((red>stored_red) && ((blue <stored_blue) || (green<stored_green)))

但是这并不起作用。我认为我需要处理比例,但是无法弄清楚数学问题。


在比较颜色之前,请将它们分离到RGB节点中。 - ringbuffer_peek
据我所记,Amiga 没有 RGB 模式。相反,它使用了“位面”,其中单个像素的位组合是颜色表中的索引。在原始 Amiga 上,颜色表由 32 个条目组成,每个 R、G 和 B 都有 12 位,每种颜色四位。256 种颜色随 AGA 和 Amiga 1200 以及 4000 在 Amiga 生命期的后期而来。虽然我仍认为这是位平面技术,但最多可达 8 个平面。 - Some programmer dude
对于Amiga编程的技术信息和参考资料,我建议访问此网站(http://amigadev.elowar.com/),该网站似乎拥有所有RKM(Rom Kernel Manual)的内容。 - Some programmer dude
可以尝试使用更多的 [mcve] 来查看是否存在错误,例如您设置 stored_* 变量的值。 - Chris Turner
如果条件语句无法考虑到颜色的红色值比存储的值高得多,以及蓝色或绿色略微更高,则条件语句将失败。因此,如果存储的红色值为100,存储的绿色值为20,存储的蓝色值为21,则即使红色值为255,绿色值为21,蓝色值为22,条件语句也不会执行其代码。尽管该颜色具有更多的红色成分。 - Cogvos
显示剩余3条评论
3个回答

2

检查最大“红色”/“绿色”等的方法有不同的方式,这里首先介绍的是我原始答案使用的简单方法:

/* for all palette (color table) entries */
if(red > stored_red) {
    max_red_index = current_index;
    stored_red = red;
}
if(blue > stored_blue) {
    max_blue_index = current_index;
    stored_blue = blue;
}

当然,同样适用于绿色。
这只会给出具有最大分量的颜色,但在@Chris Turner的评论后,我认为这可能不是您想要的。

另一种方法可能是检查红色与其他颜色的比率(现在我将只使用红色):

redness = red*2/(blue + green + 1)

这个公式给出了一个介于1到510之间的数字,用于表示“红度”,它不考虑亮度。例如,R-G-B 50-10-10比255-100-100更红(4>2)。

还有许多其他的公式可以使用,例如

redness = red*red/(blue + green + 1)

该算法还会考虑亮度,并且会认为255-100-100比50-10-10更偏红色。

要使用这些公式,只需将上面的red变量设置为红色公式的结果即可。


这不会得到最红的颜色,因为它没有考虑颜色中蓝色和绿色的数量 - 例如,255,0,0比255,255,255(也称为白色)更红。 - Chris Turner
谢谢您的评论 - 我明白您的意思,我会编辑我的答案。 - alain
非常感谢您的回复。这正是我正在寻找的。不确定我将使用哪一个(带亮度还是不带亮度),我认为这取决于用户的背景颜色(对于深色,使用亮度较高的一种;对于浅色,则使用不带亮度的一种)。另外,是否有可能修改这些公式来查找最黄的颜色?我认为这更加困难,因为您无法使用最大值而是需要某种范围?如果Amiga有Hue系统,那么这将更容易,但它没有。 - Cogvos
谢谢!对于黄色,您可以添加红色和绿色,例如 yellowness = (red+green)/(blue + 1) 或类似的公式。这将把偏红色的黄色视为与偏绿色的黄色一样黄。我建议您尝试一下该公式,直到您满意为止。 - alain
@Cogvos 再想一想,对于黄色程度来说,min(red, green) 可能比 red + green 更好。 - alain

1
你应该计算红色的百分比来找到最红的颜色。
这里有一个程序,使用了一个 RGB 结构,其中包含一个用于比较两个 RGB 结构的函数,以确定它们谁更“红”。如果两种颜色具有相同的红色百分比,则具有 .r 字段中最大值的颜色被认为是“更红”的。另一个函数接受指向 RGB 结构的指针数组,并返回指向被认为是“最红”的 RGB 三元组的指针。
请注意,在计算百分比时必须小心检查所有零(0,0,0)的 RGB 元组;否则可能会尝试除以零。感谢 @alain 在我的原始代码中发现了这个错误。
#include <stdio.h>
#include <stdbool.h>

struct RGB {
    int r;
    int g;
    int b;
};

bool is_redder_pct(struct RGB *triple1, struct RGB *triple2);
struct RGB * max_red(struct RGB *triples, size_t num_triples);

int main(void)
{
    struct RGB colors[] = { { .r = 125, .g = 0, .b = 0 },
                            { .r = 150, .g = 255, .b = 0 },
                            { .r = 100, .g = 20, .b = 21 },
                            { .r = 255, .g = 21, .b = 22 },
                            { .r = 0, .g = 0, .b = 0 },
                            { .r = 255, .g = 255, .b = 255 },
                            { .r = 128, .g = 128, .b = 128 },
                            { .r = 255, .g = 0, .b = 0 } };
    size_t num_colors = sizeof colors / sizeof *colors;

    struct RGB *reddest = max_red(colors, num_colors);

    printf("The reddest color is: (%d, %d, %d)\n",
           reddest->r, reddest->g, reddest->b);

    return 0;
}

/* Returns true if triple1 is at least as red as triple2 */
bool is_redder_pct(struct RGB *triple1, struct RGB *triple2)
{
    bool ret_val;
    int triple1_sum = triple1->r + triple1->g + triple1->b;
    int triple2_sum = triple2->r + triple2->g + triple2->b;

    /* if triple1 is black, triple1 is not redder than triple2 */
    if (triple1_sum == 0) {
        ret_val = false;

    /* otherwise, if triple2 is black, triple1 is redder than triple2 */
    } else if (triple2_sum == 0) {
        ret_val = true;

    /* otherwise the percentages are calculated in a comparison */    
    } else {
        ret_val = triple1->r / (triple1_sum * 1.0)
            >= triple2->r / (triple2_sum * 1.0);
    }

    return ret_val;
}

/* Returns a pointer to the RGB struct in the array TRIPLES
 * that compares "reddest" */
struct RGB * max_red(struct RGB *triples, size_t num_triples)
{
    struct RGB *max = &triples[0];

    for (size_t i = 1; i < num_triples; i++) {
        struct RGB *curr = &triples[i];
        if (is_redder_pct(curr, max) && curr->r > max->r) {
            max = curr;
        }
    }

    return max;
}

程序输出:
The reddest color is: (255, 0, 0)

如果颜色表中有黑色(0,0,0),则会进行(float)除以零操作。我不记得这是否可以或者会在Amiga上引起“guru meditation”。回答得好。 - alain
@alain-- 感谢您发现了那个错误;测试颜色数组也包含了一个 (0, 0, 0) 三元组!我认为现在一切都好了.... - ad absurdum
非常感谢您的解决方案。 零除是不可以的,会引起系统失败(或在后续的系统上导致软件故障)。 现在我所要做的就是选择适合情况的解决方案。非常感谢您的帮助。 - Cogvos
@Cogvos-- 注意,此答案中的当前代码没有除以零错误;那是答案第一个版本中遗漏的输入情况的结果。 - ad absurdum

0
以下是基于CompuPhase-颜色度量的“低成本逼近”的颜色差,但避免了平方根函数(因此测量了颜色差的平方,这对比较目的来说是可以接受的),并将结果缩放以避免由整数除法引起的精度损失。它将颜色元组数组按“最红”到“最不红”的顺序排序。
比较使中灰比黑色更红,黑色比白色更红,这似乎相当武断!
#include <stdio.h>
#include <stdlib.h>

/*
 * Sorts an array of RGB color values (each component from 0 to 255)
 * in descending order of redness.
 *
 * Uses a low-cost approximation of color distance from
 * <https://www.compuphase.com/cmetric.htm>.  It uses a weighted Euclidean
 * distance function to measure the distance between colors, where the weight
 * factors depend on the mean level of "red":
 *
 * rmean = (c1.r + c2.r) / 2
 * dr = c1.r - c2.r
 * dg = c1.g - c2.g
 * db = c1.b - c2.b
 *
 * dc = sqrt(((2 + (rmean / 256)) * dr * dr) + (4 * dg * dg) +
 *           ((2 + ((255 - rmean) / 256)) * db * db))
 *
 * Uses a modified version of the above which returns the square of the
 * distance between two colors scaled by a silly amount to avoid loss of
 * precision caused by integer division.
 */

struct rgb {
    unsigned char r;    /* red range 0..255 */
    unsigned char g;    /* green range 0..255 */
    unsigned char b;    /* blue range 0..255 */
};

/* distance squared between two colors, scaled by some silly amount. */
long color_dist_squared(const struct rgb *c1, const struct rgb *c2)
{
    long rsum = c1->r + c2->r;
    long dr = (long)c1->r - (long)c2->r;
    long dg = (long)c1->g - (long)c2->g;
    long db = (long)c1->b - (long)c2->b;

    return (((1024 + rsum) * dr * dr) + (2048 * dg * dg) +
            ((1534 - rsum) * db * db));
}

/* distance squared from pure red, scaled by some silly amount. */
long antiredness_squared(const struct rgb *c)
{
    const struct rgb pure_red = { .r = 255, .g = 0, .b = 0 };

    return color_dist_squared(&pure_red, c);
}

/*
 * qsort() comparison function.
 * a and b point to struct rgb values.
 * Returns 1 if *a is more anti-red (less red) than *b.
 * Returns 0 if *a and *b are equally (anti-)red.
 * Returns -1 if *a is less anti-red (more red) than *b.
 */
int compar_antiredness(const void *a, const void *b)
{
    const struct rgb *ca = (const struct rgb *)a;
    const struct rgb *cb = (const struct rgb *)b;
    long ara = antiredness_squared(ca);
    long arb = antiredness_squared(cb);
    long diff = ara - arb;

    return (diff > 0) - (diff < 0);
}

int main(void)
{
    struct rgb colors[] = {
        { .r = 125, .g = 0, .b = 0 },
        { .r = 150, .g = 255, .b = 0 },
        { .r = 100, .g = 20, .b = 21 },
        { .r = 255, .g = 21, .b = 22 },
        { .r = 0, .g = 0, .b = 0 },
        { .r = 255, .g = 255, .b = 255 },
        { .r = 128, .g = 128, .b = 128 },
        { .r = 255, .g = 0, .b = 0 },
    };
    size_t num_colors = sizeof(colors) / sizeof(colors[0]);
    size_t i;

    printf("Unsorted colors:\n");
    for (i = 0; i < num_colors; i++) {
        printf("[%zu] R=%u, G=%u, B=%u\n", i, (unsigned)colors[i].r,
               (unsigned)colors[i].g, (unsigned)colors[i].b);
    }
    printf("\n");
    qsort(colors, num_colors, sizeof(colors[0]), compar_antiredness);
    printf("Colors sorted from most red to least red:\n");
    for (i = 0; i < num_colors; i++) {
        printf("[%zu] R=%u, G=%u, B=%u\n", i, (unsigned)colors[i].r,
               (unsigned)colors[i].g, (unsigned)colors[i].b);
    }
    return 0;
}

以上的输出:
Unsorted colors:
[0] R=125, G=0, B=0
[1] R=150, G=255, B=0
[2] R=100, G=20, B=21
[3] R=255, G=21, B=22
[4] R=0, G=0, B=0
[5] R=255, G=255, B=255
[6] R=128, G=128, B=128
[7] R=255, G=0, B=0

Colors sorted from most red to least red:
[0] R=255, G=0, B=0
[1] R=255, G=21, B=22
[2] R=125, G=0, B=0
[3] R=100, G=20, B=21
[4] R=128, G=128, B=128
[5] R=0, G=0, B=0
[6] R=150, G=255, B=0
[7] R=255, G=255, B=255

编辑:当然,使用以下函数从最绿到最不绿或从最蓝到最不蓝排序同样容易:

/* distance squared from pure green, scaled by some silly amount. */
long antigreenness_squared(const struct rgb *c)
{
    const struct rgb pure_green = { .r = 0, .g = 255, .b = 0 };

    return color_dist_squared(&pure_green, c);
}

/*
 * qsort() comparison function.
 * a and b point to struct rgb values.
 * Returns 1 if *a is more anti-green (less green) than *b.
 * Returns 0 if *a and *b are equally (anti-)green.
 * Returns -1 if *a is less anti-green (more green) than *b.
 */
int compar_antigreenness(const void *a, const void *b)
{
    const struct rgb *ca = (const struct rgb *)a;
    const struct rgb *cb = (const struct rgb *)b;
    long aga = antigreenness_squared(ca);
    long agb = antigreenness_squared(cb);
    long diff = aga - agb;

    return (diff > 0) - (diff < 0);
}

/* distance squared from pure blue, scaled by some silly amount. */
long antiblueness_squared(const struct rgb *c)
{
    const struct rgb pure_blue = { .r = 0, .g = 0, .b = 255 };

    return color_dist_squared(&pure_blue, c);
}

/*
 * qsort() comparison function.
 * a and b point to struct rgb values.
 * Returns 1 if *a is more anti-blue (less blue) than *b.
 * Returns 0 if *a and *b are equally (anti-)blue.
 * Returns -1 if *a is less anti-blue (more blue) than *b.
 */
int compar_antiblueness(const void *a, const void *b)
{
    const struct rgb *ca = (const struct rgb *)a;
    const struct rgb *cb = (const struct rgb *)b;
    long aba = antiblueness_squared(ca);
    long abb = antiblueness_squared(cb);
    long diff = aba - abb;

    return (diff > 0) - (diff < 0);
}

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