bunex-industries

Traitement d'image interactif

Modifiez les sliders et les points de contrôle de la courbe. Un exemple de suite de fonctions appliqués sur les pixels d'un canvas HTML. Focus sur les "courbes", fameux outil de Photoshop pour ajuster les couleurs et les valeurs d'une image. Quelques mots sur vImage.

Courbe en entrée

appliquer à la luminosité

Balance des couleurs

préserver la luminosité
CR MV JB

Saturation

0% 200%

Grain

0% 100%

Netteté

-100% +100%

Vignettage

gamma Ø int. Ø ext.

Scratchs

0% 100%

J'utilise deux sources bitmap additionnelles pour le grain d'une part, et les scratchs. Les opérations de composition sont effectuées sur des canvas invisibles car elles nécessitent un contexte.

Pour les opérations mathématiques de cet exemple, les résultats visuels sont vraiment ressemblants à ceux de la même opération faite avec des kernels Core Image, à une question de colorimétrie près sans doute, mais en assumant des entrées en sRGB, j'ose dire que les résultats que j'ai vu étaient indiscernables :-)

Cela fonctionne sur Raspberry Pi et j'ai pu filtrer en temps réel un stream video venu d'une webcam. Entre le stream et l'image processing évidemment, ce n'est pas de la HD. Je posterai des détails ultérieurement !

Une petite précision à propos de la courbe. Elle est basée sur une courbe de bézier.

function Bezier( t,  P0,  P1,  P2,  P3)
{
    //Pn = coordonnées X ou coordonnées Y des points de contrôle
    //Renvoie la valeur X ou Y selon le jeu de valeur fourni du point sur la courbe à la coordonnée t.
    return Math.pow(1-t,3)*P0 + 3*Math.pow(1-t,2)*t*P1 + 3*(1-t)*Math.pow(t,2)*P2 + Math.pow(t,3)*P3;
}

Je la simplifie ensuite en une série de segments pour la dessiner mais aussi pour créer une sorte de table. Je fais des interpolations linéaires segment après segment (20 segments). Au coup d'oeil cela ne se voit pas, mais il est clair que c'est une perte de linéarité inutile. Cette partie peut être améliorée en créant une table de valeurs sur 256 niveaux, mais certainement contre un coût en performance, ce qui est difficilement acceptable.

Sur Mac cette fois, une courbe de contraste sérieuse peut être faite très vite et très bien sur des images avec les fonctions de vImage (dans Accelerate Framework). La vitesse est assez bluffante, sur les grandes images c'est impressionnant. Voir l'extrait de code ci-après. Sinon pour l'outil de courbe interactif, ceci est exactement ce qu'il faut : https://github.com/OpenFibers/Photoshop-Curves/tree/master/PSCurves. la chose mathématique m'est un peu obscure j'avoue... (voir la fonction secondDerivative)


// red, green, blue sont des arrays de 256 valeurs 8bits (un tableau pour chaque couche)
-(CGImageRef)lookUpTable:(CGImageRef)image red:(Pixel_8*)red green:(Pixel_8*)green blue:(Pixel_8*)blue {
    vImage_Error err;
    vImage_CGImageFormat outputFormat = {
        .bitsPerComponent = (uint32_t) CGImageGetBitsPerComponent(image),
        .bitsPerPixel = (uint32_t) CGImageGetBitsPerPixel(image),
        .bitmapInfo = CGImageGetBitmapInfo(image),
        .colorSpace = CGImageGetColorSpace(image)};

    vImage_Buffer intputBuffer, outputBuffer;
    CGFloat color[] = {1.0,1.0,1.0,1.0};
    vImageBuffer_InitWithCGImage(&intputBuffer, &outputFormat, color, image, 0);
    vImageBuffer_Init(&outputBuffer, CGImageGetHeight(image), CGImageGetWidth(image), 32, 0);

    //on crée une table pour l'alpha à 255 partout.
    Pixel_8 alpha[255] = {255};

    //Cette fonction transforme les pixels selon les tables RGB de 256 points issus de la courbe.
    vImageTableLookUp_ARGB8888(&intputBuffer, &outputBuffer, red, green, blue, alpha, 0);

    CGImageRef outputImage = vImageCreateCGImageFromBuffer(&outputBuffer, &outputFormat, NULL, NULL, 0, &err);

    free(intputBuffer.data);
    free(outputBuffer.data);

    return outputImage;
}