RGB'/> RGB'/> RGB'/> La conversion de l'espace colorimétrique de PIL YCbCr -> RGB | abulletproofidea.com

Question La conversion de l'espace colorimétrique de PIL YCbCr -> RGB


L'algorithme utilisé par PIL v1.1.7 donne des résultats à l'aspect «délavé». Lors de la conversion des mêmes données source en utilisant ffmpeg ça a l'air correct En utilisant mplayer donne des résultats identiques à ffmpeg (peut-être qu'ils utilisent la même bibliothèque en dessous). Cela m'amène à penser que PIL peut être à l'origine des conversions d'espace de couleur. La conversion semble provenir de 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

*/

Ceci est juste un commentaire dans la source, bien sûr c'est du code C et la fonction réelle est implémentée avec des tables de consultation pas la multiplication de matrice (le static INT16 R_Cr etc. coupés pour la brièveté):

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

J'ai googlé mais il semble y avoir beaucoup de confusion à propos de la «bonne» façon de faire cette conversion d'espace colorimétrique. Donc, ma question est la suivante: est-ce que c'est une meilleure façon?


modifier:  Après avoir lu les liens fournis par Mark Ransom, j'ai découvert que des définitions contradictoires existaient selon que vous utilisiez toute la gamme de YCbCr ou que vous étaliez sur la plage valide. Voir les liens ci-dessous pour plus d'informations:

Il semble que la version PIL utilise un algorithme incorrect. J'ai donc déplacé ma propre fonction pour la conversion qui donne les résultats corrects (version "SDTV"). Code inclus ci-dessous, pour les futurs lecteurs à utiliser:

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

13
2017-08-12 14:00


origine


Réponses:


Si vous regardez les définitions de Wikipedia, vous pouvez voir qu'il existe deux définitions contradictoires pour YCbCr. le UIT-R BT.601 définition comprime les valeurs dans la gamme 16-235 pour fournir une salle de jeu et une marge, tandis que la JPEG la version utilise la gamme complète 0-255. Si vous décodiez des valeurs dans l’espace BT.601 en utilisant la formule pour JPEG, le résultat serait définitivement effacé.


7
2017-08-12 15:54