Примечание для читателей: Это длинный вопрос, но для этого требуется фон для понимания заданного вопроса.Создание доминантных цветов для изображения 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,
Ну, я действительно открыл проблему на jpgjs: https://github.com/notmasteryet/jpgjs/issues/40. он частично работает, так как результат не совсем тот же, что и с массивом байтов RGB от Canvas (все еще исследуя, почему ...) – loretoparisi
У меня есть проблема с '' 'jpg.js''' с дополнительной информацией и опубликована онлайн-демонстрация здесь: http://www.parisilabs.com/colors2/, показывающий результаты. – loretoparisi
Похоже, у вас в значительной степени есть это, только некоторые незначительные вариации. Я заметил, что в версии вашего кода (process_image) вы масштабируете изображение перед чтением байтов из 'ctx.drawImage (img, 0, 0, 200, 200)'. Тем не менее, я не вижу одинакового кода масштабирования в примере в комментарии прямо над этим. Возможно, это объясняет разницу? –