Цветовое пространство пил преобразование YCbCr на -> РГБ

алгоритм, используемый 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, и фактическая функция реализовано с помощью таблиц поиска не матричного умножения (the static INT16 R_Cr etc. для краткости):

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

я погуглил, но, похоже, есть много путаницы о "правильном" способе преобразования цветового пространства. Поэтому мой вопрос в том, правильно ли это - и если нет, то что является лучшим способом?


edit: после прочтения ссылок, предоставленных Mark Ransom, я обнаружил, что противоречивые определения существуют в зависимости от того, используете ли вы полный диапасон 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 ответов


Если вы посмотрите на определения Википедии, вы можете увидеть, что есть два противоречивых определения для YCbCr. The ITU-R BT.601 определение сжимает значения в диапазоне 16-235, чтобы обеспечить пространство для ног и запас, в то время как в формате JPEG версия использует полный диапазон 0-255. Если вы должны были декодировать значения в BT.601 пробел используя формулу для JPEG, результат определенно будет выглядеть размытым.