基于绿色相对于蓝色和红色的数量计算价值

3

我在这个问题上有些困惑。

这是我的困境:

一种颜色有三个元素:红、绿和蓝(RGB)。

所有颜色都有从0到255的值。

例如:

var red:int = 53; 
var green:int = 150;
var blue:int = 28;

我的评分系统是这样工作的:颜色越绿,得分就越高,最高分为10分。
因此:
var red:int = 0; 
var green:int = 255;
var blue:int = 0;

将获得最高分数(10分)和:

var red:int = 255; 
var green:int = 0;
var blue:int = 255;

将获得最低分数(0)。

我需要制定某种方程式。贝叶斯定理是我应该去的地方吗?

真的毫无头绪。


我需要产生某种方程式。也许 green / 255 x 10 对于你的问题已经足够了?我已经扩展成一个答案,如果这不是你想要的,请告诉我... - VC.One
我的意思是,你是否真正隔离了像素的绿色值(即忽略红/蓝色部分),还是R / B值也会影响“绿色”的得分? - VC.One
R/B元素也会影响颜色,这就是问题所在! - James Mallon
6个回答

2

您的问题是选择正确的 (r,g,b) 函数,以返回0到10之间的值。您声明 (0,255,0) 应该返回10.0,而其他值应该返回更少,(255,0,255) 应该返回0。因此,您需要为该函数选择至少两个参数。一个是在 (x,x,x) 处的值(如果您希望该值相等,否则该参数将是 X 的函数),另一个是非极端值插值的规则。因此,您的函数应该如下所示:

function score(r,g,b:int):Number {
    var rc:int=Math.max(0,Math.min(255,r)); // clamp
    var gc:int=Math.max(0,Math.min(255,g));
    var bc:int=Math.max(0,Math.min(255,b));
    const GRAY:Number=5.0; // value at (x,x,x)
    if ((rc==rg) and (rc==rb)) return GRAY;
    return ir(r,g,b); // interpolation rule
}

实际上,您可以在所有8个角落指定值,并按照您能想象的任何规律进行简单插值。例如,通过相对距离。像这样:
const SCORES:Array=[
    0, // black, (0,0,0)
    0, // red, (255,0,0)
    10, // green, (0,255,0)
    6.66666, // yellow, (255,255,0)
    0, // blue, (0,0,255)
    0, // magenta, (255,0,255)
    6.66666, // cyan, (0,255,255)
    5.0 // white, (255,255,255)
];
const POINTS:Array=[
    {r:0;g:0;b:0},
    {r:255;g:0;b:0},
    {r:0;g:255;b:0},
    {r:255;g:255;b:0},
    {r:0;g:0;b:255},
    {r:255;g:0;b:255},
    {r:0;g:255;b:255},
    {r:255;g:255;b:255}
]; // respective points

function score(r,g,b:int):Number {
    var rc:int=Math.max(0,Math.min(255,r)); // clamp
    var gc:int=Math.max(0,Math.min(255,g));
    var bc:int=Math.max(0,Math.min(255,b));
    var dists:Array=new Array();
    var td:Number=0;
    for (var i:int=POINTS.length-1;i>=0;i--) 
        var dist:Number=distance({r=rc;g=gc;b=bc},POINTS[i]); // TODO
        if (dist<1e-6) return SCORES[i]; // we're at the corner
        dists.unshift(1.0/dist);
        td+=1.0/dist;
    }
    var result:Number=0;
    for (i=POINTS.length-1;i>=0;i--) 
        result+=SCORES[i]*dists[i]/td;
    return result;
}

1
如果只是检查颜色值并从0-255范围内提取0-10分数,则只需执行以下操作:
score = Math.round( (green / 255 ) * 10 );

根据这个逻辑,我们可以看出:

绿色 = 253 得分为10
绿色 = 190 得分为7
绿色 = 128 得分为5
绿色 = 47 得分为2

如果数字在 X 以上但小于 Y,则存在一个阈值,它会给出相同的分数,例如任何介于243和255之间的 green 都将获得10分。
PS:一些可测试的代码:
var score:int = 0;
var green:int = 128; //#change this for testing
score = Math.round( (green / 255 ) * 10 );

trace ("score is : " + score);

嗨,这非常好,我已经走了这么远。我只需要一些比较绿色、红色和蓝色百分比的方法。例如,如果绿色得分为7,但蓝色和红色得分为5和6,我们该如何给出一个值? - James Mallon
问题在于,如果绿色为255,而红色和蓝色也为255,则颜色并不是绿色。 - James Mallon

1
很不幸,我不擅长Action script,所以无法提供实际代码,但我认为我有一些想法可以帮助你。据我所知,您的问题是要考虑所有3个成分来测量给定RGB颜色的"绿度",使得:
  • 绿色将获得100%的评分
  • 红色或蓝色或洋红色将获得0%
  • 白色(所有3种颜色的平均混合)将获得中间值

如果我需要设计这样的方案,我会从RGB切换到某种不同的着色方案,例如YUV或更确切地说HSL/HSV。实际上,“H”组件,即Hue似乎是您需要的。根据维基百科的说法:

色相(Hue)是颜色的主要属性之一(称为颜色外观参数),在技术上被定义为“刺激可以被描述为与被描述为红色、绿色、蓝色和黄色的刺激相似或不同的程度”(在CIECAM02模型中)。

要计算色相,首先需要确定最大和最小组件以及它们之间的差异:

 M = max(R, G, B) 
 m = min(R, G, B) 
 C = M - m

那么

If Red is the Max, then Hue = (G - B)/C
If Green is the Max, then Hue = 2.0 + (B - R)/C
If Blue is the Max, then Hue = 4.0 + (R - G)/C

这会生成[-1,5]范围内的值。您可以将其乘以60并包装到[0,360]中,以将其转换为更常规的度数,但这不重要,因为这只是一个线性比例尺。因此,使用如前所述计算的Hue,可以通过Hue值接近2(或以度数表示为120)来测量颜色的“绿色程度”。
请注意,从此公式计算靠近绿色的颜色(即其中绿色是最大分量的颜色)的“绿色程度”为:
"greenishess" = abs(B - R)/(G - min(B,R))

我觉得这很有道理。其中 B = R 的部分指定了颜色的饱和度,只有当 R 或 B 超过另一个时,颜色才会从“纯绿”变为黄色或蓝色。

还要注意的是,您需要为所有从黑到白的“灰色”颜色分配一些任意的“绿色值”,因为 C 为 0,不能除以 0。

最后,您不必直接使用 Hue。您可以基于它构建一个更复杂的公式。例如,您可以设置一些截止点,将纯红和蓝即超出 [0; 4] 范围(或[0; 240] 度)的所有内容都分配为 0% 的评分。默认情况下,Hue 只会分配 0% 给品红色,即 {红 = 255,绿 = 0,蓝 = 255}。类似于这样:

"greenishness" = [2 - abs(min(4, max(0, Hue)) - 2)] / 2 * 100%

你也可以通过应用一些非线性变换来“曲线化”测量结果。

好吧,虽然OP没有确切地说出来,但如果他真的想要描述绿色是什么样子,只用颜色色相如何有所帮助呢?我尝试过实现这个功能并意识到它不会产生好的结果。例如,考虑一些颜色,比如 cfdacf - 它的色相为120度,但我个人会告诉你它是灰色的。同样对于 010a01 - 它基本上没有绿色值,基本上是黑色,但具有120度的色相值。 - Paweł Audionysos
1
@PawełAudionysos,我同意根据OP的目标,Hue可能是完美的指标,也可能不是。不幸的是,从问题和提供的非常基本的示例中很难猜测他的目标。但是我仍然认为,在不同的颜色方案中完成这样的任务可能会更容易的想法很重要,值得一提。从我的经验来看,许多人并不知道RGB(和/或CMYK)并不是唯一的选择。而其他对于这个问题的回答似乎只是证实了这种观念。 - SergGr
即使按照您所描述的方式仅获取色调,如果我们不使用内置函数(min、max等),则需要通过r、g、b两次循环两次编写代码(如果自己编写,则可以将其简化为单个循环...)以获取最小值和最大值。然后检查最大颜色并为每种颜色使用3个不同的公式。如果有像FF55FF这样的颜色,这里不清楚您是否应该假定蓝色是最大值并计算两次色调?灰色颜色也存在相同的歧义,正如您已经提到的那样。 - Paweł Audionysos
HVS 可以有它的用途,但我想警告一下,“这个任务似乎很棘手,所以我要将颜色转换为 HVS”这种方法并不会自动让它变得容易。最终,您可能需要修改您的 HVS 值,因此您需要实现类似复杂度的反向过程来将其转换回 RGB,因为大多数格式和硬件都使用它。有一天,您需要处理一些图像,因此您将在几百万像素上使用您的“makeSureItsGreeny()”,并想知道为什么它如此缓慢。 - Paweł Audionysos
我并没有说"makeSureItsGreeny()"与问题有关。我只是试图说明,尽可能坚持使用其他颜色空间可能并不总是最佳选择。"因此,我认为没有理由它们应该慢得多..."再看看你需要采取的所有步骤,仅从RGB中获取色调,添加值和饱和度的步骤——这仅仅是开始使用HSV。你能否编写完整的实现示例,将RGB转换为HSV而不使用内置的min/max函数?只需尝试自己编写,您就会明白为什么我说它更慢了。 - Paweł Audionysos
显示剩余3条评论

1
您没有说明这个方程式的用途。我认为已经有人提到了,您有一个带有3个参数的函数,但您只给出了两个单参数的预期输出,并且有许多函数可以满足您的期望,但在这两个输出之间工作完全不同。
我不知道您是否注意到,例如,您的colorFitness0x000000提供了50分的得分 - 这是您所期望的吗?
对于您目前的问题状态,我也可以提出以下建议。
function score(c:uint) {
    var g:uint = (c >>> 8) & 0xFF;
    var s:uint = g + (c >>> 16 & g ^ g);
    s = (s + (c & 0xFF & g ^ g)) / 0x2FD * 100;
    trace("score for", c.toString(16), ":", s);
}

score(0x00FF00); //100
score(0x000000); //0
score(0xFF00FF); //0
score(0xFF7FFF); //16
score(0xFFFFFF); //33
score(0x00FFFF); //66
score(0xFFFF00); //66
score(0x7FFF7F); //66

但如果您告诉我们需要该公式的原因或更精确地说明其功能,那将是很好的。


1
Pawel,我认为我也可以批评你的函数。你当前的score是否无法明显区分大部分红色和仅略带绿色的0xC03000与明显绿色的0x003000,从而使它们的得分相等约为18.8?或者明显略带绿色的0xB040B0与得分约为25的明显绿色的0x004000相同?我很想找到一个真正的非盲人,看看他们是否感觉这些颜色同样“绿色”。 - SergGr
你可以,而且你应该。你说得很有道理,虽然它并不适用于所有情况,这就是为什么我想尝试并比较你的建议。我的方法为具有更多绿色成分(即更亮或者换句话说,更喜欢绿色值而非其他值)的数值提供更高的分数。003000 是一种非常暗的颜色(绿色成分很少),所以红色成分很多也无关紧要。我更关心的是 007F00(清晰的绿色)得分为 49,而 FFFF00(明亮的黄色)得分为 66,但另一方面,可以肯定地说得分>75的颜色... - Paweł Audionysos
请提供更多上下文,以便我可以为您提供准确的翻译。 - Paweł Audionysos
我并不确定像 0x004000 这样的颜色是否“显然”是绿色的,因为颜色感知是非常主观的,你甚至可能会因为房间的颜色而有所不同,更不用说屏幕上的邻近颜色了,特别是如果这种颜色占据了很小的区域... - Paweł Audionysos

1

这有点棘手,因为有三个组件,但如果我理解正确,只有一个组件决定了你的排名。

你可以完全忽略红色和蓝色的组件,仅根据0-255绿色级别进行排名;结果将是两种绿色=64的颜色,但交换了红色和蓝色的值,例如rgb[100, 64, 50]和rgb[50, 64, 100]将得到相同的分数,因为它们都具有相同数量的绿色。这一结果意味着绿色得分是3D RGB坐标在“绿轴”上的投影。


0

我想出了一个正确的方法来完成这个。

请查看下面的代码:

var color = parseInt(colorString);
var fitness:Number = 0;
var red:int = (color>> 16) & 0xFF;
var green:int = (color>> 8) & 0xFF;
var blue:int = color & 0xFF;
var greenPercentage = (green/255)*100;
var redAndBluePercentage = ((red+blue)/510)*100;
fitness = greenPercentage-redAndBluePercentage;
fitness = (fitness+100)/2;
trace(fitness);

数学运算如下:

将您的颜色分成红色、绿色和蓝色。

取出颜色中绿色所占的百分比。(x/255)

取出颜色中红色和蓝色所占的百分比。(x/510)

将绿色所占百分比减去红色和蓝色所占百分比。

进行一些数学计算,得出0-100之间的可能得分。

colorFitness("0xFF00FF"); //Outputs 0
colorFitness("0x00FF00"); //Outputs 100

我已经添加了新答案。请看一下。 - Paweł Audionysos

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