简体   繁体   中英

HTML5 Canvas Enhance Image

I have the following function to brighten/darken an image.

<script>
brightness = function(delta) {
    return function (pixels, args) {
        var d = pixels.data;
        for (var i = 0; i < d.length; i += 4) {
            d[i] += delta;     // red
            d[i + 1] += delta; // green
            d[i + 2] += delta; // blue   
        }
        return pixels;
    };
};
</script>

Just want to try out something more interesting, is it possible that I can perform auto enhancement to an image? What I mean is to brighten/darken only certain area in a photo. How should I go about detecting if the pixel is dark for example, then I should brighten it slightly? Thanks.

What you want to do is to modify the image dynamic.
So you must decide, for normalized pixel's luminosity ranging from 0.0 to 1.0, of a transform function that you will apply on the luminosity of each pixel.

The function you seek will have to enhance lower luminosity (near 0), and keep quite the same luminosity for high luminosity (near 1).

Here's an example of a (typicall) transfert function :
在此处输入图片说明

So you'll want a gamma > 1.
You see here, for instance, that if the input luminosity is 0.2, the output luminosity is 0.45 , that is more than twice the original value.
for 0.8 input, we have 0.95 value, a 20% increase.

To change only the luminosity without changing the perceived color, the simplest solution i see is to use another colorspace, like hsl.
With h,s,l, you represent a color in a way that is meaningfull for the human eye :
h is hue : the 'color',
saturation is the color 'strength',
and l is ... the luminosity.

So the steps are :

for each pixel  
    compute h,s,l of the pixel out of its r,g,b  
    apply one transform function on luminosity 
              a good one is : new l  =  Math.pow(l, 1 / gamma);  
    compute new (r,g,b) out of (h, s, new l)  
    write those values.  

i made a fiddle to illustrate :

fiddle result : http://jsfiddle.net/gamealchemist/yqvmC/embedded/result/
fiddle itself : http://jsfiddle.net/gamealchemist/yqvmC/

Edit : here is a modified version, that takes an image as input, and returns an image.

The parameter can either be a gamma value (number), or the transform function that you like.
I added a gamma compress function for the example. You can see on the result (scroll down to see it), that compressing is quite harsh : all luminosity values get centered around ratio*(max-min), which makes the image very readable, but with a low contrast.

Here is the code :

// pow is the power of the function
// min is min value returned
// max is max value returned.
function gamma(pow, min, max, x) {
    return min + Math.pow(x, 1 / pow) * (max - min);
}

// pow is the 'gamma' used for both part of the curves
// min is the minimum value returned / max the max
// center is the luminosity where we stop reducing and start expanding
// ratio states where reduced luminosity should lay between min and max.
function gammaCompress(pow, min, max, center, ratio, x) {
    var xr = 0;
    if (x < center) {
        xr = x / center;
        return min + Math.pow(xr, 1 / pow) * (max - min) * ratio;
    } else {
        xr = (x - center) / (1 - center);
        return min + (max - min) * ratio + Math.pow(xr, 1 / pow) * (max - min) * (1 - ratio);
    }
}

function getEnligthedImage(sourceImage, transform) {
    // if a number, not a bound transform function, was provided,
    // assume it's a gamma targetting [0;1]
    if (typeof transform != 'function') {
        transform = gamma.bind(null, transform, 0, 1);
    }
    var tgtCv = document.createElement('canvas');
    tgtCv.width = sourceImage.width;
    tgtCv.height = sourceImage.height;
    var context = tgtCv.getContext('2d');
    context.drawImage(img, 0, 0);
    var imgData = context.getImageData(0, 0, canvasWidth, canvasHeight);
    var pix = imgData.data;
    var hslValue = {        h: 0,        s: 0,        l: 0    };
    var rgbValue = {        r: 0,        g: 0,        b: 0    };
    for (var i = 0; i < pix.length; i += 4) {
        rgbToHsl(pix[i], pix[i + 1], pix[i + 2], hslValue);
        hslValue.l = transform(hslValue.l);
        hslToRgb(hslValue.h, hslValue.s, hslValue.l, rgbValue);
        pix[i] = rgbValue.r;
        pix[i + 1] = rgbValue.g;
        pix[i + 2] = rgbValue.b;
    }
    context.putImageData(imgData, 0, 0);
    var newImage = new Image();
    newImage.src = tgtCv.toDataURL("image/png");
    return newImage;
}

var result = getEnligthedImage(img, 1.6);
var pr = document.createElement('div');
pr.innerHTML = 'example for a gamma 1.6'
document.body.appendChild(pr);
document.body.appendChild(result);

var compressor = gammaCompress.bind(null, 1.4, 0.2, 1.0, 0.5, 0.5);
var compressedResult = getEnligthedImage(img, compressor);
pr = document.createElement('div');
pr.innerHTML = 'example using a gamma compressor. min is 0.2'
document.body.appendChild(pr);
document.body.appendChild(compressedResult);

If you want to do some other things with the image, save to file, send to server, or like, search google :-) , this link might help :
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement

Simple function for auto adjust colors. Will brighten or darken image based on histogram.

Brighten/darken only certain area in a photo is hard, because it is difficult to join changed and unchanged areas.

Sorry no fiddle, i have problems with images here.

<img alt="" src="bright.png" />
<br /><br />
<canvas id="cc"></canvas>

<script>
var img = new Image();
img.src = 'bright.png';
img.onload = function(){
    var canvas = document.getElementById("cc");
    var ctx = canvas.getContext("2d");
    canvas.width=300;
    canvas.height=200;
    ctx.drawImage(img, 0, 0);

    auto_adjust(ctx, 300, 200);
    }

function auto_adjust(context, W, H){
    //settings
    var white = 240;    //white color min
    var black = 30;     //black color max
    var target_white = 1;   //how much % white colors should take
    var target_black = 0.5; //how much % black colors should take
    var modify = 1.1;   //color modify strength

    var img = context.getImageData(0, 0, W, H);
    var imgData = img.data;
    var n = 0;  //pixels count without transparent

    //make sure we have white
    var n_valid = 0;
    for(var i = 0; i < imgData.length; i += 4){
            if(imgData[i+3] == 0) continue; //transparent
            if((imgData[i] + imgData[i+1] + imgData[i+2]) / 3 > white) n_valid++;
            n++;
        }
    target = target_white;
    var n_fix_white = 0;
    var done = false;
    for(var j=0; j < 30; j++){
        if(n_valid * 100 / n >= target) done = true;
        if(done == true) break;
        n_fix_white++;

        //adjust
        for(var i = 0; i < imgData.length; i += 4){
            if(imgData[i+3] == 0) continue; //transparent
            for(var c = 0; c < 3; c++){
                var x = i + c;
                if(imgData[x] < 10) continue;
                //increase white
                imgData[x] *= modify;
                imgData[x] = Math.round(imgData[x]);
                if(imgData[x] > 255) imgData[x] = 255;
                }
            }

        //recheck
        n_valid = 0;
        for(var i = 0; i < imgData.length; i += 4){
            if(imgData[i+3] == 0) continue; //transparent
                if((imgData[i] + imgData[i+1] + imgData[i+2]) / 3 > white) n_valid++;
            }
        }

    //make sure we have black
    n_valid = 0;
    for(var i = 0; i < imgData.length; i += 4){
        if(imgData[i+3] == 0) continue; //transparent
            if((imgData[i] + imgData[i+1] + imgData[i+2]) / 3 < black) n_valid++;       
        }
    target = target_black;
    var n_fix_black = 0;
    var done = false;
    for(var j=0; j < 30; j++){
        if(n_valid * 100 / n >= target) done = true;
        if(done == true) break;
        n_fix_black++;

        //adjust
        for(var i = 0; i < imgData.length; i += 4){
            if(imgData[i+3] == 0) continue; //transparent
            for(var c = 0; c < 3; c++){
                var x = i + c;
                if(imgData[x] > 240) continue;
                //increase black
                imgData[x] -= (255-imgData[x]) * modify - (255-imgData[x]);
                imgData[x] = Math.round(imgData[x]);
                }
            }

        //recheck
        n_valid = 0;
        for(var i = 0; i < imgData.length; i += 4){
            if(imgData[i+3] == 0) continue; //transparent
                if((imgData[i] + imgData[i+1] + imgData[i+2]) / 3 < black) n_valid++;
            }
        }

    //save  
    context.putImageData(img, 0, 0);
    //log('Iterations: brighten='+n_fix_white+", darken="+n_fix_black);
    }
</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM