Генерация доминирующих цветов для изображения RGB с XMLHttpRequest

Примечание для читателей. Это длинный вопрос, но для понимания поставленного вопроса требуется справочная информация.

метод цветового квантования обычно используется, чтобы получитьдоминирующие цвета изображения. Одна из известных библиотек, которая выполняет квантование цвета,Leptonica сквозьМодифицированное квантование медианного среза (MMCQ) и квантование октри (OQ) Github-хЦвет-вор @lokesh - очень простая реализация в алгоритме MMCQ на JavaScript:

var colorThief = new ColorThief();
colorThief.getColor(sourceImage);

Технически, изображение на<img/> HTML-элемент поддерживается на<canvas/> элемент:

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

И это проблема сTVML, как мы увидим позже.

Другая реализация, о которой я недавно узнал, была связана с этой статьейИспользование imagemagick, awk и kmeans для поиска доминирующих цветов в изображениях это ссылки наИспользование python для создания великолепных тем рабочего стола Linux, Автор разместил статью оИспользование python и k-means для поиска доминирующих цветов в изображениях это было использовано там (извините за все эти ссылки, но я слежу за своей историей ...).

Автор был очень продуктивным и добавил версию JavaScript, которую я публикую здесь:Использование JavaScript и k-means для поиска доминирующих цветов в изображениях

В этом случае мы генерируем доминирующие цвета изображения, используя не алгоритм MMCQ (или OQ), а K-средние. Проблема в том, что изображение должно быть также:

<canvas id="canvas" style="display: none;" width="200" height="200"></canvas>

а потом

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

Это потому, что в Canvas есть метод getContext (), который предоставляет API для рисования 2D-изображений - см.Введение в Canvas 2D API

Этот контекст CTX передается в функцию обработки изображений

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

Таким образом, вы можете нарисовать изображение на холсте через контекст и получить данные изображения:

ctx.drawImage(img, 0, 0, 200, 200);
data = ctx.getImageData(0, 0, 200, 200).data;

Другое хорошее решение в CoffeeScript,ColorTunes, но это также использует:

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

Но, подождите, у нас нет<canvas/> элемент вTVML!

Конечно, есть нативные решения, такие как Objective-CColorCube, DominantColor - это использует K-средства

и очень хороший и многоразовыйColorArt @AaronBrethorst от CocoaControls.

Несмотря на то, что это может быть использовано в приложении TVML через нативный мост JavaScriptCore - смотритеКак соединить TVML / JavaScriptCore с UIKit / Objective-C (Swift)?

моя цель состоит в том, чтобы сделать эту работу полностью вTVJS а такжеTVML.

Самая простая реализация MMCQ JavaScript не требует Canvas: см.Базовый порт Javascript MMCQ (модифицированное квантование медианного среза) отНик Рабиновиц, но нужен RGB-массив изображения:

var cmap = MMCQ.quantize(pixelArray, colorCount);

что взято из HTML<canvas/> и это причина этого!

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

[ВОПРОС] Как создать доминирующие цвета изображения RGB без использования HTML5<canvas/>, но в чистом JavaScript из изображенияByteArray принес сXMLHttpRequest?

[ОБНОВИТЬ] Я отправил этот вопросЦвет-Вор github repo, адаптирующий вычисления массива RGB к последней кодовой базе. Решение, которое я попробовал, состояло в следующем

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

но он не возвращает правильный массив цветов RGB.

[ОБНОВИТЬ] Благодаря всем предложениям я получил это работает. Теперь полный пример доступен наGithub,

Ответы на вопрос(1)

Ваш ответ на вопрос