JPEG: YCrCb <-> RGB 转换精度

JPEG: YCrCb <-> RGB conversion precision

我已经使用
中的 JPEG 转换公式实现了 rgb->ycrcb 和 ycrcb->rgb 转换 http://www.w3.org/Graphics/JPEG/jfif3.pdf
(相同于:http://en.wikipedia.org/wiki/YCbCr(JPEG 转换))。

检查结果是否正确时(原始->YCrCb->RGB),部分像素相差1,例如201->200。

精度误差的平均百分比为 0.1%,因此并不严重。

/// converts RGB pixel to YCrCb using { en.wikipedia.org/wiki/YCbCr: JPEG conversion }
ivect4 rgb2ycrcb(int r, int g, int b)
{
    int y =  round(0.299*r + 0.587*g + 0.114*b) ;
    int cb = round(128.0 - (0.1687*r) - (0.3313*g) + (0.5*b));
    int cr = round(128.0 + (0.5*r) - (0.4187*g) - (0.0813*b));
    return ivect4(y, cr, cb, 255);
}
/// converts YCrCb pixel to RGB using { en.wikipedia.org/wiki/YCbCr: JPEG conversion }
ivect4 ycrcb2rgb(int y, int cr, int cb)
{
    int r = round(1.402*(cr-128) + y);
    int g = round(-0.34414*(cb-128)-0.71414*(cr-128) + y);
    int b = round(1.772*(cb-128) + y);
    return ivect4(r, g, b, 255);
}

我用圆公式:
地板((x)+ 0.5)

使用其他类型的舍入时,例如float(int) 或 std::ceil(),结果更糟。

那么,有没有办法在不损失精度的情况下进行 YCrCb <-> RGB 转换?

问题不在于舍入模式。

即使您将浮点常量转换为比率并且仅使用整数数学运算,您仍然会在逆运算后看到不同的值。

要了解原因,请考虑一个函数,我告诉您我要将数字 0 到 N 移动到 0 到 N-2 的范围内。事实上,这个变换只是没有逆。您可以通过浮点计算 (f(x) = x*(N-2)/N) 或多或少地精确表示它,但一些相邻值将映射到整数数学中的相同结果(鸽笼原理!)。这是一个简化和 "compresses" 范围,但同样的事情发生在任意仿射变换中,就像您正在使用的这个一样。

如果你有 r、g、b 浮点数,并保持这种状态直到你量化为整数,那将是一个不同的故事 - 但在整数中你总是会看到原始和整数之间的一些差异逆.

另一个问题是rgb和YCbCR之间没有一对一的映射。存在没有对应RGB值的YCbCr值和没有对应YCbCR值的RBG值

当两个三元组使用相同数量的比特时,只有大约 60% 的 RGB 值可以在 YCbCr space 中表示。这意味着当您采用 3*8 位 RGB 三元组,将其转换并舍入为 3*8 位精度时,RGB->YCbCr 中发生的损坏最大。诀窍是以更高的精度存储 YCbCr 三元组,直到需要进行前向 DCT。在那里,无论如何都需要扩大数据,所以你可以做,例如16 bit * 16 bit -> MSB16相乘,各种SIMD指令集都很好支持

在解码器处则相反:逆 DCT 的结果必须以更高的精度存储,直到进行 YCbCr->RGB 转换。

这不会使过程无损,但对于 JPEG,它可能会在质量等级的极端高端购买几 dB 的 PSNR,即肉眼无法看到差异但可以测量。

是的,据说 JPEG XR 定义了一种可逆的颜色转换。如果您想深入调查他们是如何做的,代码是开源的。该方法在我链接到的维基页面上有松散的描述。

另外 this SO post 可能会给您一些见解。