getPixel depuis HTML Canvas?

111

Est-il possible d'interroger un objet HTML Canvas pour obtenir la couleur à un emplacement spécifique?

Liam
la source

Réponses:

171

Il y a une section sur la manipulation des pixels dans la documentation du W3C.

Voici un exemple sur la façon d'inverser une image :

var context = document.getElementById('myCanvas').getContext('2d');

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = 255 - pix[i  ]; // red
    pix[i+1] = 255 - pix[i+1]; // green
    pix[i+2] = 255 - pix[i+2]; // blue
    // i+3 is alpha (the fourth element)
}

// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);
Georg Schölly
la source
La section de manipulation de pixels est maintenant disponible ici: w3.org/TR/2dcontext2/#pixel-manipulation
jtraulle
55

Avez-vous essayé la méthode getImageData?

var data = context.getImageData(x, y, 1, 1).data;
var rgb = [ data[0], data[1], data[2] ];
Théo.T
la source
2
cela ne devrait-il pas être context.getImageData () et non canvas.getImageData ()?
Crashalot
2
@Crashalot dépend de ce que contient la var "canvas", cela pourrait simplement être le contexte d'un canvas avec un nom de var merdique.
tbleckert
1
Wow, très élégant! J'ai pensé à rechercher le point dans tout le contexte, mais c'est beaucoup plus intelligent.
TheOne
5
C'est intelligent, mais si vous appelez beaucoup getPixel, il est beaucoup plus rapide de mettre en cache l'objet ImageData pour toute l'image (0,0, largeur, hauteur), puis de calculer l'index en utilisant idx = (y * width + x) * 4la réponse de Georg. Cependant, n'oubliez pas d'actualiser cet objet mis en cache chaque fois que l'image change.
noio
2
Quel est ce Color()constructeur? Cela ne semble exister nulle part
fregante
12

Oui bien sûr, à condition d'avoir son contexte. Comment obtenir le contexte du canevas?

var imgData = context.getImageData(0,0,canvas.width,canvas.height);
// { data: [r,g,b,a,r,g,b,a,r,g,..], ... }

function getPixel(imgData, index) {
  var i = index*4, d = imgData.data;
  return [d[i],d[i+1],d[i+2],d[i+3]] // returns array [R,G,B,A]
}

// AND/OR

function getPixelXY(imgData, x, y) {
  return getPixel(imgData, y*imgData.width+x);
}



PS: Si vous prévoyez de faire muter les données et de les redessiner sur le canevas, vous pouvez utilisersubarray

var
 a = getPixel(idt, 188411), // Array(4) [0, 251, 0, 255]
 b = idt.data.subarray(188411*4, 188411*4 + 4) // Uint8ClampedArray(4) [0, 251, 0, 255]

a[0] = 255 // does nothing
getPixel(idt, 188411), // Array(4) [0, 251, 0, 255]

b[0] = 255 // mutates the original imgData.data
getPixel(idt, 188411), // Array(4) [255, 251, 0, 255]

// or use it in the function
function getPixel(imgData, index) {
  var i = index*4, d = imgData.data;
  return imgData.data.subarray(index, index+4) // returns subarray [R,G,B,A]
}

Vous pouvez expérimenter cela sur http://qry.me/xyscope/ , le code pour cela est dans la source, il suffit de le copier / coller dans la console.

Qwerty
la source
2
Yay! Merci, cela fonctionne très bien et c'est plus rapide que d'appelercontext.getImageData(x, y, 1, 1);
adelriosantiago
C'est plus rapide en effet. Merci!
dmitru le
9
function GetPixel(context, x, y)
{
    var p = context.getImageData(x, y, 1, 1).data; 
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);  
    return hex;
}

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}
at77
la source
4
ctx devrait être défini comme un attribut dans GetPixel
Marcio
1
Belle déclaration de retour rgbToHex
Qwerty
6

Notez que getImageData renvoie un instantané. Les implications sont:

  • Les modifications ne prendront effet qu'après putImageData
  • Les appels getImageData et putImageData sont relativement lents
Don Park
la source
4
// Get pixel data 
var imageData = context.getImageData(x, y, width, height);
//color at (x,y) position
var color = [];
color['red'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 0];
color['green'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 1];
color['blue'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 2];
color['alpha'] = imageData.data[((y*(imageData.width*4)) + (x*4)) + 3];
Pour toujours
la source
0

Vous pouvez utiliser i << 2.

const data = context.getImageData(x, y, width, height).data;
const pixels = [];

for (let i = 0, dx = 0; dx < data.length; i++, dx = i << 2) {
    pixels.push({
        r: data[dx  ],
        g: data[dx+1],
        b: data[dx+2],
        a: data[dx+3]
    });
}
A-312
la source
0

Oneliner à long pixel de lecture pratique (dessinez le pixel ici )

let rp=((s='.myCanvas',c=document.querySelector(s),ctx=c.getContext('2d')) => (x,y)=>Object.values(ctx.getImageData(x, y, 1, 1).data))();

rp(10,20) // rp(x,y) returns: [r,g,b,a] with values 0-255

La première ligne est la partie initiale où vous pouvez changer le sélecteur de canevas s='.myCanvas'. Cet oneliner pratique est bon pour tester des algorithmes ou faire une preuve de concept, mais pour le code de production, il est préférable d'utiliser un autre code plus clair et lisible.

Kamil Kiełczewski
la source
0

Si vous souhaitez extraire une couleur particulière de pixel en passant les coordonnées du pixel dans la fonction, cela vous sera utile

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function detectColor(x,y){
  data=ctx.getImageData(x,y,1,1).data;
  col={
    r:data[0],
    g:data[1],
    b:data[2]
  };
  return col;
}

x, yest la coordonnée dont vous souhaitez filtrer la couleur.

var color=detectColor(x,y)

La couleur est l'objet, vous obtiendrez la valeur rgb par color.r, color.g, color.b.

MD Rijwan
la source