提问人:Askaga 提问时间:2/11/2017 更新时间:11/9/2023 访问量:5317
如何计算具有相同感知亮度的RGB值
How to calculate RGB values with identical perceived brightness
问:
我正在尝试生成具有相同感知亮度的 RGB 颜色。
函数 R*0.2126+ G*0.7152+ B*0.0722 据说用于计算给定 RGB 颜色的感知亮度(或等效灰度颜色)。
假设我们对所有 RGB 值使用区间 [0,1],我们可以计算以下内容:
- 黄色 = RGB(1,1,0) => 亮度=0.9278
- 蓝色 = RGB(0,0,1) => 亮度=0.0722
因此,为了使黄色调与蓝色调一样暗淡,我可以简单地对每个RGB组件的黄色进行简单的计算:
- dim_yellow = 黄色 * 0.0722 / 0.9278
然而,当做相反的事情时,从而将蓝色“放大”到与原始黄色相同的感知亮度时,B 分量显然超过 1,这不能显示在计算机屏幕上。
我猜想多余的 B 分量丢失的亮度可以“重新分配”到 R 和 G 分量,从而伪造出更亮的蓝色。那么,计算这些最终RGB值的最佳通用方法是什么?
答:
我建议您使用 HSV 颜色模型而不是 RGB,因为您只需修改 Value(Brightness) 组件即可轻松实现所需的内容。 wiki 页面还包含如何将 RGB 转换为 HSV 并返回
编辑: 尝试使用CIELAB色彩空间,因为它接近人类的视觉
评论
这些不是你要找的数学
函数 R*0.2126+ G*0.7152+ B*0.0722 据说用于计算给定 RGB 颜色的感知亮度(或等效灰度颜色)。
不,这是不正确的,或者至少是不完整的。是的,R*0.2126+ G*0.7152+ B*0.0722 是光谱系数,但这不是全部。
首先,不要在此上下文中使用术语亮度。亮度不是光的量度,它是一种感知,而不是一个可测量的量。当我们谈论光和比色法时,请使用术语“亮度”(L 或 Y)。亮度是光的线性量度,而不是感知。
CIELAB的感知亮度或L*(Lstar)是基于人类对亮度变化的感知。它接近约0.43的功率曲线。
sRGB是通常用于计算机显示器和网络的色彩空间,它不像光那样是线性的,也不完全像感知的L*曲线。sRGB的传输曲线接近1/2.2的功率曲线。也就是说,sRGB数据/信号被提高到0.455的幂,然后显示器施加2.2的幂。
坏了
你的数学运算不起作用,因为你没有考虑转移曲线。在应用系数之前,必须对 sRGB 值进行线性化。那么这些之和将等于亮度 1。
sRGB 中的 #FFFF00 等于亮度的 0.9278,但这是 96.76% 的 sRGB 值或 97.14% 的 L* 值
sRGB 中的 #0000FF 等于亮度的 0.0722,但这是 29.79% 的 sRGB 值或 32.3% 的 L* 值
下面是一个包含一些值的图表,扩展了您的示例:
因此,要回答其余的问题,要获得与显示器能够达到的更高亮度的蓝色,需要降低亮度,添加 R 和 G 以增加亮度。
在此图表中,我们有完全饱和但较深的红色和绿色以匹配 7% 的蓝色亮度,然后我们有 18% 的亮度(如 18% 的灰色卡片),在这里我们必须降低蓝色的饱和度以增加亮度值。
如何计算
首先,您需要线性化sRGB分量,然后应用系数,如果您需要确定亮度。如果你想出一些对线性化组件进行数学运算的值,那么你需要重新伽玛编码以回到sRGB。
我已经讨论过这是其他几个答案,比如这里。
对于给定的色调,您只需乘以所需的亮度与当前亮度的比率即可。
即,要找到与蓝色具有相同亮度的红色,我们可以
// l = .299 r^2 + .587 g^2 + .114 b^2
target_color = V(1, 0, 0);
desired_brightness = sqrt(sum(V(0, 0, 1) * V(0, 0, 1) * V(0.229, 0.587, 0.114)));
current_brightness = sqrt(sum(target_color * target_color * V(0.229, 0.587, 0.114)));
desired_color = target_color * desired_brightness / current_brightness
演示 https://tom-huntington.github.io/match-brightness/
我意识到 Myndx 的公式更好,但它是非线性的,所以你必须使用割线方法。我们可以对亮度进行平方根以加速收敛(因为它的关系近似于平方):
fn sRGBtoLin(colorChannel : f32) -> f32 {
if ( colorChannel <= 0.04045 ) {
return colorChannel / 12.92;
} else {
return pow((( colorChannel + 0.055)/1.055),2.4);
}
}
fn luminance(color : vec3<f32>) -> f32 {
var lin = vec3<f32>(sRGBtoLin(color.x), sRGBtoLin(color.y), sRGBtoLin(color.z));
var coef = vec3<f32>(0.2126, 0.7152, 0.0722);
//var coef = vec3<f32>(0.299 , 0.587 , 0.114);
var rng = lin * coef;
return sqrt(rng.x + rng.y + rng.z);
}
main() {
var target_brightness = brightness(target_color);
var xi = color * 1.5;
var xim = color / 1.5;
for (var i = 0u; i < 20; i++) {
var bxi = brightness(xi);
var bxim = brightness(xim);
var xip = xi - (bxi - target_brightness) * (xi - xim) / (bxi - bxim);
if (distance(bxi, bxim) > 0.000000000001)
{
xim = xi;
xi = xip;
}
}
下一个:“内存不足”是可恢复的错误吗?
评论