如何将画布图像的 RGB 颜色空间限制为特定的颜色数组?前:
var colors = ['#aaffee','#cc44cc','#00cc55','#0000aa'];
var rgb = [230,111,90];
// match rgb with the colors and return the closest match
当我遍历画布图像数据中的像素时,我需要从这个数组中获得最接近的匹配。有没有一个聪明的功能可以做到这一点?
如何将画布图像的 RGB 颜色空间限制为特定的颜色数组?前:
var colors = ['#aaffee','#cc44cc','#00cc55','#0000aa'];
var rgb = [230,111,90];
// match rgb with the colors and return the closest match
当我遍历画布图像数据中的像素时,我需要从这个数组中获得最接近的匹配。有没有一个聪明的功能可以做到这一点?
如何将颜色限制为特定的调色板
您必须将每个原始像素映射到最近的调色板颜色。
为此,您实际上计算了色轮上原始颜色和调色板颜色之间的距离。
这是一个插图。原始像素(橙色)在我们指定的调色板中具有指向每种颜色的箭头(假设我们的调色板有 3 种指定颜色:红色、绿色、蓝色)。
具有最短箭头长度的调色板颜色将替换原始像素。
由于橙红色箭头最短,调色板红色将代替原来的橙色。
这是将原始颜色映射到调色板颜色的重要函数:
// use Euclidian distance to find closest color
// send in the rgb of the pixel to be substituted
function mapColorToPalette(red,green,blue){
var color,diffR,diffG,diffB,diffDistance,mappedColor;
var distance=25000;
for(var i=0;i<palette.length;i++){
color=palette[i];
diffR=( color.r - red );
diffG=( color.g - green );
diffB=( color.b - blue );
diffDistance = diffR*diffR + diffG*diffG + diffB*diffB;
if( diffDistance < distance ){
distance=diffDistance;
mappedColor=palette[i];
};
}
return(mappedColor);
}
这是代码和小提琴:http: //jsfiddle.net/m1erickson/GWQQH/
注意:您可以使用哈希表、树搜索等来改进此代码。
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:15px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvasOriginal=document.getElementById("OriginalCanvas");
var ctx=canvasOriginal.getContext("2d");
var canvasMapped=document.getElementById("MappedCanvas");
var ctxMapped=canvasMapped.getContext("2d");
// draw some off-colored rectangles
ctx.beginPath();
ctx.fillStyle="rgb(140,70,60)"; //red-ish
ctx.rect(10,10,20,20);
ctx.fill();
ctx.beginPath();
ctx.fillStyle="rgb(70,140,60)"; //green-ish
ctx.rect(10,40,20,20);
ctx.fill();
ctx.beginPath();
ctx.fillStyle="rgb(70,60,140)"; //blue-ish
ctx.rect(10,70,20,20);
ctx.fill();
// create an array of palette colors
var palette=[{r:255,g:0,b:0},{r:0,g:255,b:0},{r:0,g:0,b:255}];
// load all pixels into an array
var imageData=ctx.getImageData(0,0,canvasOriginal.width,canvasOriginal.height);
var data=imageData.data;
// rewrite all pixels using only the mapped colors
var mappedColor;
for(var i=0; i<data.length; i+=4) {
mappedColor = mapColorToPalette(data[i], data[i+1], data[i+2]);
if(data[i+3]>10){
data[i] = mappedColor.r;
data[i+1] = mappedColor.g;
data[i+2] = mappedColor.b;
}
}
ctxMapped.putImageData(imageData,0,0);
// use Euclidian distance to find closest color
function mapColorToPalette(red,green,blue){
var color,diffR,diffG,diffB,diffDistance,mappedColor;
var distance=25000;
for(var i=0;i<palette.length;i++){
color=palette[i];
diffR=( color.r - red );
diffG=( color.g - green );
diffB=( color.b - blue );
diffDistance = diffR*diffR + diffG*diffG + diffB*diffB;
if( diffDistance < distance ){
distance=diffDistance;
mappedColor=palette[i];
};
}
return(mappedColor);
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="OriginalCanvas" width=60 height=100></canvas>
<canvas id="MappedCanvas" width=60 height=100></canvas>
</body>
</html>
最好的方法可能是选择与所有颜色最匹配的颜色
var colors = ['#aaffee','#cc44cc','#00cc55','#0000aa'];
var rgb = [230,111,90];
var best = 768;
var canvasColor = colors[0];
for (i=0; i< colors.length; i++){
t = 0;
for (j=0; j<3; j++){
t += Math.abs(rgb[j] - parseInt(colors[i].substring(j*2+1,j*2+3), 16));
}
if (t < best){
best = t;
canvasColor = colors[i];
}
}
只是另一种解决方案。为了将 rgb 转换为 hsl,我使用了 TinyColor
function fixColors(c) {
var ctx = c.getContext("2d"),
colors = [
{ rgb: { r: 255, g: 0, b: 0 } },
{ rgb: { r: 255, g: 255, b: 0 } },
{ rgb: { r: 0, g: 255, b: 0 } },
{ rgb: { r: 0, g: 255, b: 255 } },
{ rgb: { r: 0, g: 0, b: 255 } },
{ rgb: { r: 255, g: 0, b: 255 } },
{ rgb: { r: 255, g: 0, b: 0 } }
],
pixelData = ctx.getImageData(0, 0, c.width, c.height),
data = pixelData.data,
cache = { },
m = Math,
i;
colors.forEach(function(c) {
c.hsl = rgb2hsl(c.rgb);
});
for (i = 0; i < data.length; i += 4) {
var key = "c_" + data[i + 0] + "_" + data[i + 1] + "_" + data[i + 2],
originalColor,
newColor;
if (!cache.hasOwnProperty(key)) {
cache[key] = tinycolor({r: data[i + 0], g: data[i + 1], b: data[i + 2]}).toHsl();
}
originalColor = cache[key]
newColor = colors.reduce(function(r, l) {
return m.abs(r.hsl.h - originalColor.h) < m.abs(l.hsl.h - originalColor.h) ? r : l;
});
data[i + 0] = newColor.rgb.r;
data[i + 1] = newColor.rgb.g;
data[i + 2] = newColor.rgb.b;
}
ctx.putImageData(pixelData, 0, 0);
}
function rgb2hsl(c) {
return tinycolor(c).toHsl();
}