Search code examples
canvaspixeljavascriptcore

Javascript canvas pixel manipulation


I have product images and each image has two configurable areas as below: 1. background 2. Foreground

I have to develop a feature where customer can change the color of both areas and save the design.

My problem is, i am traversing the image to read old pixel value and change it with new selected color.

Bit old pixel values are not consistent. There is textured effect on the image, which is causing the pixel value to be changed at every pixel.

How can i achieve the desired the functionality?

or, if you can suggest me any other approach, that will be great.

sample image link: http://developersbench.com/canvas/img/design_2.jpg


Solution

  • If i guess right, what you want is to change the pixels that are 'near' from a given color to be changed into another color.

    Easiest way i see is to use h,s,l color space : in this 'natural' color space, colors that have a similar hue will be similarly perceived by the eye. And you can just 'shift' the hue, while keeping same saturation (== color 'strength'), and lightness.

    So process your image point by point :
    • Convert point r, g, b to hue, saturation, lightness
    • ? Is the current hue near enough from sourceHue ?
    ---->> shift it to newHue (keeping same saturation / lightness)

    fiddle is here :

    http://jsfiddle.net/9GLNP/

    You can play around with parameters. The texture you provided has 18 avg hue, so if the source hue is too far from 18, no changes will be done. 8-10 seems a good tolerance.

    enter image description here

    // Provides a new canvas containing [img] where
    // all pixels having a hue less than [tolerance] 
    // distant from [tgtHue] will be replaced by [newHue]
    function shiftHue(img, tgtHue, newHue, tolerance) {
        // normalize inputs
        var normalizedTargetHue = tgtHue / 360;
        var normalizedNewHue = newHue / 360;
        var normalizedTolerance = tolerance / 360;
        // create output canvas
        var cv2 = document.createElement('canvas');
        cv2.width = img.width;
        cv2.height = img.height;
        var ctx2 = cv2.getContext('2d');
        ctx2.drawImage(img, 0, 0);
        // get canvad img data 
        var imgData = ctx2.getImageData(0, 0, img.width, img.height);
        var data = imgData.data;
        var lastIndex = img.width * img.height * 4;
        var rgb = [0, 0, 0];
        var hsv = [0.0, 0.0, 0.0];
        // loop on all pixels
        for (var i = 0; i < lastIndex; i += 4) {
            // retrieve r,g,b (! ignoring alpha !) 
            var r = data[i];
            var g = data[i + 1];
            var b = data[i + 2];
            // convert to hsv
            RGB2HSV(r, g, b, hsv);
            // change color if hue near enough from tgtHue
            var hueDelta = hsv[0] - normalizedTargetHue;
            if (Math.abs(hueDelta) < normalizedTolerance) {
                // adjust hue
                // ??? or do not add the delta ???
                hsv[0] = normalizedNewHue  //+ hueDelta;
                // convert back to rgb
                hsvToRgb(rgb, hsv);
                // store
                data[i] = rgb[0];
                data[i + 1] = rgb[1];
                data[i + 2] = rgb[2];
            }
        }
        ctx2.putImageData(imgData, 0, 0);
        return cv2;
    }
    

    EDIT : To get the hue, i consoled.log the hues... ;-) But many (if not all) image software have a color picker that shows hsl.
    For hsl, i have no specific links. google it, and also play with it within a graphic software.
    To avoid coding nightmare, i guess you have to decide of a convention on the textures you use, like they are 60 +/- 5 hue or like. So then you only have to decide of the final hue in your game. Detection might be tricky.