2015-10-23 6 views
5

Примечание для читателей: Это длинный вопрос, но для этого требуется фон для понимания заданного вопроса.Создание доминантных цветов для изображения RGB с XMLHttpRequest

color quantization technique обычно используется для получения цветных изображений. Один из хорошо известных библиотек, которые делают цвет квантования Leptonica через Modified Median Cut Quantization (MMCQ) and octree quantization (OQ) Github-х Color-thief по @lokesh очень простая реализация в JavaScript алгоритма MMCQ:

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

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

var CanvasImage = function (image) { 
    this.canvas = document.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, как мы увидим позже.

Другая реализация, которую я недавно узнал, была связана с этой статьей Using imagemagick, awk and kmeans to find dominant colors in images, которая ссылается на Using python to generate awesome linux desktop themes. Автор опубликовал статью о Using python and k-means to find the dominant colors in images, которая использовалась там (извините за все эти ссылки, но я слежу за своей историей ...).

Автор был очень продуктивным, и добавил версию JavaScript тоже, что я отправляю здесь: Using JavaScript and k-means to find the dominant colors in images

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

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

Это потому, что холст имеет метод getContext(), которые предоставляют 2D изображение рисования API, - см An introduction to the 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-C ColorCube, DominantColor - это использование K-средства

и очень красиво и многоразовые ColorArt по @AaronBrethorst из CocoaControls.

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

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

Простейшая реализация MMCQ JavaScript не нуждается в Canvas: см Basic Javascript port of the MMCQ (modified median cut quantization) по Nick Rabinowitz, но нуждается в массиве 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?

[UPDATE] Я отправил этот вопрос Color-Thief GitHub репо, адаптируя вычисления массива 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 цветов.

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

ответ

1

Элемент canvas используется как удобный способ декодирования изображения в массив RGBA. Вы также можете использовать чистые библиотеки JavaScript для декодирования изображений.

jpgjs является декодером JPEG и pngjs является PNG-декодером. Похоже, что JPEG-декодер будет работать с TVJS как есть. Однако декодер PNG выглядит так, как будто он работает в среде узла или веб-браузера, поэтому вам может потребоваться немного изменить его.

+0

Ну, я действительно открыл проблему на jpgjs: https://github.com/notmasteryet/jpgjs/issues/40. он частично работает, так как результат не совсем тот же, что и с массивом байтов RGB от Canvas (все еще исследуя, почему ...) – loretoparisi

+0

У меня есть проблема с '' 'jpg.js''' с дополнительной информацией и опубликована онлайн-демонстрация здесь: http://www.parisilabs.com/colors2/, показывающий результаты. – loretoparisi

+0

Похоже, у вас в значительной степени есть это, только некоторые незначительные вариации. Я заметил, что в версии вашего кода (process_image) вы масштабируете изображение перед чтением байтов из 'ctx.drawImage (img, 0, 0, 200, 200)'. Тем не менее, я не вижу одинакового кода масштабирования в примере в комментарии прямо над этим. Возможно, это объясняет разницу? –