我正在寻找某种公式或算法来确定给定RGB值的颜色亮度。 我知道它不可能像将RGB值相加然后具有更高总和就更亮那么简单,但我不知道从哪里开始。
我正在寻找一种计算颜色亮度的公式或算法,其输入是RGB值。我知道只是简单地将RGB值相加并且总和越高的颜色越亮是不行的,因此我需要一个更复杂的方法。我正在寻找某种公式或算法来确定给定RGB值的颜色亮度。 我知道它不可能像将RGB值相加然后具有更高总和就更亮那么简单,但我不知道从哪里开始。
我正在寻找一种计算颜色亮度的公式或算法,其输入是RGB值。我知道只是简单地将RGB值相加并且总和越高的颜色越亮是不行的,因此我需要一个更复杂的方法。根据您的需要,计算Luminance的方法可能会有所不同。以下是三种计算Luminance的方法:
亮度(某些色彩空间的标准):(0.2126*R + 0.7152*G + 0.0722*B)
来源
亮度(感知选项1):(0.299*R + 0.587*G + 0.114*B)
来源
亮度(感知选项2,计算速度较慢): → sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )
sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )
(感谢@MatthewHerbst)来源
[编辑:添加了使用每种方法排序的命名 css 颜色的示例。]
我认为你需要的是RGB到亮度转换公式。
光度/数字ITU BT.709:
Y = 0.2126 R + 0.7152 G + 0.0722 B
数字ITU BT.601(在R和B分量上赋予更高的权重):
Y = 0.299 R + 0.587 G + 0.114 B
如果你愿意为了性能而牺牲精度,这里有两个逼近公式可供选择:
Y = 0.33 R + 0.5 G + 0.16 B
Y = 0.375 R + 0.5 G + 0.125 B
这些可以快速计算:
Y = (R+R+B+G+G+G)/6
Y = (R+R+R+B+G+G+G+G)>>3
蓝色
+ 3绿色)/6,第二个公式为(3红色 + 蓝色
+ 4绿色)>>3。虽然在这两个快速估算中蓝色的权重最小,但它仍然存在。 - Franci PenovY = (R<<1+R+G<<2+B)>>3
(在ARM上只需3-4个CPU周期)。但我想好的编译器会为您进行这种优化。 - rjmunroY = (R << (1 + R + G) << (2 + B)) >> 3
相同。正确的代码片段是:int Y = ((R << 1) + R + (G << 2) + B) >> 3;
。 - itachi唯一准确的答案是@jive-dadson和@EddingtonsMonkey的答案,并且得到@nils-pipenbrinck的支持。其他答案(包括被接受的答案)链接或引用的来源要么是错误的,无关的,过时的,或者是失效的。
L*
混淆)。
感知亮度被一些视觉模型使用,如CIELAB,这里的L*
(Lstar)是感知亮度的值,并且是非线性的,以近似人类视觉的非线性响应曲线。(即,对感知是线性的,但对光是非线性的)。
亮度是一种感知属性,它没有“物理”测量。然而,一些颜色外观模型确实有一个被称为“Q”的值,用于感知亮度,这与感知亮度不同。...因为显然它在某个地方丢失了...
将所有sRGB 8位整数值转换为0.0-1.0的小数
vR = sR / 255;
vG = sG / 255;
vB = sB / 255;
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 = (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;
}
}
IEC 61966-2-1:1999 标准
维基百科 sRGB
维基百科 CIELAB
维基百科 CIEXYZ
Charles Poynton 的 Gamma FAQ
L*a*b*
没有考虑到许多心理物理属性。其中之一是 Helmholtz-Kohlrausch 效应,但还有许多其他因素。CIELAB 绝不是一个“完整”的图像评估模型。在我的帖子中,我试图尽可能全面地涵盖基本概念,而不涉及非常深入的细节。Hunt 模型、Fairchild 的模型和其他模型做得更全面,但也更加复杂。 - MyndexYtoLstar(Y)
接受0到1范围内的值,并返回0到100范围内的值,这可能会有些令人困惑。 - Chris Dennisif (Y <= (216/24389)
缺少一个)
。 - OG Sean0.2126 * R + 0.7152 * G + 0.0722 * B
0.299 * R + 0.587 * G + 0.114 * B
sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)
第四张图片 - WCAG 2.0 SC 1.4.3 相对亮度 和 对比度比例 公式(请参见@Synchro's的答案here)
根据一行中颜色的数量,有时可以在第一张和第二张图片上发现图案。我从未在第三或第四个算法的图片上发现任何图案。
如果必须选择,我会选择第三个算法,因为它更容易实现,并且比第四个算法快约33%。
^2
和sqrt
是从非线性RGB快速逼近线性RGB的方法,而不是更正确的^2.2
和^(1/2.2)
。不幸的是,使用非线性输入而不是线性输入非常普遍。 - Mark Ransom以下是将sRGB图像(如浏览器等使用的)转换为灰度图像的唯一正确算法。
在计算内积之前,需要对色彩空间应用反Gamma函数。然后将Gamma函数应用于缩小的值。如果不考虑Gamma函数,则可能导致多达20%的误差。
对于典型的计算机内容,色彩空间为sRGB。 sRGB的正确数字约为0.21、0.72和0.07。sRGB的Gamma是一个复合函数,近似于1 /(2.2)的指数函数。下面是C++的全部代码。
// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;
// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
double c = ic/255.0;
if ( c <= 0.04045 )
return c/12.92;
else
return pow(((c+0.055)/(1.055)),2.4);
}
// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
if(v<=0.0031308)
v *= 12.92;
else
v = 1.055*pow(v,1.0/2.4)-0.055;
return int(v*255+0.5); // This is correct in C++. Other languages may not
// require +0.5
}
// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
return gam_sRGB(
rY*inv_gam_sRGB(r) +
gY*inv_gam_sRGB(g) +
bY*inv_gam_sRGB(b)
);
}
gray
函数在计算亮度值后进行了伽马压缩,但亮度本身是未压缩的灰度值,因此如果你想计算亮度,则应该省略对 gam_sRGB
的调用。如果你只是想将颜色转换为黑白显示,则应该保留它。 - Tim Kuipers建议使用W3C标准推荐的公式,而不是在这里随机选择公式。
这是一个简单但精确的PHP实现WCAG 2.0 SC 1.4.3的相对亮度和对比度比率公式。它产生的值适合于评估符合WCAG要求所需的比率,如此页面上,因此适用于任何Web应用程序。这很容易移植到其他语言。
/**
* Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
* @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
* @param string $col A 3 or 6-digit hex colour string
* @return float
* @author Marcus Bointon <marcus@synchromedia.co.uk>
*/
function relativeluminance($col) {
//Remove any leading #
$col = trim($col, '#');
//Convert 3-digit to 6-digit
if (strlen($col) == 3) {
$col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
}
//Convert hex to 0-1 scale
$components = array(
'r' => hexdec(substr($col, 0, 2)) / 255,
'g' => hexdec(substr($col, 2, 2)) / 255,
'b' => hexdec(substr($col, 4, 2)) / 255
);
//Correct for sRGB
foreach($components as $c => $v) {
if ($v <= 0.04045) {
$components[$c] = $v / 12.92;
} else {
$components[$c] = pow((($v + 0.055) / 1.055), 2.4);
}
}
//Calculate relative luminance using ITU-R BT. 709 coefficients
return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}
/**
* Calculate contrast ratio acording to WCAG 2.0 formula
* Will return a value between 1 (no contrast) and 21 (max contrast)
* @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
* @param string $c1 A 3 or 6-digit hex colour string
* @param string $c2 A 3 or 6-digit hex colour string
* @return float
* @author Marcus Bointon <marcus@synchromedia.co.uk>
*/
function contrastratio($c1, $c2) {
$y1 = relativeluminance($c1);
$y2 = relativeluminance($c2);
//Arrange so $y1 is lightest
if ($y1 < $y2) {
$y3 = $y1;
$y1 = $y2;
$y2 = $y3;
}
return ($y1 + 0.05) / ($y2 + 0.05);
}
除了其他人说的内容之外,还需要补充:
这些公式在实践中效果还不错,但如果您需要非常精确的结果,您需要先将颜色转换为线性颜色空间(应用反图像伽马),对三种基本颜色进行加权平均,如果您想显示颜色,则需要将亮度恢复到监视器伽马。
忽略伽马值和使用正确的伽马值之间的亮度差异,在暗灰色方面高达20%。
#FF0000
和 #FFFFFF
一样亮? - Klesunlightness = (max(r, g, b) + min(r, g, b)) / 2
。 - Klesun今天我在JavaScript中解决了一个类似的任务。
我使用了这个getPerceivedLightness(rgb)
函数来处理HEX RGB颜色。
它通过Fairchild和Perrotta公式对亮度进行校正,从而处理Helmholtz-Kohlrausch效应。
/**
* Converts RGB color to CIE 1931 XYZ color space.
* https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
* @param {string} hex
* @return {number[]}
*/
export function rgbToXyz(hex) {
const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
const X = 0.4124 * r + 0.3576 * g + 0.1805 * b
const Y = 0.2126 * r + 0.7152 * g + 0.0722 * b
const Z = 0.0193 * r + 0.1192 * g + 0.9505 * b
// For some reason, X, Y and Z are multiplied by 100.
return [X, Y, Z].map(_ => _ * 100)
}
/**
* Undoes gamma-correction from an RGB-encoded color.
* https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
* https://dev59.com/QHRB5IYBdhLWcg3wiHpl
* @param {number}
* @return {number}
*/
function sRGBtoLinearRGB(color) {
// Send this function a decimal sRGB gamma encoded color value
// between 0.0 and 1.0, and it returns a linearized value.
if (color <= 0.04045) {
return color / 12.92
} else {
return Math.pow((color + 0.055) / 1.055, 2.4)
}
}
/**
* Converts hex color to RGB.
* https://dev59.com/7G035IYBdhLWcg3wGMDa
* @param {string} hex
* @return {number[]} [rgb]
*/
function hexToRgb(hex) {
const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
if (match) {
match.shift()
return match.map(_ => parseInt(_, 16))
}
}
/**
* Converts CIE 1931 XYZ colors to CIE L*a*b*.
* The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
* https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
* @param {number[]} color The CIE 1931 XYZ color to convert which refers to
* the D65/2° standard illuminant.
* @returns {number[]} The color in the CIE L*a*b* color space.
*/
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
[x, y, z] = [x, y, z].map((v, i) => {
v = v / D65[i]
return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
})
const l = 116 * y - 16
const a = 500 * (x - y)
const b = 200 * (y - z)
return [l, a, b]
}
/**
* Converts Lab color space to Luminance-Chroma-Hue color space.
* http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
* @param {number[]}
* @return {number[]}
*/
export function labToLch([l, a, b]) {
const c = Math.sqrt(a * a + b * b)
const h = abToHue(a, b)
return [l, c, h]
}
/**
* Converts a and b of Lab color space to Hue of LCH color space.
* https://stackoverflow.com/questions/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
* @param {number} a
* @param {number} b
* @return {number}
*/
function abToHue(a, b) {
if (a >= 0 && b === 0) {
return 0
}
if (a < 0 && b === 0) {
return 180
}
if (a === 0 && b > 0) {
return 90
}
if (a === 0 && b < 0) {
return 270
}
let xBias
if (a > 0 && b > 0) {
xBias = 0
} else if (a < 0) {
xBias = 180
} else if (a > 0 && b < 0) {
xBias = 360
}
return radiansToDegrees(Math.atan(b / a)) + xBias
}
function radiansToDegrees(radians) {
return radians * (180 / Math.PI)
}
function degreesToRadians(degrees) {
return degrees * Math.PI / 180
}
/**
* Saturated colors appear brighter to human eye.
* That's called Helmholtz-Kohlrausch effect.
* Fairchild and Pirrotta came up with a formula to
* calculate a correction for that effect.
* "Color Quality of Semiconductor and Conventional Light Sources":
* https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
* @return {number}
*/
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
const l_ = 2.5 - 0.025 * l
const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
return l + l_ * g * c
}
export function getPerceivedLightness(hex) {
return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}
首先,这是旧的ITU BT.601算法的结果。左边的使用原始sRGB值,右边的使用线性值。
0.299 R + 0.587 G + 0.114 B
在这个分辨率下,左侧的实际上看起来相当不错!但是如果你仔细看,就会发现一些问题。在更高的分辨率下,不需要的伪影更加明显。线性的不会受到这些影响,但是会有很多噪音。让我们将其与ITU-R建议BT.709进行比较...
0.2126 R + 0.7152 G + 0.0722 B
哦,天啊。这显然不是用于原始sRGB值的!然而,大多数人都会这样做!
在高分辨率下,当使用线性值时,您可以真正看到这个算法的有效性。与早期的算法相比,它几乎没有噪音。虽然这些算法都不是完美的,但这个算法是最好的。