本文介绍了PIL的色彩空间转换的YCbCr - > RGB的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由PIL v1.1.7使用的算法给我'淘汰'的妆效。当我使用的ffmpeg转换成同一来源的数据看起来正常。用mplayer给予的ffmpeg相同的结果(可能是他们使用下的同一库)。这使我相信PIL可堆砌了他们的色彩空间转换。转换似乎 libImaging / ConvertYCbCr.c要采购

  / * JPEG / JFIF的YCbCr转换    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 $ C $Ç所以他们不使用矩阵乘法和实际功能与查找表来实现(在静态INT16 R_Cr 等剪断为了简洁):

 无效
ImagingConvertYCbCr2RGB(UINT8 *总分,常量UINT8 *中,INT像素)
{
    INT X;
    UINT8一个;
    INT R,G,B;
    INT Y,CR,CB;    为(X = 0; X&下;像素; X ++,在+ = 4,出+ = 4){        Y =在[0];
        CB = [1];
        CR =在[2];
        一个= [3];        R = Y +((R_Cr [CR])GT;> SCALE);
        G = Y +((G_Cb [CB] + G_Cr [CR])GT;> SCALE);
        B = Y +((B_Cb [CB])GT;> SCALE);        出[0] =(为r = 0)? 0:(R> = 255)? 255,R;
        出[1] =(克下; = 0)? 0:(g取代; = 255)? 255:克;
        出[2] =(b将; = 0)? 0:(B> = 255)? 255:B;
        出[3] =一个;
    }
}

我用Google搜索,但似乎有很多混乱的关于正确的方式来做到这一点色彩空间转换。所以我的问题是,在上述正确的,如果没有什么更好的办法?


编辑:的看完由马克赎金提供的链接,我推出我自己的功能,给了我正确的妆效工作:

 高清YUV2RGB(IM):
  转换阵列状的YUV图像为rgb色彩空间  因为在PIL的模式的YCbCr纯numpy的实施b0rked。
  TODO:这口东西给C扩展,使用速度查找表
  
  ##矛盾的定义是否存在取决于你是否使用全套
  的YCbCr的##或夹紧到的有效范围。看这里
  ## http://www.equasys.de/colorconversion.html
  ## http://www.fourcc.org/fccyvrgb.php
  从numpy的进口点,ndarray,阵列
  如果不是im.dtype =='UINT8:
    提高ImageUtilsError('YUV2RGB只实施了UINT8数组)  ##更好的输入到有效范围内的剪辑只是为了安全起见
  YUV = ndarray(im.shape)## float64
  YUV [:,:0] = IM [:,:0] .clip(16,235).astype(yuv.dtype) - 16
  YUV [:,:,1:] = IM [:,:,1:]夹(16,240).astype(yuv.dtype) - 128  ## ITU-R BT.601版本(SDTV)
  A =阵列([1,0,0.701]
             [1,-0.8​​86 * 0.114 / 0.587,-0.701 * 0.299 / 0.587],
             [1.,0.886,0。]])
  A [:0] * = 255./219。
  A [:,1:] * = 255./112。  ## ITU-R BT.709版本(HDTV)的
#A =阵列([1.164,0,1.793]
#[1.164,-0.213,-0.533]
#[1.164,2.112,0])  RGB =点(YUV,A.T)
  返回rgb.clip(0,255).astype('UINT8')


解决方案

如果你看一下维基百科的定义,你可以看到有针对的YCbCr两种相互矛盾的定义。该定义COM presses值的范围16 235提供footroom和头部空间,而版采用全范围为0-255。如果您在使用JPEG公式BT.601空间去code值,其结果肯定会看起来冲了出去。

The algorithm used by PIL v1.1.7 gives me 'washed out' looking results. When I convert the same source data using ffmpeg it looks normal. Using mplayer gives identical results to ffmpeg (probably they use the same library underneath). This leads me to believe PIL may be stuffing up their colour space conversions. The conversion seems to be sourced in 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

*/

This is just a comment in the source, of course it's C code so they don't use matrix multiplication and the actual function is implemented with lookup tables (the static INT16 R_Cr etc. snipped for brevity) :

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;
    }
}

I have googled but there seems to be a lot of confusion about the 'right' way to do this colour space conversion. So my question is, is the above correct and if not what is a better way?


edit: After reading the links provided by Mark Ransom, I rolled my own function for the job which gives me the correct looking results:

def yuv2rgb(im):
  """convert array-like yuv image to rgb colourspace

  a pure numpy implementation since the YCbCr mode in PIL is b0rked.
  TODO: port this stuff to a C extension, using lookup tables for speed
  """
  ## conflicting definitions exist depending on whether you use the full range
  ## of YCbCr or clamp out to the valid range.  see here
  ## http://www.equasys.de/colorconversion.html
  ## http://www.fourcc.org/fccyvrgb.php
  from numpy import dot, ndarray, array
  if not im.dtype == 'uint8':
    raise ImageUtilsError('yuv2rgb only implemented for uint8 arrays')

  ## better clip input to the valid range just to be on the safe side
  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

  ## ITU-R BT.601 version (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.

  ## ITU-R BT.709 version (HDTV)
#  A = array([[1.164,     0.,  1.793],
#             [1.164, -0.213, -0.533],
#             [1.164,  2.112,     0.]])

  rgb = dot(yuv, A.T)
  return rgb.clip(0, 255).astype('uint8')
解决方案

If you look at Wikipedia's definitions, you can see that there are two conflicting definitions for YCbCr. The ITU-R BT.601 definition compresses the values to the range 16-235 to provide footroom and headroom, while the JPEG version uses the full range 0-255. If you were to decode values in the BT.601 space using the formula for JPEG, the result would definitely look washed out.

这篇关于PIL的色彩空间转换的YCbCr - &GT; RGB的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 04:16
查看更多