我正在寻找某种公式或算法来确定给定RGB值的颜色的亮度。我知道这不像把RGB值加在一起那么简单,更高的总和更亮,但我有点不知所措,不知道从哪里开始。


当前回答

我已经在接受的答案中对三种算法做了比较。我循环生成颜色,大约每400个颜色使用一次。每种颜色由2x2像素表示,颜色从最深到最浅(从左到右,从上到下)排序。

第一张图片-亮度(相对)

0.2126 * R + 0.7152 * G + 0.0722 * B

第二张图片- http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

第三张图片- HSP颜色模型

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

第4张图- WCAG 2.0 SC 1.4.3相对亮度和对比度公式(见@Synchro的答案在这里)

根据一行中的颜色数量,有时可以在第一张和第二张图片上发现图案。我从第3或第4算法的图片上没有发现任何模式。

如果我必须选择,我会选择算法3,因为它更容易实现,比4快33%。

其他回答

这里有一小段C代码,可以正确地计算可感知的亮度。

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}

“接受”的答案是不正确和不完整的

唯一准确的答案是@ ji- dadson和@EddingtonsMonkey的答案,并支持@ niles -pipenbrinck。其他答案(包括已接受的答案)链接到或引用了错误的、不相关的、过时的或坏的来源。

简要:

sRGB必须在应用系数之前线性化。 亮度(L或Y)与光一样是线性的。 感知亮度(L*)与人类感知一样是非线性的。 HSV和HSL在感知方面甚至远不准确。 sRGB的IEC标准指定阈值为0.04045,而不是0.03928(这是来自过时的早期草案)。 为了有用(即相对于感知),欧几里得距离需要一个感知一致的笛卡尔向量空间,如CIELAB。sRGB不是其中之一。


以下是正确而完整的回答:

由于这条线索在搜索引擎中出现频率很高,我添加了这个答案来澄清关于这个主题的各种误解。

亮度是光的线性测量,对正常视力进行光谱加权,但对亮度的非线性感知不进行调整。它可以是相对度量,如CIEXYZ中的Y,或L, cd/m2的绝对度量(不要与L*混淆)。

一些视觉模型如CIELAB使用感知明度,这里L* (Lstar)为感知明度值,且为非线性,以近似人类视觉非线性响应曲线。(也就是说,对知觉是线性的,但因此对光是非线性的)。

亮度是一种感知属性,它不具有“物理”度量。然而,一些颜色外观模型确实有一个值,通常用“Q”表示感知亮度,这与感知亮度不同。

Luma (Y´')是一种伽玛编码的加权信号,用于某些视频编码(Y´I´Q´)。不要与线性亮度混淆。

Gamma或传递曲线(TRC)是一种通常与感知曲线相似的曲线,通常用于存储或广播图像数据,以减少感知噪声和/或提高数据利用率(以及相关原因)。

为了确定感知亮度,首先将gamma编码的R´G´B´图像值转换为线性亮度(L或Y),然后转换为非线性感知亮度(L*)


寻找亮度:

...因为很明显它在某个地方丢失了……

第一步:

将所有sRGB 8位整数值转换为十进制0.0-1.0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

第二步:

将gamma编码的RGB转换为线性值。例如,sRGB(计算机标准)要求功率曲线约为V^2.2,尽管“准确的”变换是:

其中V´为sRGB的伽玛编码R、G或B通道。 伪代码:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4);
        }
    }

第三步:

要找到亮度(Y),应用sRGB的标准系数:

使用上述函数的伪代码:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

找到可感知的轻盈:

步骤四:

从上面取亮度Y,变换为L*

伪代码:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389)) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L*是一个从0(黑色)到100(白色)的值,其中50是感知的“中间灰色”。L* = 50相当于Y = 18.4,换句话说,一张18%的灰卡,代表一张照片曝光的中间(安塞尔·亚当斯V区)。

引用:

IEC 61966-2-1:1999标准 维基百科sRGB 维基百科CIELAB 维基百科CIEXYZ Charles Poynton的Gamma常见问题解答

把这看作是对Myndex的精彩回答的补充。正如他(和其他人)解释的那样,计算RGB颜色的相对亮度(和感知亮度)的算法是设计用于线性RGB值的。你不能只是将它们应用到原始sRGB值上,并希望得到相同的结果。

理论上,这一切听起来都很棒,但我真的需要亲眼看看证据,所以,受到彼得·赫塔克(Petr Hurtak)的颜色渐变的启发,我自己做了一个。它们说明了两种最常见的算法(ITU-R建议BT.601和BT.709),并清楚地说明了为什么应该使用线性值(而不是伽玛校正值)进行计算。

首先,下面是旧的ITU BT.601算法的结果。左边的使用原始sRGB值。右边的使用线性值。

ITU-R BT.601颜色亮度梯度

0.299 r + 0.587 g + 0.114 b

在这个分辨率下,左边的照片实际上看起来非常好!但如果你仔细观察,你会发现一些问题。在更高的分辨率下,不需要的人工制品更加明显:

线性的不受这些影响,但是有很多干扰。让我们将其与ITU-R建议BT.709进行比较……

ITU-R BT.709颜色亮度梯度

0.2126 r + 0.7152 g + 0.0722 b

哦男孩。显然不打算与原始sRGB值一起使用!然而,这正是大多数人所做的!

在高分辨率下,你可以真正看到这个算法在使用线性值时是多么有效。它没有之前那个那么多噪音。虽然这些算法都不是完美的,但这个算法已经是最好的了。

方法可以根据您的需要而有所不同。以下是计算亮度的3种方法:

亮度(某些颜色空间的标准):(0.2126*R + 0.7152*G + 0.0722*B)光源 亮度(感知选项1):(0.299*R + 0.587*G + 0.114*B)光源 亮度(感知选项2,计算较慢):根号(0.241*R^2 + 0.691*G^2 + 0.068*B^2)→根号(0.299*R^2 + 0.587*G^2 + 0.114*B^2)(感谢@MatthewHerbst)来源

[编辑:添加了使用命名css颜色的例子,按每种方法排序。]

再加上其他人说的话:

所有这些方程在实践中都工作得很好,但如果你需要非常精确,你必须首先将颜色转换为线性颜色空间(应用逆图像-gamma),对原色进行权重平均,如果你想显示颜色- 把亮度调回监控器伽马。

在深灰色中,忽略伽玛和正确伽玛之间的亮度差异高达20%。