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,

Antworten auf die Frage(2)

Ihre Antwort auf die Frage