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 和饱和度减半。