Du nouveau

… et à mon avis c’est du lourd : https://discuss.pixls.us/t/new-feature-support-for-ctl-scripts/40716/1

Avec l’ajout du support de scripts CTL, on peut ajouter des possibilités de traitement de couleurs et de tonalités très avancées, c’est pratiquement comme si on pouvait se créer son propre pipeline !

Plus d’infos sur le wiki : agriggio / ART / wiki / Luts — Bitbucket

1 « J'aime »

Donc, il faut compiler l’interpréteur CTL ? Je vais essayer cet aprem le Repair Café !

Sauf s’il existe déjà un binaire preécompilé spour ton système d’exploitation, il faut le compiler

Alberto va dorénavant placer les scripts dans un entrepôt à part, ils ne seront plus installés en même temps qu’ART. Ils sont ici, il y en a de nouveaux :
https://bitbucket.org/agriggio/art-ctlscripts/src/master/

1 « J'aime »

Je vois qu’Alberto est en train d’ajouter le support de LUTs 3D, donc peut-être aura-t-on bientôt la possibilité d’utiliser des LUTs au format .CUBE directement dans ART, sans avoir à les convertir au format HaldCLUT. A suivre…

1 « J'aime »

Voici un script CTL traduit par Alberto à partir d’un script dctl (Davinci Resolve) que je lui ai envoyé. Ce script est censé permettre de reproduire la densité des couleurs propre aux émulsions argentique, c’est-à-dire que la saturation ne fonctionne pas comme dans la saturation classique RGB qui conduit à une augmentation la luminance des couleurs, mais qui compense en diminuant la luminance des couleurs saturées. Ça donne des couleurs plus profondes, plus agréables à l’œil.

Voici le contenu du script, qu’Alberto a visiblement choisi de ne pas inclure dans son entrepôt de scripts ctl. Copiez/collez le code dans un nouveau fichier texte, sauvez en .ctl dans le dossier ctlscripts.

// Film Density


struct float3 {
    float x;
    float y;
    float z;
};


float3 make_float3(float x, float y, float z)
{
    float3 res = { x, y, z };
    return res;
}


float fmin(float a, float b)
{
    if (a < b) {
        return a;
    } else {
        return b;
    }
}


float fmax(float a, float b)
{
    if (a > b) {
        return a;
    } else {
        return b;
    }
}


float3 RGB_to_HSV(float3 RGB)
{
    float3 HSV;
    float min = fmin(fmin(RGB.x, RGB.y), RGB.z);
    float max = fmax(fmax(RGB.x, RGB.y), RGB.z);
    HSV.z = max;
    float delta = max - min;
    if (max != 0.0) {
        HSV.y = delta / max;
    } else {
        HSV.y = 0.0;
        HSV.x = 0.0;
        return HSV;
    }
    if (delta == 0.0) {
        HSV.x = 0.0;
    } else if (RGB.x == max) {
        HSV.x = (RGB.y - RGB.z) / delta;
    } else if (RGB.y == max) {
        HSV.x = 2.0 + (RGB.z - RGB.x) / delta;
    } else {
        HSV.x = 4.0 + (RGB.x - RGB.y) / delta;
    }
    HSV.x = HSV.x * 1.0 / 6.0;
    if (HSV.x < 0.0) {
        HSV.x = HSV.x + 1.0;
    }
    return HSV;
}


float3 HSV_to_RGB(float3 HSV_in)
{
    float3 HSV = HSV_in;
    float3 RGB;
    if (HSV.y == 0.0) {
        RGB.x = HSV.z;
        RGB.y = HSV.z;
        RGB.z = HSV.z;
    } else {
        HSV.x = HSV.x * 6.0;
        float i = floor(HSV.x);
        float f = HSV.x - i;
        if (i >= 0) {
            i = fmod(i, 6.0);
        } else {
            i = fmod(i, 6.0) + 6.0;
        }
        float p = HSV.z * (1.0 - HSV.y);
        float q = HSV.z * (1.0 - HSV.y * f);
        float t = HSV.z * (1.0 - HSV.y * (1.0 - f));
        if (i == 0) {
            RGB.x = HSV.z;
        } else if (i == 1.0) {
            RGB.x = q;
        } else if (i == 2.0) {
            RGB.x = p;
        } else if (i == 3.0) {
            RGB.x = p;
        } else if (i == 4.0) {
            RGB.x = t;
        } else {
            RGB.x = HSV.z;
        }
        if (i == 0) {
            RGB.y = t;
        } else if (i == 1.0) {
            RGB.y = HSV.z;
        } else if (i == 2.0) {
            RGB.y = HSV.z;
        } else if (i == 3.0) {
            RGB.y = q;
        } else if (i == 4.0) {
            RGB.y = p;
        } else {
            RGB.y = p;
        }
        if (i == 0) {
            RGB.z = p;
        } else if (i == 1.0) {
            RGB.z = p;
        } else if (i == 2.0) {
            RGB.z = t;
        } else if (i == 3.0) {
            RGB.z = HSV.z;
        } else if (i == 4.0) {
            RGB.z = HSV.z;
        } else {
            RGB.z = q;
        }
    }
    return RGB;
}


float RGB_to_Sat(float3 RGB)
{
    float min = fmin(fmin(RGB.x, RGB.y), RGB.z);
    float max = fmax(fmax(RGB.x, RGB.y), RGB.z);
    float delta = max - min;
    float Sat = 0.0;
    if (max != 0.0) {
        Sat = delta / max;
    }
    return Sat;
}


float3 Saturation(float3 RGB_in, float luma, float Sat)
{
    float3 RGB = RGB_in;
    RGB.x = (1.0 - Sat) * luma + RGB.x * Sat;
    RGB.y = (1.0 - Sat) * luma + RGB.y * Sat;
    RGB.z = (1.0 - Sat) * luma + RGB.z * Sat;
    return RGB;
}


float get_luma(float3 RGB, float Rw, float Gw, float Bw)
{
    float R;
    float G;
    float B;
    R = Rw + 1.0 - (Gw / 2.0) - (Bw / 2.0);
    G = Gw + 1.0 - (Rw / 2.0) - (Bw / 2.0);
    B = Bw + 1.0 - (Rw / 2.0) - (Gw / 2.0);
    float luma = (RGB.x * R + RGB.y * G + RGB.z * B) / 3.0;
    return luma;
}


float Limiter(float val, float limiter)
{
    float alpha;
    if (limiter > 1.0) {
        alpha = val + (1.0 - limiter) * (1.0 - val);
    } else if (limiter >= 0.0) {
        if (val >= limiter) {
            alpha = 1.0;
        } else {
            alpha = val / limiter;
        }
    } else if (limiter < -1.0) {
        alpha =  (1.0 - val) + (limiter + 1.0) * val;
    } else if (val <= (1.0 + limiter)) {
        alpha = 1.0;
    } else { 
        alpha = (1.0 - val) / (1.0 - (limiter + 1.0));
    }
    if (alpha < 0) {
        alpha = 0;
    } else if (alpha > 1) {
        alpha = 1;
    }
    return alpha;
}


float3 transform(varying float p_R, varying float p_G, varying float p_B,
                 float p_Den, float p_WR, float p_WG, float p_WB,
                 float p_LimitS, float p_LimitL, bool p_Display)
{
    float3 rgbIn = make_float3(p_R, p_G, p_B);
    if (p_Den == 0.0 && p_Display == 0) {
        return rgbIn;
    }

    float WR = 2.0 - p_WR;
    float WG = 2.0 - p_WG;
    float WB = 2.0 - p_WB;
    float luma = get_luma(rgbIn, WR, WG, WB);
    float SatA = 1.0 / (p_Den + 1.0);
    float3 rgbOut = Saturation(rgbIn, luma, SatA);

    float alphaS = 1.0;
    float alphaL = 1.0;
    float alpha = 1.0;

    if (p_LimitS > 0.0) {
        float sat = RGB_to_Sat(rgbIn);
        alphaS = Limiter(sat, p_LimitS);
        alpha = alphaS;
    }
    if (p_LimitL > 0.0) {
        alphaL = (rgbIn.x + rgbIn.y + rgbIn.z) / 3.0;
        alphaL = Limiter(alphaL, p_LimitL);
        alpha = alpha * alphaL;
    }

    rgbOut = RGB_to_HSV(rgbOut);
    rgbOut.y = rgbOut.y * 1.0 / SatA ;
    rgbOut = HSV_to_RGB(rgbOut);

    if (alpha < 1.0) {
        rgbOut.x = rgbOut.x * alpha + (1.0 - alpha) * rgbIn.x;
        rgbOut.y = rgbOut.y * alpha + (1.0 - alpha) * rgbIn.y;
        rgbOut.z = rgbOut.z * alpha + (1.0 - alpha) * rgbIn.z;
    }

    if (p_Display) {
        rgbOut.x = alpha;
        rgbOut.y = alpha;
        rgbOut.z = alpha;
    }

    return rgbOut;
}

// @ART-colorspace: "rec2020"
// @ART-label: "Film Density"

// @ART-param: ["p_Den", "Film Density", 0, 2, 0, 0.001]
// @ART-param: ["p_WR", "Red Weight", 0, 2, 1, 0.001]
// @ART-param: ["p_WG", "Green Weight", 0, 2, 1, 0.001]
// @ART-param: ["p_WB", "Blue Weight", 0, 2, 1, 0.001]
// @ART-param: ["p_LimitS", "Low Saturation Limiter", 0, 1, 0, 0.001]
// @ART-param: ["p_LimitL", "Low Luma Limiter", 0, 1, 0, 0.001]
// @ART-param: ["p_Display", "Display Alpha"]

void ART_main(varying float r, varying float g, varying float b,
              output varying float rout,
              output varying float gout,
              output varying float bout,
              float p_Den, float p_WR, float p_WG, float p_WB,
              float p_LimitS, float p_LimitL, bool p_Display)
{
    float3 res = transform(r, g, b, p_Den, p_WR, p_WG, p_WB,
                           p_LimitS, p_LimitL, p_Display);
    rout = res.x;
    gout = res.y;
    bout = res.z;
}
1 « J'aime »

@sguyader Merci pour le script.
Ce qui est remarquable, c’est que ces scripts viennent avec leur GUI.
Autre surprise, c’est la vitesse d’exécution. je craignais au départ que ce soit long, mais non. C’est une grosse évolution.
Maintenant, ce qui peut manquer, c’est une explication sur la maniere d’utiliser tous ces scripts.

Je suis d’accord pour le manque d’info.
Concernant le script Film Density, dont le dctl original pour Resolve vient d’ici, voici une description rapide :

  • conversion transparente pour l’utilisateur de RGB vers HSV (espace de travail Rec2020 ajouté par Alberto)
  • curseur principal Film Density : contrôle la quantité globale de l’effet
  • curseurs Red / Green / Blue Weight : contrôlent la contribution de chaque canal dans l’effet densité
  • curseurs Low Saturation / Low Luma Limiter : permettent de garder l’effet sous contrôle pour la saturation ou la luminosité
  • case Display Apha : permet de visualiser la zone d’action des curseurs “Limiter”
1 « J'aime »