Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit canvas colors to a specific array

How can I limit the RGB color space of a canvas image to a specific array of colors? F.ex:

var colors = ['#aaffee','#cc44cc','#00cc55','#0000aa'];
var rgb = [230,111,90];

// match rgb with the colors and return the closest match

I need to get the closest match from this array when I loop through the pixels in the canvas image data. Is there a clever function that can do that?

like image 371
David Hellsing Avatar asked Sep 20 '25 07:09

David Hellsing


1 Answers

How To limit colors to a specific color palette

You must map each and every original pixel to the nearest palette color.

To do this you actually calculate the distance between the original and palette colors on the colorwheel.

Here is an illustration. The original pixel (orange color) has arrows to each color in our specified palette (assume our palette has 3 specified colors: red, green, blue).

The palette color with the shortest arrow length is substituted for the original pixel.

Since the orange-red arrow is shortest, palette red will be substituted for original orange.

enter image description here

This is the important function that maps original color to palette color:

    // 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);
    }

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/GWQQH/

Note: you can improve this code using hash tables, tree searches, etc.

<!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>
like image 180
markE Avatar answered Sep 22 '25 00:09

markE