Comment obtenir la couleur des coordonnées x, y d'un pixel à partir d'une image?

124

Existe-t-il un moyen de vérifier si un point sélectionné (x, y) d'une image PNG est transparent?

Danny Fox
la source
1
L'image peut-elle être chargée d'une manière ou d'une autre sur un élément Canvas?
S'il n'y a pas d'autre moyen, alors
Danny Fox

Réponses:

199

En vous basant sur la réponse de Jeff, votre première étape serait de créer une représentation sur toile de votre PNG. Ce qui suit crée un canevas hors écran qui est de la même largeur et hauteur que votre image et a l'image dessinée dessus.

var img = document.getElementById('my-image');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);

Après cela, lorsqu'un utilisateur clique, utilisez event.offsetXet event.offsetYpour obtenir la position. Cela peut ensuite être utilisé pour acquérir le pixel:

var pixelData = canvas.getContext('2d').getImageData(event.offsetX, event.offsetY, 1, 1).data;

Étant donné que vous n'obtenez qu'un seul pixel, pixelData est un tableau à quatre entrées contenant les valeurs R, V, B et A du pixel. Pour alpha, tout élément inférieur à 255 représente un certain niveau de transparence, 0 étant totalement transparent.

Voici un exemple de jsFiddle: http://jsfiddle.net/thirtydot/9SEMf/869/ J'ai utilisé jQuery pour plus de commodité dans tout cela, mais ce n'est en aucun cas nécessaire.

Remarque: getImageData relève de la politique de même origine du navigateur pour empêcher les fuites de données, ce qui signifie que cette technique échouera si vous salissez le canevas avec une image d'un autre domaine ou (je crois, mais certains navigateurs peuvent avoir résolu ce problème) SVG de n'importe quel domaine. Cela protège contre les cas où un site diffuse une image personnalisée pour un utilisateur connecté et un attaquant veut lire l'image pour obtenir des informations. Vous pouvez résoudre le problème en diffusant l'image à partir du même serveur ou en implémentant le partage de ressources cross-origin .

Brian Nickel
la source
3
boom - travail génial. Je savais que j'aurais dû faire un violon comme celui-ci, mais j'étais trop gros et paresseux. vous l'avez tué ici
Jeff Escalante
Pour que cela soit correct, vous devez définir l'espace de coordonnées du canevas, c'est-à-dire définir la largeur et la hauteur pour correspondre aux dimensions de l'image. La largeur et la hauteur CSS ne servent qu'à étirer le canevas final pour la mise en page, ce qui n'aide pas lorsqu'il s'agit d'un élément non affiché. Essayez quelque chose comme $ ('<canvas width =' + img.width + 'height =' + img.height + '/>') ;. Ou cela ne s'applique-t-il pas aux toiles hors écran?
fabspro
@fabspro: C'est un excellent point. Puisque le dessin et la lecture des données d'image se font via le contexte, les valeurs de largeur et de hauteur n'ont aucun sens. Ils n'ont aucun effet néfaste car le canevas et non le contexte est déformé et la raison pour laquelle je n'ai pas vu cet effet sur ma démo est simplement parce que l'image est plus petite que la largeur / hauteur initiale du contexte. Je mettrai à jour en conséquence, bien qu'il soit plus sûr d'accéder .widthet .heightsur le canevas lui-même que par concaténation.
Brian Nickel
6
Vous ne pouvez pas l'utiliser pour les images distantes. "Impossible d'obtenir des données d'image à partir du canevas car le canevas a été contaminé par des données d'origine croisée. SecurityError: Une tentative a été faite pour briser la politique de sécurité de l'agent utilisateur."
Karl Glaser
18

Canvas serait un excellent moyen de le faire, comme @pst l'a dit ci-dessus. Découvrez cette réponse pour un bon exemple:

getPixel depuis HTML Canvas?

Un code qui vous serait également utile:

var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

for (var i = 0, n = pix.length; i < n; i += 4) {
  console.log pix[i+3]
}

Cela ira ligne par ligne, vous devez donc convertir cela en x, y et soit convertir la boucle for en une vérification directe, soit exécuter une condition à l'intérieur.

En relisant votre question, il semble que vous souhaitiez être en mesure d'obtenir le point sur lequel la personne clique. Cela peut être fait assez facilement avec l'événement click de jquery. Exécutez simplement le code ci-dessus dans un gestionnaire de clics en tant que tel:

$('el').click(function(e){
   console.log(e.clientX, e.clientY)
}

Ceux-ci devraient saisir vos valeurs x et y.

Jeff Escalante
la source
Ce n'est pas ce que le commentateur a demandé (apparemment) et ne répond pas du tout à la question. La réponse acceptée fonctionne. Je recommande de supprimer cette réponse ...
Agamemnus
5

Les deux réponses précédentes montrent comment utiliser Canvas et ImageData. Je voudrais proposer une réponse avec un exemple exécutable et en utilisant un cadre de traitement d'image, vous n'avez donc pas besoin de gérer les données de pixels manuellement.

MarvinJ fournit la méthode image.getAlphaComponent (x, y) qui renvoie simplement la valeur de transparence du pixel en coordonnées x, y. Si cette valeur est 0, le pixel est totalement transparent, les valeurs entre 1 et 254 sont des niveaux de transparence, enfin 255 est opaque.

Pour la démonstration, j'ai utilisé l'image ci-dessous (300x300) avec un fond transparent et deux pixels aux coordonnées (0,0) et (150,150) .

entrez la description de l'image ici

Sortie de la console:

(0,0): TRANSPARENT
(150,150): NOT_TRANSPARENT

image = new MarvinImage();
image.load("https://i.imgur.com/eLZVbQG.png", imageLoaded);

function imageLoaded(){
  console.log("(0,0): "+(image.getAlphaComponent(0,0) > 0 ? "NOT_TRANSPARENT" : "TRANSPARENT"));
  console.log("(150,150): "+(image.getAlphaComponent(150,150) > 0 ? "NOT_TRANSPARENT" : "TRANSPARENT"));
}
<script src="https://www.marvinj.org/releases/marvinj-0.7.js"></script>

Gabriel Ambrósio Archanjo
la source
1

Avec : 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) {
    if (data[dx+3] <= 8)
        console.log("transparent x= " + i);
}
A-312
la source