Generieren Sie die dominanten Farben für ein RGB-Bild mit XMLHttpRequest
Ein Hinweis für Leser: Dies ist eine lange Frage, aber es braucht einen Hintergrund, um die gestellte Frage zu verstehen.
Das Farbquantisierungstechnik wird häufig verwendet, um das @ zu erhaltdominante Farben eines Bildes. Eine der bekannten Bibliotheken für die Farbquantisierung ist Leptonica durch dasModified Median Cut Quantization (MMCQ) und Octree Quantization (OQ) Github's Farbdieb by @lokesh ist eine sehr einfache Implementierung des MMCQ-Algorithmus in JavaScript:
var colorThief = new ColorThief();
colorThief.getColor(sourceImage);
Technisch ist das Bild auf einem<img/>
HTML-Element wird auf einem @ gesiche<canvas/>
Element
var CanvasImage = function (image) {
this.canvas = docum,ent.createElement('canvas');
this.context = this.canvas.getContext('2d');
document.body.appendChild(this.canvas);
this.width = this.canvas.width = image.width;
this.height = this.canvas.height = image.height;
this.context.drawImage(image, 0, 0, this.width, this.height);
};
Und das ist das Problem mitTVML
, wie wir später sehen werden.
Eine andere Implementierung, die ich kürzlich kennengelernt habe, wurde mit diesem Artikel verlinktMit imagemagick, awk und kmeans dominante Farben in Bildern finden, das auf @ verweiVerwenden Sie Python, um fantastische Linux-Desktop-Designs zu erstellen. Der Autor hat einen Artikel über @ geposteMit Python und k-means die dominanten Farben in Bildern finden, das dort verwendet wurde (Entschuldigung für all diese Links, aber ich verfolge meinen Verlauf ...).
Der Autor war super produktiv und hat auch eine JavaScript-Version hinzugefügt, die ich hier poste:Mit JavaScript und k-means die dominanten Farben in Bildern finden
In diesem Fall erzeugen wir die dominanten Farben eines Bildes, ohne den MMCQ- (oder OQ-) Algorithmus zu verwenden, sondern K-Means. Das Problem ist, dass das Bild auch ein sein muss:
<canvas id="canvas" style="display: none;" width="200" height="200"></canvas>
und dan
function analyze(img_elem) {
var ctx = document.getElementById('canvas').getContext('2d')
, img = new Image();
img.onload = function() {
var results = document.getElementById('results');
results.innerHTML = 'Waiting...';
var colors = process_image(img, ctx)
, p1 = document.getElementById('c1')
, p2 = document.getElementById('c2')
, p3 = document.getElementById('c3');
p1.style.backgroundColor = colors[0];
p2.style.backgroundColor = colors[1];
p3.style.backgroundColor = colors[2];
results.innerHTML = 'Done';
}
img.src = img_elem.src;
}
Das liegt daran, dass der Canvas eine getContext () -Methode hat, mit der APIs zum Zeichnen von 2D-Bildern verfügbar gemacht werden - sieheEine Einführung in die Canvas 2D API
Dieser Kontext ctx wird an die Bildverarbeitungsfunktion übergeben
function process_image(img, ctx) {
var points = [];
ctx.drawImage(img, 0, 0, 200, 200);
data = ctx.getImageData(0, 0, 200, 200).data;
for (var i = 0, l = data.length; i < l; i += 4) {
var r = data[i]
, g = data[i+1]
, b = data[i+2];
points.push([r, g, b]);
}
var results = kmeans(points, 3, 1)
, hex = [];
for (var i = 0; i < results.length; i++) {
hex.push(rgbToHex(results[i][0]));
}
return hex;
}
So können Sie ein Bild auf der Leinwand durch den Kontext zeichnen und Bilddaten abrufen:
ctx.drawImage(img, 0, 0, 200, 200);
data = ctx.getImageData(0, 0, 200, 200).data;
Eine andere schöne Lösung ist in CoffeeScript, ColorTunes, aber dies verwendet auch a:
ColorTunes.getColorMap = function(canvas, sx, sy, w, h, nc) {
var index, indexBase, pdata, pixels, x, y, _i, _j, _ref, _ref1;
if (nc == null) {
nc = 8;
}
pdata = canvas.getContext("2d").getImageData(sx, sy, w, h).data;
pixels = [];
for (y = _i = sy, _ref = sy + h; _i < _ref; y = _i += 1) {
indexBase = y * w * 4;
for (x = _j = sx, _ref1 = sx + w; _j < _ref1; x = _j += 1) {
index = indexBase + (x * 4);
pixels.push([pdata[index], pdata[index + 1], pdata[index + 2]]);
}
}
return (new MMCQ).quantize(pixels, nc);
};
Aber, warte, wir haben kein<canvas/>
Element inTVML
!
atürlich gibt es native Lösungen wie Objective-C ColorCube, DominantColor - dies verwendet K-means
und die sehr schön und wiederverwendbar ColorArt von @AaronBrethorst von CocoaControls.
Trotz der Tatsache, dass dies in einer TVML-Anwendung über eine native JavaScriptCore-Bridge verwendet werden könnte - sieheWie brücke ich TVML / JavaScriptCore mit UIKit / Objective-C (Swift)?
Mein Ziel ist es, diese Arbeit vollständig in @ zu machTVJS
undTVML
.
Die einfachste MMCQ-JavaScript-Implementierung benötigt keinen Canvas: sieheBasic Javascript Port der MMCQ (Modified Median Cut Quantization) durch Nick Rabinowitz, benötigt aber das RGB-Array des Bildes:
var cmap = MMCQ.quantize(pixelArray, colorCount);
das stammt aus dem HTML<canvas/>
und das ist der Grund dafür!
function createPalette(sourceImage, colorCount) {
// Create custom CanvasImage object
var image = new CanvasImage(sourceImage),
imageData = image.getImageData(),
pixels = imageData.data,
pixelCount = image.getPixelCount();
// Store the RGB values in an array format suitable for quantize function
var pixelArray = [];
for (var i = 0, offset, r, g, b, a; i < pixelCount; i++) {
offset = i * 4;
r = pixels[offset + 0];
g = pixels[offset + 1];
b = pixels[offset + 2];
a = pixels[offset + 3];
// If pixel is mostly opaque and not white
if (a >= 125) {
if (!(r > 250 && g > 250 && b > 250)) {
pixelArray.push([r, g, b]);
}
}
}
// Send array to quantize function which clusters values
// using median cut algorithm
var cmap = MMCQ.quantize(pixelArray, colorCount);
var palette = cmap.palette();
// Clean up
image.removeCanvas();
return palette;
}
[FRAGE So erzeugen Sie die dominanten Farben eines RGB-Bildes ohne Verwendung von HTML5<canvas/>
, aber in reinem JavaScript aus dem @ eines BildByteArray
abgeholt mitXMLHttpRequest
?
[AKTUALISIEREN Ich habe diese Frage an @ gestell Color-Thiefgithub repo, Anpassung der RGB-Array-Berechnungen an die neueste Codebasis. Die Lösung, die ich ausprobiert habe, war dieses
ColorThief.prototype.getPaletteNoCanvas = function(sourceImageURL, colorCount, quality, done) {
var xhr = new XMLHttpRequest();
xhr.open('GET', sourceImageURL, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var uInt8Array = new Uint8Array(this.response);
var i = uInt8Array.length;
var biStr = new Array(i);
while (i--)
{ biStr[i] = String.fromCharCode(uInt8Array[i]);
}
if (typeof colorCount === 'undefined') {
colorCount = 10;
}
if (typeof quality === 'undefined' || quality < 1) {
quality = 10;
}
var pixels = uInt8Array;
var pixelCount = 152 * 152 * 4 // this should be width*height*4
// Store the RGB values in an array format suitable for quantize function
var pixelArray = [];
for (var i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
offset = i * 4;
r = pixels[offset + 0];
g = pixels[offset + 1];
b = pixels[offset + 2];
a = pixels[offset + 3];
// If pixel is mostly opaque and not white
if (a >= 125) {
if (!(r > 250 && g > 250 && b > 250)) {
pixelArray.push([r, g, b]);
}
}
}
// Send array to quantize function which clusters values
// using median cut algorithm
var cmap = MMCQ.quantize(pixelArray, colorCount);
var palette = cmap? cmap.palette() : null;
done.apply(this,[ palette ])
} // 200
};
xhr.send();
}
, aber es gibt nicht das richtige RGB-Farbarray zurück.
[AKTUALISIEREN Danke an alle vorschläge hab ich es geschafft. Jetzt ist ein vollständiges Beispiel auf @ verfügba Github,