1

After working/studying hours with canvas i have managed to get the image clone and it's pixels now i have made the user select a color from a color specterm and i have that color hex in my function :

move: function (color) {

 // 'color' is the value user selected

var img_id = jQuery(".selected_bg_img").attr("id");

alert(img_id);

var x = 0;
var y = 0;
var width = 409;
var height = 409;


var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById(img_id);
ctx.drawImage(img,x,y,width,height);

var imageData = ctx.getImageData(0,0,width,height);

var data = imageData.data;

alert(data);

}

now two tasks are getting in way,
1. How do we extract the maximum color from data ?
2. How do we convert that maximum color to the color we got in function ? for live working example i have link given at end
NOTE: Select the images (any last 3) from left side of the product image and and when color is choose it clones the image to the canvas below.
Here i am trying to clone the image with replacement of maximum color with the color user selected.
NOTE: Maximum color i mean (dominant color in image), e.g http://lokeshdhakar.com/projects/color-thief/ this project is getting the dominant color of image but it's on node and i'm trying to get the dominant and to change that color before cloning too .

for (var i=0;i<imgData.data.length;i+=4)
{
  imgData.data[i]=255-imgData.data[i];
  imgData.data[i+1]=255-imgData.data[i+1];
  imgData.data[i+2]=255-imgData.data[i+2];
  imgData.data[i+3]=255;
  }

*****EDIT***** My problem is not very complex i think i have this image enter image description here

so we can see clearly that the dominant color is grey, by any method i am just trying to replace that color with the new color i have in my function move and draw that image with the new color. The image from where i have taken and shows another example :
http://www.art.com/products/p14499522-sa-i3061806/pela-silverman-colorful-season-i.htm?sOrig=CAT&sOrigID=0&dimVals=729271&ui=573414C032AA44A693A641C8164EB595 on left side of image when we select the similar image they have option "change color" at the bottom center of image. This is what exactly i'm trying to do.

So now i tried to read the image 1x1 and this is what i get in the console when i log it(particular for the image i have shown) :

[166, 158, 152, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…]

So it maybe the very first start from top left corner, i guess it need to be iterated over whole image and this is what i get when i iterated over whole image:

[110, 118, 124, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255…]

The loop i used for iteration is:

var imageData = ctx.getImageData(0,0,width,height);

var data = imageData.data;

for (var i=0;i<data.length;i+=4)
{
  data[i]=255-data[i];
  data[i+1]=255-data[i+1];
  data[i+2]=255-data[i+2];
  data[i+3]=255;
}
console.log(data);

Is this iteration right ? If yes, it seems to be now matter of replacing the dominant color and i'm blank here how do we replace it and write back image with the replaced color or is there something more ?
*************EDIT***********
i have now user value in rgb, need to look for places where that value should be placed

Habib Rehman
  • 590
  • 3
  • 15
  • 36
  • How do you define "maximum color" (i.e what does it mean to you?) – Kaiido Sep 28 '16 at 22:20
  • i have explained bit more, please check it – Habib Rehman Sep 28 '16 at 23:01
  • So use "*dominant color*" and not "*maximum color*" . For the how they do it, I guess they are iterating all pixel colors and then get the one with the maximum number of occurrence (which is not the dominant mixed color btw, i.e in their example image, the dominant mixed color I got by drawing the image at 1x1px is `rgb(38, 32, 27)`) – Kaiido Sep 28 '16 at 23:19
  • @Kaiido yes i get that drawing of 1x1 but how do we write back with replacing that color in loop? p.s i have added another exact example that's what i was trying to do and now i think its matter of replacing the dominant color – Habib Rehman Sep 29 '16 at 06:37
  • 1
    Instead of doung such a major edit to your question, **after someone answered it** it would have been better to ask a new one. – Kaiido Sep 29 '16 at 09:37
  • okay i did it here: http://stackoverflow.com/questions/39767263/dominant-color-conversion-of-image-in-html-canvas – Habib Rehman Sep 29 '16 at 10:03

1 Answers1

2

This answer may be what you want as it is a little unclear.

Finding the dominant colour in an image

There are two ways that I use. There are many other ways to do it, which is the best is dependent on the needs and quality requiered. I will concentrat on using a histogram to filter out irrelevent pixels to get a result.

The easy way.

Create a canvas that is 1 by 1 pixel. Ensure that canvas smoothing is on.

ctx.imageSmoothingEnabled = true;    
ctx.mozImageSmoothingEnabled = true;

The draw the image onto that 1 by 1 canvas

ctx.drawImage(image,0,0,1,1);

Get the pixel data

var data = ctx.getImageData(0,0,1,1);

And the rgb values are what you are after.

r = data.data[0];
g = data.data[0];
b = data.data[0];

The result applied to the image below.

enter image description here

The long way

This method is long and problematic. You need to ignore the low and high values in the histogram for rgb (ignore black and white values) and for HSL ignore Saturations and Values (Lightness) that are 0 or 100% or you will get reds always dominating for images that have good contrast.

In image processing a histogram is a plot of the distribution of image properties (eg red green blue) within the image. As digital images have discrete values the histogram (a bar graph) will plot along the x axis the value and in the y axis the number of pixels that have that value.

An example of the histogram. Note that this code will not run here as there is cross origin restriction. (sorry forgot my browser had that disabled).

/** CopyImage.js begin **/
// copies an image adding the 2d context
function copyImage(img){
    var image = document.createElement("canvas");  
    image.width = img.width;
    image.height = img.height; 
    image.ctx = image.getContext("2d"); 
    image.ctx.drawImage(img, 0, 0);
    return image;
}
function copyImageScale(img,scale){
    var image = document.createElement("canvas");  
    image.width = Math.floor(img.width * scale);
    image.height = Math.floor(img.height * scale); 
    image.ctx = image.getContext("2d"); 
    image.ctx.drawImage(img, 0, 0,image.width,image.height);
    return image;
}
/** CopyImage.js end **/
function getHistogram(img){  // create a histogram of rgb and Black and White
    var R,G,B,BW;
    R = [];
    G = [];
    B = [];
    BW = [];  // Black and white is just a mean of RGB
    for(var i=0; i < 256; i++){
       R[i] = 0;  
       G[i] = 0;  
       B[i] = 0;  
       BW[i] = 0;  
    }
    var max = 0;    // to normalise the values need max
    var maxBW = 0;   // Black and White will have much higher values so normalise seperatly
    var data = img.ctx.getImageData(0,0,img.width,img.height);
    var d = data.data;
    var r,g,b;
    i = 0;
    while(i < d.length){
        r = d[i++];  // get pixel rgb values
        g = d[i++];
        b = d[i++];
        i++; // skip alpha
        R[r] += 1;  // count each value
        G[g] += 1;
        B[b] += 1;
        BW[Math.floor((r+g+b)/3)] += 1;
    }
    // get max
    for(i = 0; i < 256; i++){
        max = Math.max(R[i],G[i],B[i],max);
        maxBW = Math.max(BW[i],maxBW);
    }
    // return the data    
    return {
        red : R,
        green : G,
        blue : B,
        gray : BW,
        rgbMax : max,
        grayMax : maxBW,
    };
}

function plotHistogram(data,ctx,sel){
    var w = ctx.canvas.width;
    var h = ctx.canvas.height;
    ctx.clearRect(0,0,w,h); // clear any existing data
    var d = data[sel];
    if(sel !== "gray"){
        ctx.fillStyle = sel;
    }
    var rw = 1 / d.length; // normalise bar width
    rw *= w; // scale to draw area;
    for(var i = 0; i < d.length; i++ ){
        var v = 1-(d[i]/data.rgbMax); // normalise and invert for plot
        v *= h; // scale to draw area;
        var x = i/d.length;  // normaise x axis
        x *= w; // scale to draw area
        if(sel === 'gray'){
            ctx.fillStyle = "rgb("+i+","+i+","+i+")";
        }
        ctx.fillRect(x,v,rw,h-v); // plot the bar
    }
}

var canMain = document.createElement("canvas");  
canMain.width = 512;
canMain.height = 512; 
canMain.ctx = canMain.getContext("2d"); 
document.body.appendChild(canMain);
var ctx = canMain.ctx;

var can = document.createElement("canvas");  
can.width = 512;
can.height = 512; 
can.ctx = can.getContext("2d"); 

// load image and display histogram
var image = new Image();
image.src = "https://i.stack.imgur.com/tjTTJ.jpg";
image.onload = function(){
    image = copyImage(this);
    var hist = getHistogram(image);
    // make background black
    ctx.fillStyle = "black"
    ctx.fillRect(0,0,ctx.canvas.width,ctx.canvas.height);
    
    // create and show each plot
    plotHistogram(hist,can.ctx,"red");
    document.body.appendChild(copyImageScale(can,0.5));
    ctx.drawImage(can,0,0,canvas.width,canvas.height);
    plotHistogram(hist,can.ctx,"green");
    document.body.appendChild(copyImageScale(can,0.5));
    ctx.globalCompositeOperation = "lighter"
    ctx.drawImage(can,0,0,canvas.width,canvas.height);
    plotHistogram(hist,can.ctx,"blue");
    document.body.appendChild(copyImageScale(can,0.5));
    ctx.globalCompositeOperation = "lighter"
    ctx.drawImage(can,0,0,canvas.width,canvas.height);
    plotHistogram(hist,can.ctx,"gray");
    document.body.appendChild(copyImageScale(can,0.5));
    ctx.globalCompositeOperation = "source-over"
    ctx.globalAlpha = 0.9;
    ctx.drawImage(can,0,0,canvas.width,canvas.height);
    ctx.globalAlpha = 1;
}

As the above code required a hosted image the results are shown below.

The image

enter image description here

The RGB and gray histograms as output from above code.

enter image description here

From this you can see the dominant channels and the distribution of rgb over the image (Please note that I removed the black as this image has a lot of black pixels that where make the rest of the pixel counts shrink into insignificance)

To find the dominant colour you can do the same but for the HSL values. For this you will need to convert from RGB to HSL. This answer has a function to do that. Then it is just a matter of counting each HSL value and creating a histogram.

The next image is a histogram in HSL of the above sample image.

enter image description here

As you can see there is a lot of red, then a peek at yellow, and another in green.

But as is this still will not get you what you want. The second histogram (saturation) shows a clear peak in the center. There are a lot of colours that have good saturation but this does not tell you what colour that is.

You need to filter the HSL value to around that peak.

What I do is multiply the HSL values by the f(s) = 1-Math.abs(s-50)/50 s is Saturation range 0-100. This will amplify hue and value where saturation is max. The resulting histogram looks like

enter image description here

Now the peak hue (ignoring the reds) is orange. The hue value is ~ 42. Then you apply a filter that removes all pixels that are not near the hue = 42 (you may want to give a falloff). You apply this filter on the HSL value you get after the last step. The next image shows the resulting image after applying this filter to the original

enter image description here

and then get a sum of all the remaining pixel's R,G,B. The mean of each RGB value is the common colour.

The common colour as by this method.

enter image description here

Note the result is different than if you apply this method as described and in the code above. I used my own image processing app to get the instrume steps and it uses true logarithmic photon counts for RGB values that will tend to be darker than linear interpolation. The final step expands the filtered pixel image to an instrume step that is too complicated to describe here (a modification on the easy method at start of answer). looks like

enter image description here

Also I applied this to the whole image, this can be very slow for large images, but it will work just as well if you first reduce the size of the image (must have image smoothing on (defaults of most browsers)). 128 8 128 is a good size but you can go down lower. It will depend on the image how small you can make it befor it fails.

Community
  • 1
  • 1
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • yes that's kind of what i'm looking for but sorry for being unclear a bit i have edited question with the more exact description, i don't think i have to saturate the image as it won't go that much complex as an example http://www.art.com/products/p14499522-sa-i3061806/pela-silverman-colorful-season-i.htm?sOrig=CAT&sOrigID=0&dimVals=729271&ui=573414C032AA44A693A641C8164EB595 this is what i'm trying to do with the wall of image, they have that option in bottom of image "choose color" – Habib Rehman Sep 29 '16 at 06:35