2011-08-12 18 views
11

PIL v1.1.7で使用されているアルゴリズムは、「洗い流された」見た目の結果をもたらします。 ffmpegを使用して同じソースデータを変換すると、正しく表示されます。 mplayerを使用すると、同じ結果がffmpeg(おそらく同じライブラリを下に使用している)になります。これはPILが色空間変換を詰め込んでいるかもしれないと私に信じさせる。変換はlibImaging/ConvertYCbCr.cで調達しているように見える:PILの色空間変換YCbCr - > RGB

/* 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のコードだと、実際の機能は、ルックアップテーブルを用いて実装されて乗算を行列でない(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; 
    } 
} 

私はグーグルではありますが、この色空間変換を行う「正しい」方法について多くの混乱があるようです。だから私の質問は、上記の正しいです - そして、より良い方法は何ですか?


編集:マーク身代金から提供されたリンクを読んだ後、私は矛盾する定義は、あなたがたYCbCrの全範囲を使用するか、有効な範囲に出クランプかどうかに応じて存在することを発見しました。詳細は下記のリンクを参照してください。

PILのバージョンが正しくないアルゴリズムを使用しているようですので、私は正しいを与える変換のために私自身の機能を巻いてきました(「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 
+1

http://en.wikipedia.org/wiki/YCbCrのメカニズムと一致していますか? –

答えて

7

をあなたはウィキペディアの定義を見れば、あなたはYCbCrのための2つの競合する定義が存在していることがわかります。 ITU-R BT.601の定義では、フットルームとヘッドルームを提供するために値を16-235の範囲に圧縮し、JPEGのバージョンは0-255のフルレンジを使用します。 JPEGの公式を使ってBT.601空間の値をデコードすると、結果は間違いなく消えてしまいます。

+1

これは正しい軌道に乗ってくれました。さまざまな基準(擬似語)の明確な説明がここにあります - > http://www.equasys.de/colorconversion.html – wim

関連する問題