将YCbCr转换为RGB可逆?

时间:2015-09-01 00:00:32

标签: colors rgb yuv

我正在玩一些不同的图像格式,遇到了一些奇怪的东西。当从RGB转换为YCbCr然后再转换回RGB时,结果与我的开始非常相似(像素值的差异几乎总是小于4)。但是,当我从YCbCr转换为RGB然后再转换回YCbCr时,我经常得到非常不同的值。有时值会有差异超过40。

我不确定为什么会这样。我的印象是,可以通过YCbCr表达的颜色是RGB中的颜色的子集,但看起来这是完全错误的。 YCbCr中是否有一些已知的颜色子集可以转换为RGB然后又回到原始值?

我用来转换的代码(基于this site):

def yuv2rgb(yuv):
  ret = []
  for rows in yuv:
    row = []
    for y, u, v in rows:
      c = y - 16
      d = u - 128
      e = v - 128
      r = clamp(1.164*c +           1.596*e, 16, 235)
      g = clamp(1.164*c - 0.392*d - 0.813*e, 16, 240)
      b = clamp(1.164*c + 2.017*d          , 16, 240)
      row.append([r, g, b])
    ret.append(row)
  return ret

def rgb2yuv(rgb):
  ret = []
  for rows in rgb:
    row = []
    for r, g, b in rows:
      y = int( 0.257*r + 0.504*g + 0.098*b + 16)
      u = int(-0.148*r - 0.291*g + 0.439*b + 128)
      v = int( 0.439*r - 0.368*g - 0.071*b + 128)
      row.append([y, u, v])
    ret.append(row)
  return ret

编辑:

我创建了这个问题的基本3D图表。所有点都是值差小于10的斑点。它形成了一个非常有趣的形状。 X是Cb,Y是Cr,Z是Y.

Each point is a YCbCr value that converts nicely to RGB and back

3 个答案:

答案 0 :(得分:1)

据我所知,您应该能够以最小的精度损失转换为两种格式。

网站you have mentioned有另一组转换公式称为" RGB到全范围YCbCr"和#34;全范围YCbCr到RGB",我相信这些是您应该使用的,我认为它应该使您能够前后转换而没有任何问题。

修改

由于这些公式对你不起作用,我将在android中分享用于RGB和YUV之间转换的公式:

R = clamp(1 * Y +        0 * (U - 128) + 1.13983 * (V - 128), 0, 255);
G = clamp(1 * Y + -0.39465 * (U - 128) + -0.5806 * (V - 128), 0, 255);
B = clamp(1 * Y + 2.03211 * (U - 128) +       0 * (V - 128), 0, 255);

Y = clamp(0.299    * R + 0.587    * G + 0.114    * B, 0, 255);
U = clamp(-0.14713 * R + -0.28886 * G + 0.436    * B + 128, 0, 255);
V = clamp(0.615    * R + -0.51499 * G + -0.10001 * B + 128, 0, 255);

我刚试过,似乎来回工作。注意128的和和减法,因为这个YUV表示由无符号字节范围(0..255)组成,因此是RGB(通常),所以如果你真的需要一个(16..235)和(16 .. 240)YCbCr的范围你可能需要另一个公式。

答案 1 :(得分:1)

正如我在评论中所说,您的第一个问题是您在y的循环内使用c而不是yuv2rgb进行计算。

第二个问题是你将RGB值钳位到错误的范围:RGB应该是0..255。

RGB计算应如下所示:

  r = clamp(1.164*c +           1.596*e, 0, 255)
  g = clamp(1.164*c - 0.392*d - 0.813*e, 0, 255)
  b = clamp(1.164*c + 2.017*d          , 0, 255)

答案 2 :(得分:1)

不,完全不是。 [以上所有讨论都是针对 8 位的。] 在全范围 R'G'B' 到有限范围 YCbCr 的情况下很明显(没有 bijection)。例如,您可以在此处进行测试:

https://res18h39.netlify.app/color

全范围的 R'G'B' 值 238、77、45 被编码到有限的 YCbCr 和 BT.601 矩阵:在学校舍入后你将得到有限的范围 120、90、201,但如果你将它舍入,你会在 R'G'B' 中得到 238、77、44。而 238, 77, 44 值将变为相同的值。哎呀。这是:游戏结束。

在全范围 RGB 到全范围 YCbCr 的情况下... YCbCr 中有一些值为负的 R'、G'、B'。 (例如在有限范围内 YCbCr BT.709 值 139、151、24 将是 RGB -21、182、181,只需转换为全范围 YCbCr。)再次,没有双射。

接下来,有限范围 R'G'B' 到有限范围 YCbCr ... 再次,没有双射。 YCbCr 中的黑色实际上是 16、128、128 并且仅此而已。所有其他 16、x、y 都是不允许的 [它们在 xvYCC 中,这是非标准的],而它们在 R、G、B 中,并且所有 235、128、128 都相同。当然,前面的负数 R'、G'、B' 也适用。

从有限范围到全范围,我不知道。