簡體   English   中英

PIL 的色彩空間轉換 YCbCr -> RGB

[英]PIL's colour space conversion YCbCr -> RGB

PIL v1.1.7 使用的算法給出了“褪色”的結果。 使用ffmpeg轉換相同的源數據時,它看起來是正確的。 使用mplayer可以得到與ffmpeg相同的結果(也許它們在下面使用相同的庫)。 這讓我相信 PIL 可能會堵塞色彩空間轉換。 轉換似乎來自libImaging/ConvertYCbCr.c

/*  JPEG/JFIF YCbCr conversions

    Y  = R *  0.29900 + G *  0.58700 + B *  0.11400
    Cb = R * -0.16874 + G * -0.33126 + B *  0.50000 + 128
    Cr = R *  0.50000 + G * -0.41869 + B * -0.08131 + 128

    R  = Y +                       + (Cr - 128) *  1.40200
    G  = Y + (Cb - 128) * -0.34414 + (Cr - 128) * -0.71414
    B  = Y + (Cb - 128) *  1.77200

*/

這只是源代碼中的注釋,當然它是 C 代碼,實際的 function 是通過查找表實現的,而不是矩陣乘法( static INT16 R_Cr

void
ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels)
{
    int x;
    UINT8 a;
    int r, g, b;
    int y, cr, cb;

    for (x = 0; x < pixels; x++, in += 4, out += 4) {

        y = in[0];
        cb = in[1];
        cr = in[2];
        a = in[3];

        r = y + ((           R_Cr[cr]) >> SCALE);
        g = y + ((G_Cb[cb] + G_Cr[cr]) >> SCALE);
        b = y + ((B_Cb[cb]           ) >> SCALE);

        out[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r;
        out[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g;
        out[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b;
        out[3] = a;
    }
}

我已經用谷歌搜索了,但似乎對進行這種色彩空間轉換的“正確”方法有很多困惑。 所以我的問題是,以上是否正確——如果不是,還有什么更好的方法?


編輯:閱讀 Mark Ransom 提供的鏈接后,我發現存在沖突的定義取決於您是使用 YCbCr 的全部范圍還是限制在有效范圍內。 有關更多信息,請參見下面的鏈接:

似乎 PIL 版本使用了不正確的算法,所以我推出了自己的 function 進行轉換,得到正確的外觀結果(“SDTV”版本)。 代碼如下,供未來讀者使用:

from numpy import dot, ndarray, array

def yuv2rgb(im, version='SDTV'):
    """
    Convert array-like YUV image to RGB colourspace

    version:
      - 'SDTV':  ITU-R BT.601 version  (default)
      - 'HDTV':  ITU-R BT.709 version
    """
    if not im.dtype == 'uint8':
        raise TypeError('yuv2rgb only implemented for uint8 arrays')

    # clip input to the valid range
    yuv = ndarray(im.shape)  # float64
    yuv[:,:, 0] = im[:,:, 0].clip(16, 235).astype(yuv.dtype) - 16
    yuv[:,:,1:] = im[:,:,1:].clip(16, 240).astype(yuv.dtype) - 128

    if version.upper() == 'SDTV':
        A = array([[1.,                 0.,  0.701            ],
                   [1., -0.886*0.114/0.587, -0.701*0.299/0.587],
                   [1.,  0.886,                             0.]])
        A[:,0]  *= 255./219.
        A[:,1:] *= 255./112.
    elif version.upper() == 'HDTV':
        A = array([[1.164,     0.,  1.793],
                   [1.164, -0.213, -0.533],
                   [1.164,  2.112,     0.]])
    else:
        raise Exception("Unrecognised version (choose 'SDTV' or 'HDTV')")

    rgb = dot(yuv, A.T)
    result = rgb.clip(0, 255).astype('uint8')

    return result

如果您查看 Wikipedia 的定義,您會發現 YCbCr 有兩個相互沖突的定義。 ITU-R BT.601定義將值壓縮到 16-235 范圍以提供腳部空間和頭部空間,而JPEG版本使用完整范圍 0-255。 如果您要使用 JPEG 的公式對 BT.601 空間中的值進行解碼,那么結果肯定會被淘汰。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM