IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaScript Discussion :

Changer la luminosité d'une image avec canvas [API HTML5]


Sujet :

JavaScript

  1. #1
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut Changer la luminosité d'une image avec canvas
    Bonjour.

    J'essaye de faire une classe javascript me permettant de manipuler une image avec un canvas. Jusqu’à présent j'ai réussi à faire les changements souhaités( contraste, luminosité, noir&blanc, etc ...). Seulement les modifications de contraste et de luminosité ne fonctionne qu'une fois. Je m'explique. Ces modifications sont appliqués à l'aide d'un curseur allant de -100 à 100. Si je déplace le curseur à la valeur 100. Tout ce passe très bien. Mon image deviens très lumineuse. Seulement si je le ramène à 0, je ne retrouve pas l'image de départ. Après quelque recherche, cela viens du format RGB qui ne peut stocker que 255 valeur pour chaque couleur. Ainsi en haute luminosité. l'image est "brulée et perd des informations. Pour contrer ce phénomène je passe mon image d'un Uint8 clampe à un Int32Array permettant de contenir plus de valeur. Je fais mes changement et je rechange mon Int32Arrayen Uint8 clampe
    pour mettre à jour le canvas. Seulement voila. Rien ne se passe au moment du cast. Voici mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
     
    function CanvasEffect(canvas) {
        var ctx = canvas.getContext("2d");
        var WIDTH = canvas.width;
        var HEIGHT = canvas.height;
        var stack = [];
        var imageData;
        var data;
     
        this.apply = function(effect, intensity) {
            if(data == undefined) {
                imageData = ctx.getImageData(0,0, WIDTH, HEIGHT);
                var imgData = imageData.data;
                // Save the data of the orignal file.
                data= new Int32Array(imgData);
            }
            var int = Number(intensity);
            var refresh =  true;
            switch (effect) {
            case "blur":
                refresh = false;
                stackBlurCanvasRGB('canvas', 0, 0,  WIDTH, HEIGHT, int);
                break;
            case "sharpen":
     
                break;
            case "luminosity":
                console.log("stack['luminosity'] : " + stack["luminosity"]);
                lastInt = (stack["luminosity"] == undefined) ? 0 : stack["luminosity"];
                newInt = int - lastInt;
                console.log("int : " + int, "lastInt : " + lastInt, "newInt : " + newInt);
                for(var i=0;i < data.length;i+=4) {
                    data[i] += newInt; //Red
                    data[i+1] += newInt; //Green
                    data[i+2] += newInt; //Blue
                }
                break;
            case "contrast":
                lastInt = (stack["contrast"] == undefined) ? 0 : stack["contrast"];
                newInt = int - lastInt;
                var factor = (259 * (newInt + 255)) / (255 * (259 - newInt));
                for(var i = 0; i < data.length ; i+=4) {
                    data[i] = factor *  (data[i] - 128) + 128; //Red
                    data[i+1] = factor *  (data[i+1] - 128) + 128; //Green
                    data[i+2] = factor *  (data[i+2] - 128) + 128; //Blue
                }
                break;
            case "negatif":
                console.log("negatif");
                for(var i=0;i < data.length;i+=4) {
                    data[i] = 256-data[i]; //Red
                    data[i+1] = 256-data[i+1]; //Green
                    data[i+2] = 256 - data[i+2]; //Blue
                }
                break;
            case "b&w":
                console.log("b&w");
                for(var i=0;i < data.length;i+=4) {
                    var gris = data[i]*0.3 + data[i+1]*0.59 + data[i+2]*0.11;
                    data[i] = gris; //Red
                    data[i+1] = gris; //Green
                    data[i+2] = gris; //Blue
                }
                break;
            case "sepia":
                console.log("sepia");
                for(var i=0;i < data.length;i+=4) {
                    var r = data[i]*0.299 + data[i+1]*0.587 + data[i+2]*0.114;
                    data[i] = Math.max(0, Math.min(255, r*1.351)); //Red
                    data[i+1] = Math.max(0, Math.min(255, r*1.203)); //Green
                    data[i+2] = Math.max(0, Math.min(255, r*0.937)); //Blue
                }
                break;
            default:
                break;
            }
     
            if(refresh) {
                // Voici le code qui ne fonctionne pas
                var clampedArray = new Uint8ClampedArray(data); // Conversion vers un Uint8ClampedArray. 
                var newImgData=ctx.createImageData( WIDTH, HEIGHT);
                newImgData.data = clampedArray;
                console.log(newImgData.data, clampedArray);
                ctx.putImageData(newImgData,0 ,0);
                console.log(imgData.data);
            }
            stack[effect] = int;
        };
    }
    Si quelqu'un à une idée. Je suis preneur. Je sèche la dessus depuis quelque temps déjà

  2. #2
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Même en changeant la précision des couleurs à 32 bits, si tu pousses la luminosité à fond tu perdras les informations initiales. Le mieux est de garder en mémoire l'image de base utilisée et de lui réappliquer à chaque fois le traitement, plutôt que de repartir du résultat du traitement précédent.
    One Web to rule them all

  3. #3
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    J'y ai pensé. Mais l'image peut avoir plusieurs filtres d'appliqués. Cela va prendre une temps fou entre chaque modification si je doit ré-appliquer chaque filtre en même temps. Vu que mon curseur va de -100 à 100. Mais pixel ne peuvent varié qu'entre -355 et +355. Cela passe largement dans un Uint32 non?

    [EDIT] Je viens de me rendre compte que ce que je disait n'avais aucun sens . Si je résume mon problème viens du fait que le clampedArray ne contient que des valeur entre -255 et +255. Si je prend n'importe quel array qui n'a pas cette limitation , cela devrait fonctionner non?

  4. #4
    Rédacteur/Modérateur

    Avatar de SylvainPV
    Profil pro
    Inscrit en
    Novembre 2012
    Messages
    3 375
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3 375
    Points : 9 944
    Points
    9 944
    Par défaut
    Ce n'est pas la précision du nombre le problème. Mettons que tu pousses la luminosité à sa plus haute valeur possible, l'image sera donc entièrement blanche et tous les pixels auront leur composantes couleur à leur valeur maximale rgb(255,255,255). Dès lors, si tu repars de cette image, comment comptes-tu retrouver ton image ?

    La seule option est de réappliquer les traitements en gardant l'image d'origine en mémoire. Si tu gardes en mémoire des snapshots intermédiaires de l'image après chaque traitement, il n'y aura pas de différence de performance.
    One Web to rule them all

  5. #5
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    Mettons que j'ai deux pixels. Un avec un valeur de (155, 155, 155), et l'autre de (200, 200, 200). Si j'applique une luminosité de +100 sur ces 2 pixels, je aurais donc comme valeur 255,255,255 pour les 2 pixels. Du coups je perd les info du premier. On est d'accord.
    Maintenant avant application du filtre, je transforme mon Uint8ClampedArray en U8intArray. Je me retrouve donc avec des pixels ayant pour valeur : (255,255,255) et (300,300,300). Au moment de dessiner je repasse en Uint8ClampedArray, (255,255,255) pour les deux pixels.
    Si je rechange la luminosité de -100. J'applique le changement sur mon U8intArray, j'ai donc de nouveau un pixel (155, 155, 155), et l'autre à (200, 200, 200). Puis je remet en Uint8ClampedArray.

    Je ne sais pas si je suis assez clair?
    Ca c'est la théorie. Dans la pratique le passage de U8intArray -> Uint8ClampedArray, ne fonctionne pas et je ne sais pas pourquoi.

    [EDIT] : J'ai trouvé pourquoi cela ne marchait pas. Ceci ne marche pas:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    newImgData.data = clampedArray;
    Ceci fonctionne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    var clampedArray = Uint8toClampedArray(data);
                for(var i = 0; i< imageData.data.length; i+=4) {
                    imageData.data[i] = clampedArray[i];
                    imageData.data[i+1] = clampedArray[i+1];
                    imageData.data[i+2] = clampedArray[i+2];
                }
     ctx.putImageData(imageData,0 ,0);
    Après en voyant le résultat, l'image est toujours brulé. Je viens de comprendre pourquoi. Si je comprend bien. Admettons que j'ai un pixel avec ( 100, 25, 230). Si j'ajoute une luminosité de 100. je me retrouve avec un pixel en ( 200, 25, 255). Du coups les composant RGB sont déséquilibré et la couleur de mon pixel est changé. Bon je pense que je vais devoir mettre en mémoire chaque changement de l'image comme tu me l'a dis ^^

  6. #6
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Salut !
    Effectivement la valeur 300 ne peut être encodé sur un 1 seule octet non signé, que tu utilises un U8intArray ou un Uint8ClampedArray cela ne change rien !

    Donc comme te la indiqué SylvainPV tu dois garder ton image d’origine; Pour les performances temps réel, sache que calculer chaque pixel de manière itérative a et a toujours été une mauvaise idée. Tu dois utiliser le calcule parallèle en utilisant un micro-programme nommé Kernel ( ou pixel shader en WebGL)
    ShaderElement : Bénéficier de l’accélération graphique simplement par une nouvelle balise HTML <shader>
    ODE.js : portage JavaScript du célèbre moteur physique 3D Open Dynamics Engine

  7. #7
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    C'est possible ça avec le canvas 2d? tu as un peu de doc la dessus?

    Sinonje viens de penser a un truc avec la mise en mémoire des images. Si je modifie la luminosité. (+50), puis je la change de (-20). Cela va marcher. Je génère une image de +30 a partir de celle d'origine.
    Maintenant je fais la même chose mais avec un contraste et un black&White entre les deux. Je suis bien obligé de faire, lum (+30) plus contraste + black&white pour voir le résultat attendu. Donc 3 filtres à appliqué non?

  8. #8
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    oui c'est possible !

    tu peux t’entraîner sur ShaderToy , j'e viens de rapidement en créer un pour le filtre sepia avec une intensité variable ici
    ShaderElement : Bénéficier de l’accélération graphique simplement par une nouvelle balise HTML <shader>
    ODE.js : portage JavaScript du célèbre moteur physique 3D Open Dynamics Engine

  9. #9
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    Bon je vais regarder cela attentivement. Effectivement, j'aimerais bien avoir du real time . Et pour ma dernière question? Si j'ai plusieurs effet les uns à la suite des autres, Si je change le premier, je doit bien recalculer tout ceux appliqué après?

  10. #10
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Je n’ai pas trop capté l’idée de garder chaque image intermédiaire !

    De plus tes filtres ne sont pas supposés être commutatifs ?

    Dans mon shader je change l’espace des couleurs de RGB a YIQ

    Ainsi en écrasant I et Q a zero j’obtient Y (la luminance) et j’obtiens l’image en grayscale
    Si ensuite je fais varier I entre [0 … 1] j’obtiens le filtre sépia... donc pas besoin de différente passe
    ShaderElement : Bénéficier de l’accélération graphique simplement par une nouvelle balise HTML <shader>
    ODE.js : portage JavaScript du célèbre moteur physique 3D Open Dynamics Engine

  11. #11
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    Et si maintenant j'applique un effet de flou, suivie d'un négatif? il faut bien tout redessiner non? mes connaissance en webgl sont pour ainsi dire nulle. Donc je vais devoir partir de zéro, si mon navigateur veux bien fonctionner correctement. Je voit bien les rendus webgl sur le net mais si j'essaye de récupérer le context webgl , mon navigateur n'en veut pas. j'ai essayé IE11, Chromium 31 et firefox 31
    C'est le code venant direct de mozilla. J'ai une belle popup qui s'affice.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    var canvas;
      var gl;
     
      //
      // start
      //
      // Called when the canvas is created to get the ball rolling.
      // Figuratively, that is. There's nothing moving in this demo.
      //
        canvas = document.getElementById("glcanvas");
     
        initWebGL(canvas);      // Initialize the GL context
     
        // Only continue if WebGL is available and working
     
        if (gl) {
          gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Set clear color to black, fully opaque
          gl.clearDepth(1.0);                 // Clear everything
          gl.enable(gl.DEPTH_TEST);           // Enable depth testing
          gl.depthFunc(gl.LEQUAL);            // Near things obscure far things
        }
     
      //
      // initWebGL
      //
      // Initialize WebGL, returning the GL context or null if
      // WebGL isn't available or could not be initialized.
      //
      function initWebGL() {
        gl = null;
     
        try {
          gl = canvas.getContext("experimental-webgl");
        }
        catch(e) {
        }
     
        // If we don't have a GL context, give up now
     
        if (!gl) {
          alert("Unable to initialize WebGL. Your browser may not support it.");
        }
      }

  12. #12
    Membre confirmé

    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    311
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2010
    Messages : 311
    Points : 545
    Points
    545
    Par défaut
    Pas besoin d’apprendre WebGL il te faut juste comprendre le principe du pixel shader en GLSL !

    Effectivement si tes filtres ne sont pas commutatif tu doit effectuer tes opération les unes après les autres en partant de l’image d’origne ! Cela ne posera pas de problème de perf car ton shader parallelise tes calcules .

    Je n’ai pas trop comprit ShaderToy fonctionne chez toi mais pas la recup d’un context WebGL ?
    As-tu mit a jour tes drivers de ta carte graphique ? as-tu installer DirectX ?
    ShaderElement : Bénéficier de l’accélération graphique simplement par une nouvelle balise HTML <shader>
    ODE.js : portage JavaScript du célèbre moteur physique 3D Open Dynamics Engine

  13. #13
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    Dsl je fatigue je crois. J'ai oublier de remplacer le nom du canvas par le miens. Cela marche. Merci

  14. #14
    Membre éprouvé Avatar de scandinave
    Homme Profil pro
    Développeur Java, NodeJs/Angular
    Inscrit en
    Mai 2009
    Messages
    277
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Développeur Java, NodeJs/Angular

    Informations forums :
    Inscription : Mai 2009
    Messages : 277
    Points : 919
    Points
    919
    Par défaut
    j'ai trouvé ce qu'il me fallait. cela s'apelle glfx.js et permet l'application de pas mal de filtre commum sur un canvas grace a webgl

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Générer une image depuis canvas pour email avec outlook
    Par Tankian dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 22/12/2014, 21h13
  2. Changer la luminosité d'une image
    Par Speed41 dans le forum Débuter
    Réponses: 11
    Dernier message: 01/01/2014, 11h48
  3. Changer la taille d'une image chargée avec Loader
    Par DeezerD dans le forum ActionScript 3
    Réponses: 1
    Dernier message: 29/01/2008, 17h55
  4. changer la source d'une image avec Firefox
    Par couetbis dans le forum Général JavaScript
    Réponses: 11
    Dernier message: 07/11/2007, 22h45
  5. [Débutante] Création d'une image avec un composant
    Par gwendo dans le forum AWT/Swing
    Réponses: 9
    Dernier message: 09/07/2004, 09h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo