使用 XMLHttpRequest 为 RGB 图像生成主色
Generate the Dominant Colors for an RGB image with XMLHttpRequest
读者注意事项:这是一个很长的问题,但需要背景知识才能理解所提问题。
color quantization technique 通常用于获取图像的主色。
进行颜色量化的著名库之一是 Leptonica through the Modified Median Cut Quantization (MMCQ) and octree quantization (OQ)
@lokesh Github 的 Color-thief 是 MMCQ 算法 JavaScript 中的一个非常简单的实现:
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 that links to 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;
}
这是因为 Canvas 有一个 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;
}
所以可以通过Context在Canvas上绘制图像并获取图像数据:
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);
};
但是,等等,我们在 TVML
!
中没有 <canvas/>
元素
当然,也有像Objective-C ColorCube, DominantColor这样的原生解决方案-这是使用K-means
以及来自 CocoaControls 的@AaronBrethorst 非常漂亮且可重复使用的 ColorArt。
尽管这可以通过 JavaScriptCore 桥接的本机在 TVML 应用程序中使用 - 请参阅 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) by 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;
}
[问题]
如何在不使用 HTML5 <canvas/>
的情况下生成 RGB 图像的主色,而是从使用 XMLHttpRequest
获取的图像的 ByteArray
中纯 JavaScript?
[更新]
我已将此问题发布到 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、
上提供了完整示例
读者注意事项:这是一个很长的问题,但需要背景知识才能理解所提问题。
color quantization technique 通常用于获取图像的主色。 进行颜色量化的著名库之一是 Leptonica through the Modified Median Cut Quantization (MMCQ) and octree quantization (OQ) @lokesh Github 的 Color-thief 是 MMCQ 算法 JavaScript 中的一个非常简单的实现:
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 that links to 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;
}
这是因为 Canvas 有一个 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;
}
所以可以通过Context在Canvas上绘制图像并获取图像数据:
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);
};
但是,等等,我们在 TVML
!
<canvas/>
元素
当然,也有像Objective-C ColorCube, DominantColor这样的原生解决方案-这是使用K-means
以及来自 CocoaControls 的@AaronBrethorst 非常漂亮且可重复使用的 ColorArt。
尽管这可以通过 JavaScriptCore 桥接的本机在 TVML 应用程序中使用 - 请参阅 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) by 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;
}
[问题]
如何在不使用 HTML5 <canvas/>
的情况下生成 RGB 图像的主色,而是从使用 XMLHttpRequest
获取的图像的 ByteArray
中纯 JavaScript?
[更新] 我已将此问题发布到 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、
上提供了完整示例