边缘情况下的YCbCr到RGB颜色转换错误

3

我有一些问题需要将RGB照片转换为YCbCr格式,修改亮度通道后再转换回RGB格式。通常情况下这个过程能够完美地完成,但是对于某些边缘情况,它会返回负值或大于255的值,因此当我将其转换回uint8格式以便在matplotlib中显示时,会出现明亮的斑点。

RGB转YCbCr

def rgb2ycbcr(data):
    res = np.empty(data.shape)
    res[...,0] = (data[...,0] * 0.299 + data[...,1] * 0.587 + data[...,2] * 0.114)
    res[...,1] = 128 + (data[...,0] * -0.169 + data[...,1] * -0.331 + data[...,2] * 0.5)
    res[...,2] = 128 + (data[...,0] * 0.5 + data[...,1] * -0.419 + data[...,2] * -0.081)
    return res

转换YUV到RGB:

def ycbcr2rgb(data):
    res = np.empty(data.shape)
    data[...,1] = data[...,1] - 128
    data[...,2] = data[...,2] - 128
    res[...,0] = data[...,0] * 1 + data[...,2] * 1.4
    res[...,1] = data[...,0] * 1 + data[...,1] * -0.343 + data[...,2] * -0.711
    res[...,2] = data[...,0] * 1 + data[...,1] * 1.765
    return res

奇怪的是,当我不修改 Y 通道时,照片可以成功转换(我知道在修改 Y 通道时需要保持数值范围内)。

这个有帮助吗?(链接为 https://dev59.com/XpPfa4cB1Zd3GeqPAjxG#34913974) - MegaIng
不幸的是,它甚至没有正确转换大部分图像,我所拥有的只有几个像素偏差。 - Iago Quintero
1个回答

6
由于精度问题,您可能会得到负值和超过1的值,因此您需要四舍五入/转换为整数:
RGB = np.asarray([[0, 0, 0], [255, 255, 255]])
print(rgb2ycbcr(ycbcr2rgb(RGB)))

[[-0.1423 0.6689 0.1714]
[255.1412 254.3363 254.8299]]

现在一个主要的问题是你正在使用过多舍入并且与建议 ITU-T T.871不一致的常量,稍微调整你的代码以符合四位小数点舍入的参考。
RGB = np.asarray([[0, 0, 0], [255, 255, 255]])

def rgb2ycbcr(data):
    res = np.empty(data.shape)
    res[..., 0] = (
        data[..., 0] * 0.299 + data[..., 1] * 0.587 + data[..., 2] * 0.114)
    res[..., 1] = 128 + (
        data[..., 0] * -0.1687 + data[..., 1] * -0.3313 + data[..., 2] * 0.5)
    res[..., 2] = 128 + (
        data[..., 0] * 0.5 + data[..., 1] * -0.4187 + data[..., 2] * -0.0813)
    return res


def ycbcr2rgb(data):
    res = np.empty(data.shape)
    data[..., 1] = data[..., 1] - 128
    data[..., 2] = data[..., 2] - 128
    res[..., 0] = data[..., 0] * 1 + data[..., 2] * 1.402
    res[...,
        1] = data[..., 0] * 1 + data[..., 1] * -0.3441 + data[..., 2] * -0.7141
    res[..., 2] = data[..., 0] * 1 + data[..., 1] * 1.772
    return res

print(rgb2ycbcr(ycbcr2rgb(RGB)))

[[-0.0055 -0.0082 -0.0006]
[255.0054 255.0082 255.0006]]

你仍然需要将其转换为整数,但应该放在更好的位置。 我注意到你正在原地修改“data”在“ycbcr2rgb”中,你应该在进入定义时复制数组,否则你会有非常讨厌的惊喜。 我建议实现没有四舍五入的版本来自Recommendation ITU-T T.871。 我们维护的Colour具有非常坚实和完整的Y'CbCr实现,您可以使用它来验证您的计算:
RGB = np.asarray([[0, 0, 0], [255, 255, 255]])

print(colour.YCbCr_to_RGB(
    colour.RGB_to_YCbCr(
        RGB,
        K=colour.YCBCR_WEIGHTS['ITU-R BT.601'],
        in_bits=8,
        in_int=True,
        in_legal=False,
        out_bits=8,
        out_int=True,
        out_legal=True),
    K=colour.YCBCR_WEIGHTS['ITU-R BT.601'],
    in_bits=8,
    in_int=True,
    in_legal=True,
    out_bits=8,
    out_int=True,
    out_legal=False))

[[  0   0   0]
[255 255 255]]

我建议使用BT.709权重,而不是ITU-T T.871/BT.601的权重,前者更为普遍。

前者更为广泛。JPEG仅使用BT.601。 - Валерий Заподовников

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接