Erhalten Sie die durchschnittliche Farbe des Bildes über Javascript

117

Ich bin mir nicht sicher, ob dies möglich ist, aber ich möchte ein Skript schreiben, das den Durchschnitt hexoder rgbWert für ein Bild zurückgibt. Ich weiß, dass es in AS gemacht werden kann, aber ich möchte es in JavaScript machen.

bgreater
quelle
19
Mein Freund Boaz war schon einmal auf diesem Weg (hoffentlich mischt er sich ein), aber der Durchschnitt hinterlässt normalerweise eine erbrochene Farbe. Was Sie wirklich wollen, ist "dominante Farbe". Es gibt einige Möglichkeiten, dies zu erreichen (Farbtonhistogramm mit dynamischem Bucketing usw.), aber ich denke, das ist wahrscheinlich genauer für Ihr beabsichtigtes Ergebnis.
Paul Irish

Antworten:

149

AFAIK, der einzige Weg dies zu tun ist mit <canvas/>...

DEMO V2 : http://jsfiddle.net/xLF38/818/

Beachten Sie, dass dies nur mit Bildern in derselben Domäne und in Browsern funktioniert, die HTML5-Canvas unterstützen:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
        defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
        canvas = document.createElement('canvas'),
        context = canvas.getContext && canvas.getContext('2d'),
        data, width, height,
        i = -4,
        length,
        rgb = {r:0,g:0,b:0},
        count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return defaultRGB;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

Schauen Sie sich für IE excanvas an .

James
quelle
1
Gibt es eine Möglichkeit, das Farbhistogramm für ein Bild zu erhalten, das auf einer anderen Domäne liegt?
Dan Loewenherz
1
Dan: Ich denke, Sie stoßen auf die Ursprungsbeschränkung, wenn Sie versuchen, auf Bilddaten eines Bildes aus einer anderen Domäne zuzugreifen. Welches ist ein Mist.
8
Ich denke, es gibt eine schlechte Platzierung für den blauen und grünen Wert, Sie schreiben 'rgb('+rgb.r+','+rgb.b+','+rgb.g+')'und das sollte sein 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'. Es ist seltsam, wenn die dominierende Farbe blau ist, aber das Ergebnis ist grün :).
Ariona Rian
7
Es wurde eine großartige Problemumgehung für Ursprungsbeschränkungen gefunden: Fügen Sie diese hinzu, img.crossOrigin = '';bevor Sie das srcAttribut festlegen. Gefunden auf: coderwall.com/p/pa-2uw
mhu
Keine Notwendigkeit zu zählen, da count === length / 4 / blockSize;)
yckart
84

Ich dachte, ich würde ein Projekt veröffentlichen, auf das ich kürzlich gestoßen bin, um eine dominante Farbe zu erhalten:

Farbdieb

Ein Skript zum Abrufen der dominanten Farbe oder einer repräsentativen Farbpalette aus einem Bild. Verwendet Javascript und Canvas.

Die anderen Lösungen, die dominante Farben erwähnen und vorschlagen, beantworten die Frage nie wirklich im richtigen Kontext ("in Javascript"). Hoffentlich hilft dieses Projekt denen, die genau das wollen.

J. Chase
quelle
55

"Dominante Farbe" ist schwierig. Sie möchten den Abstand zwischen jedem Pixel und jedem anderen Pixel im Farbraum (euklidischer Abstand) vergleichen und dann das Pixel finden, dessen Farbe jeder anderen Farbe am nächsten kommt. Dieses Pixel ist die dominierende Farbe. Die durchschnittliche Farbe ist normalerweise Schlamm.

Ich wünschte, ich hätte MathML hier, um Ihnen die euklidische Entfernung zu zeigen. Google es.

Ich habe die obige Ausführung im RGB-Farbraum mit PHP / GD hier durchgeführt: https://gist.github.com/cf23f8bddb307ad4abd8

Dies ist jedoch sehr rechenintensiv. Es wird Ihr System bei großen Bildern zum Absturz bringen und Ihren Browser definitiv zum Absturz bringen, wenn Sie es im Client versuchen. Ich habe daran gearbeitet, meine Ausführung zu überarbeiten, um: - Ergebnisse in einer Nachschlagetabelle für die zukünftige Verwendung in der Iteration über jedes Pixel zu speichern. - Aufteilen großer Bilder in Raster von 20 x 20 Pixel für eine lokalisierte Dominanz. - den euklidischen Abstand zwischen x1y1 und x1y2 verwenden, um den Abstand zwischen x1y1 und x1y3 zu ermitteln.

Bitte lassen Sie mich wissen, wenn Sie in dieser Hinsicht Fortschritte machen. Ich würde mich freuen, es zu sehen. Ich werde dasselbe tun.

Canvas ist definitiv der beste Weg, dies im Client zu tun. SVG ist nicht, SVG ist vektorbasiert. Nachdem ich die Ausführung beendet habe, möchte ich sie als Nächstes auf der Leinwand zum Laufen bringen (möglicherweise mit einem Webworker für die Berechnung der Gesamtentfernung jedes Pixels).

Eine andere Sache, über die man nachdenken sollte, ist, dass RGB kein guter Farbraum dafür ist, da der euklidische Abstand zwischen Farben im RGB-Raum nicht sehr nahe am visuellen Abstand liegt. Ein besserer Farbraum dafür könnte LUV sein, aber ich habe keine gute Bibliothek dafür gefunden oder irgendwelche Algorithmen für die Konvertierung von RGB in LUV.

Ein völlig anderer Ansatz wäre, Ihre Farben in einem Regenbogen zu sortieren und ein Histogramm mit Toleranz zu erstellen, um unterschiedliche Farbtöne zu berücksichtigen. Ich habe dies nicht versucht, da das Sortieren von Farben in einem Regenbogen schwierig ist, ebenso wie Farbhistogramme. Ich könnte das als nächstes versuchen. Lassen Sie mich noch einmal wissen, ob Sie hier Fortschritte machen.


quelle
6
Dies ist eine dieser Antworten, für die ich mir wünschte, ich könnte mehr als nur +1 tun :-)
David Johnstone
3
Hervorragende Antwort, obwohl keine klare Lösung gefunden wurde. Dies wird mir helfen, meinen nächsten Schritt zu entscheiden ...
bgreater
Dies ist eine großartige Antwort. Ich habe ein paar Knotenmodule geschrieben, um die euklidische Entfernungsberechnung nicht in LUV, sondern im L a b * -Farbraum durchzuführen. Siehe github.com/zeke/color-namer und github.com/zeke/euclidean-distance
Zeke
1
@user: Ich habe mich nur gefragt: Wäre eine Stichprobe von beispielsweise 1% bei großen Bildern nicht ausreichend, um die Berechnung zu beschleunigen?
Jackthehipster
Klingt nach dem geometrischen Median. Vielleicht kann das helfen? stackoverflow.com/questions/12934213/… und wenn der gesuchte Punkt im Originalsatz vorhanden sein muss : en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
Lawrence Weru
16

Erstens: Dies kann ohne HTML5 Canvas oder SVG erfolgen.
Eigentlich jemand einfach zu verwaltender Client-seitige PNG - Dateien mit Hilfe von JavaScript zu generieren , ohne Leinwand oder SVG, die unter Verwendung von Daten - URI - Schema .

Zweitens: Möglicherweise benötigen Sie Canvas, SVG oder eines der oben genannten Elemente überhaupt nicht.
Wenn Sie nur benötigen Prozessbilder auf der Client - Seite, ohne sie zu modifizieren, all dies ist nicht erforderlich.

Sie können die Quelladresse aus dem img-Tag auf der Seite abrufen , eine XHR- Anfrage dafür stellen - sie wird höchstwahrscheinlich aus dem Browser-Cache stammen - und sie als Byte-Stream aus Javascript verarbeiten.
Sie benötigen ein gutes Verständnis des Bildformats. (Der obige Generator basiert teilweise auf libpng-Quellen und bietet möglicherweise einen guten Ausgangspunkt.)

Andras Vass
quelle
+1 für die Bytestream-Lösung. Dies ist eine gute Option, wenn die einzige Option darin besteht, sie in der Clientanwendung zu erstellen.
Loretoparisi
11

Ich würde über das HTML-Canvas-Tag sagen.

Sie können finden hier einen Beitrag von @Georg sprechen über einen kleinen Code von der Opera dev:

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

Dies invertiert das Bild unter Verwendung des R-, G- und B-Werts jedes Pixels. Sie können die RGB-Werte problemlos speichern, dann die roten, grünen und blauen Arrays aufrunden und sie schließlich wieder in einen HEX-Code konvertieren.

rnaud
quelle
Gibt es eine Chance, dass es eine IE-Alternative zur Canvas-Lösung gibt?
Bgreater
@ Ben4Himv: Es gibt die IE9 Platform Preview ;-) aber im Ernst, ich fürchte nicht.
Andy E
4

Javascript hat keinen Zugriff auf die einzelnen Pixelfarbdaten eines Bildes. Zumindest nicht bis html5 ... an diesem Punkt liegt es nahe, dass Sie ein Bild auf eine Leinwand zeichnen und dann die Leinwand untersuchen können (vielleicht habe ich es selbst nie gemacht).

Joel Martinez
quelle
2

All-in-One-Lösung

Ich persönlich würde Color Thief zusammen mit dieser modifizierten Version von Name that Color kombinieren , um eine mehr als ausreichende Auswahl an dominanten Farbergebnissen für Bilder zu erhalten.

Beispiel:

Betrachten Sie das folgende Bild:

Geben Sie hier die Bildbeschreibung ein

Mit dem folgenden Code können Sie Bilddaten extrahieren, die sich auf die dominante Farbe beziehen:

let color_thief = new ColorThief();
let sample_image = new Image();

sample_image.onload = () => {
  let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
    const hex = x.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
    
  }).join(''));
  
  console.log(result[0]); // #f0c420     : Dominant HEX/RGB value of closest match
  console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
  console.log(result[2]); // #ffff00     : Dominant HEX/RGB value of shade of closest match
  console.log(result[3]); // Yellow      : Dominant color name of shade of closest match
  console.log(result[4]); // false       : True if exact color match
};

sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;
Grant Miller
quelle
1

Weniger genau, aber der schnellste Weg, um mit datauriUnterstützung die durchschnittliche Farbe des Bildes zu erhalten :

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}
350D
quelle
1

Wie in anderen Antworten ausgeführt, ist häufig die dominierende Farbe das, was Sie wirklich wollen, im Gegensatz zu der durchschnittlichen Farbe, die dazu neigt, braun zu sein. Ich habe ein Skript geschrieben, das die häufigste Farbe erhält, und es in diesem Kern veröffentlicht

dctalbot
quelle
-2

Es gibt ein Online-Tool pickimagecolor.com , mit dem Sie die durchschnittliche oder dominante Farbe des Bildes ermitteln können. Sie müssen lediglich ein Bild von Ihrem Computer hochladen und dann auf das Bild klicken. Es gibt die durchschnittliche Farbe in HEX, RGB und HSV. Es werden auch die Farbtöne gefunden, die zu dieser Farbe passen, aus der Sie auswählen können. Ich habe es mehrfach benutzt.

Shubham Agrawal
quelle