Informations
Le sujet que vous avez demandé est indiqué ci-dessous. Toutefois, ce sujet ne figure pas dans la bibliothèque.

Échantillon de couleurs d’une image avec Canevas

Créez votre propre pipette pour extraire les couleurs à partir d’un dessin ou d’une photographie en utilisant les méthodes HTML5 Canvas.

Introduction

L’échantillonnage de couleurs à partir d’une image peut ajouter de l’intérêt et de la flexibilité à des sites Web tels que des sites de mode ou de décoration intérieure. Les utilisateurs peuvent choisir les couleurs sur des images échantillons pour changer, par exemple, l’aspect d’un vêtement, d’une voiture ou d’une maison. Les méthodes getImageData et putImageData de l’API Canvas rendent l’échantillonnage de pixels ou de toute une photographie relativement facile. Plusieurs autres API Canvas sont utilisées, ainsi que la méthode drawImage pour placer la photo sur une zone de dessin, l’objet CanvasImageData et la propriété data.

Dans cette rubrique, vous découvrirez comment utiliser getImageData pour tester des couleurs sur une photo afin de créer un cadre aux couleurs coordonnées. Le cadre est créé à l’aide de la propriété border avec des feuilles de style en cascade (CSS, Cascading Style Sheets). Il ne fait donc pas partie intégrante de la zone de dessin ou de l’image. Toutefois, la syntaxe est affichée pour que vous puissiez la copier dans le code de votre propre page Web. Si vous souhaitez des références supplémentaires, l’échantillon de couleur est également affiché dans les valeurs individuelles Rouge, Vert, Bleu et Alpha (transparence), la valeur de couleur CSS, et la valeur TSV (Teinte, Saturation et Valeur).

Les données d’image obtenues à l’aide de la méthode getImageData sont également manipulées de manière à convertir une photo couleur en une photo noir et blanc. La méthode putImageData est utilisée pour replacer les données manipulées sur la zone de dessin.

Le code HTML de l’exemple définit les éléments Canvas et Input de manière à obtenir et sélectionner les URL des photos. Quatre images sont codées en dur dans la page Web, mais le champ de saisie vous permet de coller n’importe quelle autre image. Quand l’application a besoin d’une image à utiliser, elle extrait l’URL du champ de saisie quand la liste déroulante (l’élément select) change ou quand le bouton Charger est activé. L’exemple complet est disponible sur le site d’exemples de MSDN.

Image de l’interface utilisateur de l’outil d’encadrement

Placement de la photo sur une zone de dessin

La première étape dans l’échantillonnage des couleurs consiste à placer la photo sur un élément de zone de dessin. Cet élément par lui-même n’est pas visible sur la page. Pour que vous puissiez l’utiliser, vous devez lui ajouter du contenu, c’est-à-dire dans notre cas, une image.

Seul l’objet canvas possède quelques méthodes et propriétés. L’illustration et la manipulation d’images sur une zone de dessin s’effectuent en grande partie à l’aide de l’objet CanvasRenderingContext2D. L’objet contextuel est retourné par la méthode getContext de l’objet canvas avec l’instruction var ctx = canvas.getContext("2d");. Pour le moment, seul un contexte 2D est pris en charge dans Windows Internet Explorer 9 et Internet Explorer 10, mais les contextes 3D tels que WebGL sont pris en charge dans certains autres navigateurs.

Contrairement à une balise img, l’élément de zone de dessin ne possède pas de propriété src pour attribuer une URL. Dans cet exemple, un élément img est créé et l’image lui est attribuée. Cette image est ensuite transférée vers la zone de dessin à l’aide de la propriété drawImage sur l’objet contextuel.

L’exemple suivant crée des variables globales pour l’élément de la zone de dessin ("canvas") et l’objet Context ("ctx"). Un objet image ("image") contenant les photos à mesure qu’elles sont chargées est créé.


var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var image = document.createElement("img");


Dans l’exemple, la fonction getImage() attribue à la propriété src de l’objet image la valeur du champ de saisie. Le chargement de l’image prend un certain temps selon la taille du fichier et la page Web doit donc attendre la fin du chargement. Pour ce faire, le gestionnaire de l’événement onreadystatechange surveille l’image. Dans le gestionnaire, la propriété readyState permet d’effectuer le suivi de la valeur de l’état complete.


function getImage() {
    var imgSrc = document.getElementById("fileField").value;

    if (imgSrc == "") {
        imgSrc = rootPath + samplefiles[0];     // Get first example
        document.getElementById("fileField").value = imgSrc;
    }

    image.src = imgSrc;

  //image.complete
    image.addEventListener("load", function () {
        var dimension = 380; // Deep dimensions reasonable.
        var dw;
        var dh;

        // set max dimension
        if ((image.width > dimension) || (image.height > dimension)) {
          if (image.width > image.height) {
            // scale width to fit, adjust height
            dw = parseInt(image.width * (dimension / image.width));
            dh = parseInt(image.height * (dimension / image.width));
          } else {
            // scale height to fit, adjust width
            dh = parseInt(image.height * (dimension / image.height))
            dw = parseInt(image.width * (dimension / image.height));
          }
          canvas.width = dw;
          canvas.height = dh;
        }
        else {

          canvas.width = image.width;
          canvas.height = image.height;
        }

        ctx.drawImage(image, 0, 0, dw, dh);  // Put the photo on the canvas
        setBorder();  // Update color border          
    }, false);
}


Si la photo est trop grande pour tenir entièrement sur l’écran, elle est mise à l’échelle. Si la hauteur ou la largeur est supérieure à la taille de la zone de dessin créée, dans le cas présent 380 x 380 pixels, une valeur de mise à l’échelle est calculée. Les photos n’étant pas nécessairement carrées, seule la dimension la plus grande (largeur ou hauteur) est utilisée pour calculer le pourcentage de mise à l’échelle. Cette valeur permet de mettre à l’échelle l’autre dimension afin de conserver les proportions lors de la l’affichage dans la zone de dessin.

La photo est ensuite copiée dans la zone de dessin à l’aide de la méthode drawImage. La méthode drawImage prend un objet image ainsi que les coordonnées des pixels x et y qui définissent le coin supérieur gauche de l’image. Ces valeurs vous permettent de spécifier à quel endroit de l’image commencer l’affichage, par exemple pour afficher des parties d’une vignette pour une animation. Les paramètres de largeur et de hauteur sont également utilisés pour spécifier la taille de l’image à créer. Dans cet exemple, la largeur et la hauteur de l’image sont dimensionnées à un maximum de 380 pixels de façon à ce que l’image s’ajuste à la zone de dessin. La méthode drawImage prend également en charge quatre paramètres supplémentaires pouvant être utilisés pour placer l’image dans la zone de dessin. Pour plus d’informations, voir la page de référence sur drawImage.

Obtenir une valeur de pixel

Pour obtenir la valeur d’un pixel, la méthode getImageData est utilisée pour obtenir un objet imageData. L’objet imageData contient un objet pixelArray, qui contient lui-même les pixels réels de l’image figurant dans la zone de dessin. L’objet pixelArray est au format RVBa (rouge, vert, bleu, alpha). Pour connaître la valeur d’un pixel unique qui est la cible d’un clic de souris, vous devez calculer l’index de l’objet pixelArray en fonction des coordonnées x et y et de la largeur de la zone de dessin. La formule ((y * canvas.width) + x) * 4 permet d’obtenir l’offset du tableau. Pour obtenir chaque couleur, commencez par la première valeur au niveau de l’offset dans l’objet pixelArray, puis recherchez les trois valeurs suivantes. Vous obtiendrez ainsi la valeur RVBa du pixel comme illustré ici :


 canvas.onclick = function (evt) {
        //  get mouse coordinates from event parameter
        var mouseX = parseInt(evt.offsetX);
        var mouseY = parseInt(evt.offsetY);

        //  get imageData object from canvas
        var imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);

        //  get pixelArray from imagedata object
        var data = imagedata.data;  

        //  calculate offset into array for pixel at mouseX/mouseY
        var i = ((mouseY * canvas.width) + mouseX) * 4;

        //  get RGBA values
        var r = data[i];        
        var g = data[i+1];
        var b = data[i+2];
        var a = data[i+3];
   

Nous avons donc utilisé la méthode getImageData pour lire les pixels directement à partir d’une image de zone de dessin. La méthode getImageData contient une spécification de sécurité qui empêche une page Web de zone de dessin de copier des pixels d’un domaine d’ordinateur à un autre (entre domaines). Une image inter-domaines peut être transférée vers la zone de dessin qui utilise drawImage, mais si la méthode getImageData est utilisée pour copier les pixels vers l’objet pixelArray, elle déclenche une erreur de sécurité DOM exception (18).

Cet exemple contient une fonction appelée getSafeImageData() permettant d’intercepter des erreurs inter-domaines sans bloquer toute la page. La fonction getSafeImageData() contient une instruction try et une instruction catch pour intercepter les exceptions. Si aucune exception ne se produit, la fonction retourne l’objet pixelArray.

Si une erreur de sécurité se produit sur un élément de zone de dessin, elle attribue à l’indicateur origin-clean (un indicateur interne) la valeur false. Aucune autre image ne peut être chargée tant que l’indicateur n’a pas été effacé, ce qui peut être fait en actualisant la page ou en détruisant puis en recréant une nouvelle zone de dessin. Quand la fonction getSafeImageData() intercepte une exception, elle supprime l’élément de zone de dessin de la page, crée une nouvelle zone de dessin et attribue les mêmes attributs et événements à cette nouvelle zone. Une fois la nouvelle zone de dessin créée (avec la valeur true attribuée à l’indicateur clean-origin), le message d’erreur s’affiche sur la nouvelle zone de dessin. La fonction getSafeImageData() retourne une chaîne vide, utilisée pour signaler que la méthode getImageData a échoué. À partir de là, vous pouvez charger une autre image choisie parmi les échantillons, ou votre propre image dans le même domaine.

Afin de conserver la compatibilité du code entre Windows 7 et Windows 8, la méthode parseInt() JavaScript est utilisée pour transformer les coordonnées de la souris en entiers. Dans Windows 8, les coordonnées de la souris sont retournées sous forme de valeurs à virgule flottante afin de fournir des informations de sous-pixel pour les fonctions CSS et celles d’autres interfaces utilisateur. Dans cet exemple, parseInt() est appliqué avant le calcul afin d’éviter les erreurs d’arrondi qui auraient pour effet de retourner des valeurs incorrectes à partir de l’objet pixelArray. Par ailleurs, si vous utilisez un nombre à virgule flottante en tant qu’index dans l’objet pixelArray, il ne retourne rien, car les index de tableau sont uniquement des valeurs d’entier.

La valeur RVBa du pixel retourné par l’objet pixelArray est convertie en une valeur hexadécimale et utilisée comme couleur d’arrière-plan pour la bordure CSS. Seules les valeurs RVB sont utilisées, et l’exemple suivant montre comment convertir trois valeurs en une seule couleur CSS.


function getHex(data, i) {
    //  Builds a CSS color string from the RGB value (ignore alpha)
    return ("#" + d2Hex(data[i]) + d2Hex(data[i + 1]) + d2Hex(data[i + 2]));
}

function d2Hex(d) {
    // Converts a decimal number to a two digit Hex value
    var hex = Number(d).toString(16);
    while (hex.length < 2) {
        hex = "0" + hex;
    }
    return hex.toUpperCase();
}


Dans cet exemple, la valeur pixelArray qui a été calculée d’après les coordonnées x/y de la souris est transmise à la fonction getHex. La fonction getHex appelle la fonction d2Hex() qui convertit les valeurs RVB décimales en valeurs hexadécimales à deux chiffres. Chaque valeur RVB convertie est concaténée en une chaîne et un signe dièse (#) est ajouté au début de la chaîne pour créer le format de la valeur de couleur CSS. Cette valeur est ensuite utilisée pour définir la couleur du style de la bordure, ainsi que quelques autres propriétés. Par exemple, voici une bordure de couleur bleu-vert avec des coins arrondis et une apparence 3D : style= 'border-color:#4E8087; border-width:30px; border-style:groove; border-radius:20px' .

Éléments CSS

Le cadre autour de la zone de dessin est créé à l’aide de la propriété de bordure. La largeur par défaut est de 30 pixels, avec un style uni, sans rayon de bordure. À mesure que les valeurs changent, la syntaxe s’affiche en dessous de l’image. La couleur de la bordure est définie quand vous cliquez sur l’image. Les valeurs du style et du rayon (coins arrondis) proviennent des menus déroulants.

Cet exemple offre cinq styles de bordure. Outre le style « solid », vous pouvez choisir des propriétés de styles de bordures 3D : « outset », « inset », « groove » et « ridge ». Les styles « outset » et « inset » sont des styles de cadre plat avec des effets d’éclairage. Les styles « grove » et « ridge » sont des styles de cadre 3D avec des effets d’éclairage. Les effets d’éclairage donnent l’apparence d’un rayon lumineux provenant du coin supérieur gauche ou du coin inférieur droit.


function setDispColors(bColor, fColor) {
    //  Set the color of anything with the class colorDisp. 
    var oDisplay = document.getElementsByClassName("colorDisp");
    //  Set the colors for each element
    for (i = 0; i < oDisplay.length; i++) {
        oDisplay[i].style.backgroundColor = bColor;
        oDisplay[i].style.color = fColor;
    }
}


Le menu déroulant des styles de rayon de bordure comporte quatre valeurs : 5, 10, 20 et 30 pixels. Ces nombres sont arbitraires et peuvent être définis sur n’importe quelle autre valeur que vous jugez appropriée. La ligne de syntaxe affichée au-dessous de l’image est mise à jour à mesure que les valeurs de style CSS sont mises à jour.

Maintien de la lisibilité des données affichées

Cet exemple utilise les valeurs de l’échantillon de couleur comme couleur d’arrière-plan des champs d’affichage de la valeur de couleur (RVB, CSS et TSV). La valeur class="colorDisp est attribuée à tous les champs d’affichage et à la propriété de bordure de l’image. Cela permet à l’exemple de définir rapidement la couleur d’arrière-plan sur tous les éléments à la fois. Les affichages sont colorés à l’aide de la fonction setDispColors(), qui transmet les couleurs de l’arrière-plan et de la police.


function setDispColors(bColor, fColor) {
    //  Set the color of anything with the class colorDisp. 
    var oDisplay = document.getElementsByClassName("colorDisp");
    //  Set the colors for each element
    for (i = 0; i < oDisplay.length; i++) {
        oDisplay[i].style.backgroundColor = bColor;
        oDisplay[i].style.color = fColor;
    }
}


Étant donné que les couleurs d’arrière-plan peuvent aller d’un beige clair à un noir très sombre, la couleur de la police affichée sur l’arrière-plan est importante. Afin de s’assurer de la lisibilité des valeurs, la couleur de la police est définie sur blanc ou sur noir, selon que les couleurs sont claires ou sombres. Pour savoir quand afficher l’une ou l’autre couleur, l’échantillon de couleur est converti en nuances de gris, et si la valeur est supérieure à un point défini (plus claire), le texte s’affiche en noir. Sinon, il s’affiche en blanc. Le seuil de l’exemple est défini sur 128, ou entre 0 et 255, la plage des couleurs RVB. L’exemple suivant illustre la fonction calcGray() qui détermine la couleur de la police.


function calcGray(data, i) {
    // Calculates the gray scale value of the chosen color and 
    // assigns a contrasting text color.
    var gray = ((data[i] + data[i + 1] + data[i + 2]) / 3); // Average RGB values       
    return ((gray > 128) ? "black" : "white");    // Assign black or white to return           
}


Cette technique fonctionne avec la plupart des couleurs, mais vous pouvez tester le seuil de façon à trouver le bon ajustement pour votre page.

Convertir une photo couleur en photo noir et blanc

La technique que nous venons d’utiliser pour obtenir les couleurs à partir d’un seul pixel peut être utilisée pour manipuler tous les pixels. Dans le prochain exemple, tous les pixels de l’objet pixelArray sont échantillonnés et modifiés pour convertir une photo couleur en noir et blanc. La moyenne des valeurs Rouge, Vert et Bleu de chaque pixel est établie de manière à n’obtenir qu’un seul nombre. La valeur d’origine de chaque canal de couleur du pixel est remplacée par la valeur moyenne. Comme chaque canal a la même valeur, le résultat est une image en nuances de gris composée de valeurs qui vont du noir au blanc.

La fonction "makeBW()" de cet exemple commence par obtenir l’objet pixelArray de la zone de dessin. Deux boucles for parcourent l’objet pixelArray pour obtenir la valeur de chaque pixel. Les valeurs RVB sont regroupées, puis la somme est divisée par trois pour obtenir la moyenne. La valeur Alpha (ou la transparence) est ignorée. La valeur obtenue est ensuite recopiée dans chaque valeur de couleur (RVB) du pixel composant le tableau. Quand tous les pixels ont été convertis, la méthode putImageData est utilisée pour replacer pixelArray dans la zone de dessin.


function makeBW() {
    //  Converts image to B&W by averaging the RGB channels, and setting each to that value
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height); // refresh canvas
    imgData = getSafeImageData(0, 0, canvas.width, canvas.height);
    if (imgData != "") {
        for (y = 0; y < imgData.height; y++) {
            for (x = 0; x < imgData.width; x++) {
                var i = ((y * 4) * imgData.width) + (x * 4);
                var aveColor = parseInt((imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2]) / 3)
                imgData.data[i] = aveColor;
                imgData.data[i + 1] = aveColor;
                imgData.data[i + 2] = aveColor;
            }
        }
        ctx.putImageData(imgData, 0, 0);
    }
}


Le code de cet exemple se déclenche quand vous cliquez sur le bouton Nuances de gris. Cliquez sur Charger pour recharger l’image couleur.

Ajout d’une teinte sépia ou cyanotype

La transformation d’une photo couleur en photo noir et blanc est un bon moyen de lui donner un petit air nostalgique. Pour remonter encore plus le temps, vous pouvez utiliser un ton sépia ou cyanotype. Ces processus sont utilisés depuis plus d’une centaine d’années pour créer des effets et pour améliorer leurs propriétés de conservation. Les processus initiaux utilisaient des produits chimiques abrasifs pour remplacer l’argent du papier photographique par d’autres composants produisant des effets de couleur. Avec les images numériques, le processus est similaire mais nettement moins salissant.

La conversion d’une photo en sépia ou cyanotype nécessite surtout la réalisation de plusieurs étapes. Vous devez effectuer les opérations suivantes sur chaque pixel :

  1. Extraire un pixel de l’objet pixelArray.
  2. Convertir le pixel en noir et blanc.
  3. Convertir le pixel en nuances de gris (RVB) en modèle de couleur TSV.
  4. Ajouter (ou retirer) les paramètres Hue, Sat et Val à la valeur TSV du pixel pour créer la teinte.
  5. Reconvertir le pixel teinté en modèle de couleur RVB.
  6. Replacer le pixel dans pixelArray.

L’exemple suivant montre les fonctions "rgb2hsv()" et "hsv2rgb()", ainsi que la fonction "makeTint()" qui ajoute les valeurs TSV aux pixels. Les teintes sont ajoutées sous forme de valeurs TSV, car cette action est plus facile à réaliser avec une seule valeur. La teinte est la couleur basée sur une échelle de 360 degrés, souvent affichée dans les logiciels sous la forme d’une roue chromatique. La saturation et la valeur (parfois appelée éclairage ou luminosité) sont exprimées sous forme de pourcentages (0 à 100 %). La conversion d’une photo en ton sépia s’effectue en ajoutant "Hue = 30" et "Sat = 30" aux valeurs de nuances de gris. Rien n’est ajouté au paramètre Val pour le ton sépia. Pour créer une photo cyanotype, la formule consiste à ajouter "Hue = 220" et "Sat = 40". Le paramètre Val est défini de façon à augmenter de 10 % pour éclairer très légèrement la photo, la teinte bleutée pouvant apparaître plus foncée que vous ne le souhaitez. Cette valeur est arbitraire et peut être différente selon les photos. Pour plus d’informations sur ces modèles, voir l’article relatif à HSL et HSV dans Wikipédia.


function rgb2hsv(r, g, b) {
    //  Converts RGB value to HSV value
    var Hue = 0;
    var Sat = 0;
    var Val = 0;

    //  Convert to a percentage
    r = r / 255; g = g / 255; b = b / 255;
    var minRGB = Math.min(r, g, b);
    var maxRGB = Math.max(r, g, b);

    // Check for a grayscale image
    if (minRGB == maxRGB) {
        Val = parseInt((minRGB * 100) + .5); // Round up
        return [Hue, Sat, Val];  
    }
    var d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r);
    var h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5);
    Hue = parseInt(60 * (h - d / (maxRGB - minRGB)));
    Sat = parseInt((((maxRGB - minRGB) / maxRGB) * 100) + .5);
    Val = parseInt((maxRGB * 100) + .5); // Round up
    return [Hue, Sat, Val];
}
function hsv2rgb(h, s, v) {
    // Set up rgb values to work with 
    var r;
    var g;
    var b;

    // Sat and value are expressed as 0 - 100%
    // convert them to 0 to 1 for calculations  
    s /= 100;
    v /= 100;

    if (s == 0) {
        v = Math.round(v * 255); // Convert to 0 to 255 and return 
        return [v, v, v]; //  Grayscale, just send back value
    }

    h /= 60;   // Divide by 60 to get 6 sectors (0 to 5)

    var i = Math.floor(h);  // Round down to nearest integer
    var f = h - i;
    var p = v * (1 - s);
    var q = v * (1 - s * f);
    var t = v * (1 - s * (1 - f));

    // Each sector gets a different mix
    switch (i) {
        case 0:
            r = v;
            g = t;
            b = p;
            break;
        case 1:
            r = q;
            g = v;
            b = p;
            break;
        case 2:
            r = p;
            g = v;
            b = t;
            break;
        case 3:
            r = p;
            g = q;
            b = v;
            break;
        case 4:
            r = t;
            g = p;
            b = v;
            break;
        default:
            r = v;
            g = p;
            b = q;
            break;
    }
    //  Convert all decimial values back to 0 - 255
    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
// Accept and add a Hue, Saturation, or Value for tinting. 
function makeTint(h, s, v) {
    //  Converts color to b&w, then adds tint
    var imgData = getSafeImageData(0, 0, canvas.width, canvas.height);

    if (imgData != "") {
        for (y = 0; y < imgData.height; y++) {
            for (x = 0; x < imgData.width; x++) {
                var i = ((y * imgData.width) + x) * 4;  // our calculation
                //  Get average value to convert each pixel to black and white
                var aveColor = parseInt((imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2]) / 3)
                //  Get the HSV value of the pixel
                var hsv = rgb2hsv(aveColor, aveColor, aveColor);
                //  Add incoming HSV values (tones)
                var tint = hsv2rgb(hsv[0] + h, hsv[1] + s, hsv[2] + v);
                // Put updated data back
                imgData.data[i] = tint[0];
                imgData.data[i + 1] = tint[1];
                imgData.data[i + 2] = tint[2];
            }
        }
        // Refresh the canvas with updated colors
        ctx.putImageData(imgData, 0, 0);
    }
}

function sepia() {
    // Refresh the canvas from the img element
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    makeTint(30, 30, 0);

}
function cyanotype() {
    // Refresh the canvas from the img element
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    makeTint(220, 40, 10);
}


Récapitulatif

Vous pouvez réaliser de nombreuses autres choses en utilisant la valeur des pixels d’une zone de dessin. Par exemple, vous pouvez tester des échantillons de pixels pour une valeur de couleur spécifique et superposer des images pour obtenir un effet de clé chromatique (écran vert). Cet effet est utilisé à la télévision et au cinéma pour créer, dans le confort d’un studio, des images montrant un présentateur météo devant un violent orage ou un acteur au sommet d’une montagne.

Rubriques connexes

Élément de zone de dessin
Propriétés Canvas
Site des exemples MSDN
Galeries de photos Contoso Images
Article Wikipédia relatif à HSL et HSV
Méthodes
Utiliser la puissance de HTML 5 Canvas pour les jeux

 

 

Afficher:
© 2014 Microsoft