如何将画布图像的RGB色彩空间限制为特定的颜色数组? F.ex:
var colors = ['#aaffee','#cc44cc','#00cc55','#0000aa'];
var rgb = [230,111,90];
// match rgb with the colors and return the closest match
当我遍历画布图像数据中的像素时,我需要从此数组获得最接近的匹配。是否有一个聪明的功能可以做到这一点?
答案 0 :(得分:9)
如何将颜色限制为特定调色板
您必须将每个原始像素映射到最近的调色板颜色。
为此,您实际上计算了色轮上原始颜色和调色板颜色之间的距离。
这是一个例子。原始像素(橙色)在指定的调色板中有每种颜色的箭头(假设我们的调色板有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>
答案 1 :(得分:0)
最好的方法可能是选择所有颜色上最匹配的颜色
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];
}
}
答案 2 :(得分:0)
只是另一种解决方案。为了将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();
}