Генерация доминирующих цветов для изображения RGB с XMLHttpRequest
Примечание для читателей. Это длинный вопрос, но для понимания поставленного вопроса требуется справочная информация.
Метод цветового квантования обычно используется для получения доминирующих цветов изображения. Одна из известных библиотек, которая выполняет квантование цветов, - это Leptonica с помощью модифицированного срединного квантования среза (MMCQ) и квантования октодеревьев (OQ). Color-thief Github от @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
, как мы увидим позже.
Еще одна реализация, о которой я недавно узнал, была связана с этой статьей Использование 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-C ColorCube, DominantColor - это использование K-средних
и очень хороший и многократно используемый ColorArt от @AaronBrethorst от CocoaControls.
Несмотря на то, что это можно использовать в приложении TVML через нативный мост для JavaScriptCore - см. Как связать TVML/JavaScriptCore с UIKit/Objective-C (Swift)?
моя цель состоит в том, чтобы сделать эту работу полностью в TVJS
а также TVML
,
Самая простая реализация MMCQ JavaScript не нуждается в Canvas: см. Порт Basic 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
?
[ОБНОВЛЕНИЕ] Я разместил этот вопрос в репозитории 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.
[ОБНОВЛЕНИЕ] Благодаря всем предложениям я получил это работает. Теперь полный пример доступен на Github,
1 ответ
Элемент canvas используется как удобный способ декодирования изображения в массив RGBA. Вы также можете использовать чистые библиотеки JavaScript для декодирования изображений.
jpgjs - это декодер JPEG, а pngjs - это декодер PNG. Похоже, что декодер JPEG будет работать с TVJS как есть. Однако PNG-декодер выглядит так, как будто он работает в среде Node или веб-браузера, поэтому вам, возможно, придется немного его настроить.