YUV调整Y分量的效果?
Effect of adjusting Y component in YUV?
我一直在对一个程序进行逆向工程,最近遇到了一个函数,该函数旨在创建一种用于文本选择的半透明颜色。它通过将 RGB 转换为 YUV、改变 Y(亮度?)分量,然后转换回 RGB 来实现。
uint32_t CalcSelectionColor(uint32_t bgr)
{
double r,g,b;
double y,u,v;
r = (bgr >> 0) & 0xFF;
g = (bgr >> 8) & 0xFF;
b = (bgr >> 16) & 0xFF;
/* RGB to YUV */
y = 0.299*r + 0.587*g + 0.114*b;
u = (b-y) * 0.565 * 0.5;
v = (r-y) * 0.713 * 0.5;
/* lower brightness? */
y = 255.0 - y;
/* YUV to RGB */
r = y + 1.403*v;
g = y - 0.344*u - 0.714*v;
b = y + 1.77*u;
return ((uint8_t)(b) << 16) | ((uint8_t)(g) << 8) | ((uint8_t)(r));
}
作为对计算机图形学知识非常有限的人,我只想更详细地了解它在转换之间的作用,以及更广泛意义上的实际预期效果。这是调整颜色或其他亮度的常用方法吗?如果我传入0x00FF00,我得到的结果是0x1E9D1E
此代码中使用的公式类似于从 RGB 到 YUV 并返回的 Julien 变换:
Transformation from RGB to YUV:
Y = 0.299R + 0.587G + 0.114B
U'= (B-Y)*0.565
V'= (R-Y)*0.713
Transformation from YUV to RGB:
R = Y + 1.403V'
G = Y - 0.344U' - 0.714V'
B = Y + 1.770U'
但是,您的代码中的公式有点不同。虽然反向变换是相同的,但正向变换对 U 和 V 分量都有一个额外的乘数 0.5。还有一个对亮度分量的微不足道的操作
y = 255.0 - y
这只是反转了亮度。那么,这里发生了什么?
如果您使用普通的 Julien RGB->YUV 变换,您会得到一种颜色表示,它是亮度 Y 和两个色调分量 U 和 V 的组合,它们定义了这张图片中显示的颜色:
但是,在您的代码中,您还将 U 和 V 分量都乘以 0.5。这意味着,在这个 UV 平面上,您从任何给定的颜色向原点 (0, 0) 靠近两倍。例如,如果初始颜色为 A 且 UV 坐标为 (-0.4, 0.3),则您将获得具有 UV 坐标 (-0.2, 0.15) 的新颜色 B。同理颜色C(0.2,-0.3)变成颜色D(0.1,-0.15):
然后反转颜色的亮度,使深色变亮,亮色变暗。这是你代码的效果。
这不是很常见,但这是一个非常好的方法。 HSL/HSV 等常用模型不能正确表示强度,并且在 hue/color 的情况下会出现一些奇怪的分段线性问题。 YUV 是一个非常好的色彩空间,表示沿一个轴的强度和垂直平面中的色度 (hue/color)。
通常在不调整(至少夹紧)U 和 V 的情况下修改 Y 有点可疑,因为在极端情况下(Y=0 黑,Y=全白)U 和 V 的范围有限(根本没有范围)端点)。否则应用它们会使您离开 RGB 立方体,并在您返回 RGB 时导致虚假的裁剪结果。但这里的技巧非常巧妙。代码是反转 Y,同时保持色度固定,因此接近黑色的 U 和 V 的输入范围限制将自动确保它们在输出中大致正确,反之亦然。
正如 Alex 所指出的,此处的代码还将色度值减半,从而降低了色彩饱和度。这可能是为了避免上述裁剪问题,但这不是必需的。但也许这也是预期视觉效果的一部分。
所以,TL;DR:效果是反相 intensity/luma 和饱和度减半。
我一直在对一个程序进行逆向工程,最近遇到了一个函数,该函数旨在创建一种用于文本选择的半透明颜色。它通过将 RGB 转换为 YUV、改变 Y(亮度?)分量,然后转换回 RGB 来实现。
uint32_t CalcSelectionColor(uint32_t bgr)
{
double r,g,b;
double y,u,v;
r = (bgr >> 0) & 0xFF;
g = (bgr >> 8) & 0xFF;
b = (bgr >> 16) & 0xFF;
/* RGB to YUV */
y = 0.299*r + 0.587*g + 0.114*b;
u = (b-y) * 0.565 * 0.5;
v = (r-y) * 0.713 * 0.5;
/* lower brightness? */
y = 255.0 - y;
/* YUV to RGB */
r = y + 1.403*v;
g = y - 0.344*u - 0.714*v;
b = y + 1.77*u;
return ((uint8_t)(b) << 16) | ((uint8_t)(g) << 8) | ((uint8_t)(r));
}
作为对计算机图形学知识非常有限的人,我只想更详细地了解它在转换之间的作用,以及更广泛意义上的实际预期效果。这是调整颜色或其他亮度的常用方法吗?如果我传入0x00FF00,我得到的结果是0x1E9D1E
此代码中使用的公式类似于从 RGB 到 YUV 并返回的 Julien 变换:
Transformation from RGB to YUV:
Y = 0.299R + 0.587G + 0.114B
U'= (B-Y)*0.565
V'= (R-Y)*0.713
Transformation from YUV to RGB:
R = Y + 1.403V'
G = Y - 0.344U' - 0.714V'
B = Y + 1.770U'
但是,您的代码中的公式有点不同。虽然反向变换是相同的,但正向变换对 U 和 V 分量都有一个额外的乘数 0.5。还有一个对亮度分量的微不足道的操作
y = 255.0 - y
这只是反转了亮度。那么,这里发生了什么?
如果您使用普通的 Julien RGB->YUV 变换,您会得到一种颜色表示,它是亮度 Y 和两个色调分量 U 和 V 的组合,它们定义了这张图片中显示的颜色:
但是,在您的代码中,您还将 U 和 V 分量都乘以 0.5。这意味着,在这个 UV 平面上,您从任何给定的颜色向原点 (0, 0) 靠近两倍。例如,如果初始颜色为 A 且 UV 坐标为 (-0.4, 0.3),则您将获得具有 UV 坐标 (-0.2, 0.15) 的新颜色 B。同理颜色C(0.2,-0.3)变成颜色D(0.1,-0.15):
然后反转颜色的亮度,使深色变亮,亮色变暗。这是你代码的效果。
这不是很常见,但这是一个非常好的方法。 HSL/HSV 等常用模型不能正确表示强度,并且在 hue/color 的情况下会出现一些奇怪的分段线性问题。 YUV 是一个非常好的色彩空间,表示沿一个轴的强度和垂直平面中的色度 (hue/color)。
通常在不调整(至少夹紧)U 和 V 的情况下修改 Y 有点可疑,因为在极端情况下(Y=0 黑,Y=全白)U 和 V 的范围有限(根本没有范围)端点)。否则应用它们会使您离开 RGB 立方体,并在您返回 RGB 时导致虚假的裁剪结果。但这里的技巧非常巧妙。代码是反转 Y,同时保持色度固定,因此接近黑色的 U 和 V 的输入范围限制将自动确保它们在输出中大致正确,反之亦然。
正如 Alex 所指出的,此处的代码还将色度值减半,从而降低了色彩饱和度。这可能是为了避免上述裁剪问题,但这不是必需的。但也许这也是预期视觉效果的一部分。
所以,TL;DR:效果是反相 intensity/luma 和饱和度减半。