Wie erhalte ich die x, y-Koordinatenfarbe eines Pixels aus einem Bild?

124

Gibt es eine Möglichkeit zu überprüfen, ob ein ausgewählter (x, y) Punkt eines PNG-Bildes transparent ist?

Danny Fox
quelle
1
Kann das Bild irgendwie auf ein Canvas-Element geladen werden?
Wenn es keinen anderen Weg gibt, dann
Danny Fox

Antworten:

198

Aufbauend auf Jeffs Antwort besteht Ihr erster Schritt darin, eine Leinwanddarstellung Ihres PNG zu erstellen. Im Folgenden wird eine Leinwand außerhalb des Bildschirms erstellt, die dieselbe Breite und Höhe wie Ihr Bild hat und auf der das Bild gezeichnet ist.

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);

Wenn ein Benutzer danach klickt, verwenden Sie event.offsetXund event.offsetY, um die Position abzurufen. Dies kann dann verwendet werden, um das Pixel zu erfassen:

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

Da Sie nur ein Pixel erfassen, ist pixelData ein Array mit vier Einträgen, das die Werte R, G, B und A des Pixels enthält. Für Alpha bedeutet weniger als 255 ein gewisses Maß an Transparenz, wobei 0 vollständig transparent ist.

Hier ist ein jsFiddle-Beispiel: http://jsfiddle.net/thirtydot/9SEMf/869/ Ich habe jQuery aus Bequemlichkeitsgründen verwendet, aber es ist keineswegs erforderlich.

Hinweis: getImageData Fällt unter die Richtlinie des Browsers mit demselben Ursprung, um Datenlecks zu vermeiden. Dies bedeutet, dass diese Technik fehlschlägt, wenn Sie die Zeichenfläche mit einem Bild aus einer anderen Domäne oder (ich glaube, aber einige Browser haben dies möglicherweise gelöst) SVG aus einer beliebigen Domäne verschmutzen. Dies schützt vor Fällen, in denen eine Site ein benutzerdefiniertes Image-Asset für einen angemeldeten Benutzer bereitstellt und ein Angreifer das Image lesen möchte, um Informationen zu erhalten. Sie können das Problem lösen, indem Sie entweder das Image vom selben Server bereitstellen oder die gemeinsame Nutzung von Ressourcen zwischen verschiedenen Ursprüngen implementieren .

Brian Nickel
quelle
3
Boom - tolle Arbeit. Ich wusste, ich hätte so eine Geige machen sollen, war aber zu fett und faul. Sie haben es hier getötet
Jeff Escalante
Damit dies korrekt ist, müssen Sie den Koordinatenraum der Leinwand festlegen, dh die Breite und Höhe so einstellen, dass sie den Bildabmessungen entsprechen. CSS-Breite und -Höhe dienen nur dazu, die endgültige Leinwand für das Layout zu dehnen, was nicht hilft, wenn es sich um ein nicht angezeigtes Element handelt. Versuchen Sie etwas wie $ ('<canvas width =' + img.width + 'height =' + img.height + '/>');. Oder trifft dies bei Off-Screen-Leinwänden irgendwie nicht zu?
Fabspro
@abspro: Das ist ein ausgezeichneter Punkt. Da das Zeichnen und Lesen der Bilddaten über den Kontext erfolgt, sind die Werte für Breite und Höhe bedeutungslos. Sie haben keine negativen Auswirkungen, da die Leinwand und nicht der Kontext verzerrt sind und der Grund, warum ich diesen Effekt in meiner Demo nicht gesehen habe, einfach darin besteht, dass das Bild kleiner als die anfängliche Breite / Höhe des Kontexts ist. Ich werde entsprechend aktualisieren, obwohl der Zugriff auf .widthund .heightauf der Leinwand selbst sicherer ist als durch Verkettung.
Brian Nickel
6
Sie können dies nicht für Remote-Images verwenden. "Bilddaten können nicht von der Zeichenfläche abgerufen werden, da die Zeichenfläche durch Ursprungsdaten beschädigt wurde. SecurityError: Es wurde versucht, die Sicherheitsrichtlinie des Benutzeragenten zu durchbrechen."
Karl Glaser
18

Canvas wäre eine großartige Möglichkeit, dies zu tun, wie @pst oben sagte. Schauen Sie sich diese Antwort für ein gutes Beispiel an:

getPixel von HTML Canvas?

Ein Code, der Ihnen auch speziell dienen würde:

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]
}

Dies wird zeilenweise erfolgen, sodass Sie dies in ein x, y konvertieren und entweder die for-Schleife in eine direkte Prüfung konvertieren oder eine bedingte Innenschleife ausführen müssen.

Wenn Sie Ihre Frage noch einmal lesen, möchten Sie in der Lage sein, den Punkt zu erreichen, auf den die Person klickt. Dies kann mit dem Klickereignis von jquery ziemlich einfach durchgeführt werden. Führen Sie einfach den obigen Code in einem Click-Handler als solchem ​​aus:

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

Diese sollten Ihre x- und y-Werte erfassen.

Jeff Escalante
quelle
Dies ist nicht das, was der Kommentator (scheinbar) gefragt hat und beantwortet die Frage überhaupt nicht. Die akzeptierte Antwort funktioniert. Ich empfehle diese Antwort zu löschen ...
Agamemnus
5

Die beiden vorherigen Antworten zeigen, wie Canvas und ImageData verwendet werden. Ich möchte eine Antwort mit einem ausführbaren Beispiel und unter Verwendung eines Bildverarbeitungsframeworks vorschlagen, damit Sie die Pixeldaten nicht manuell verarbeiten müssen.

MarvinJ stellt die Methode image.getAlphaComponent (x, y) bereit, die einfach den Transparenzwert für das Pixel in x, y-Koordinate zurückgibt. Wenn dieser Wert 0 ist, ist das Pixel vollständig transparent, Werte zwischen 1 und 254 sind Transparenzstufen, schließlich ist 255 undurchsichtig.

Zur Demonstration habe ich das Bild unten (300x300) mit transparentem Hintergrund und zwei Pixeln bei den Koordinaten (0,0) und (150,150) verwendet .

Geben Sie hier die Bildbeschreibung ein

Konsolenausgabe:

(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
quelle
2

Mit: 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
quelle