简体   繁体   中英

Blurring algorithm canvas not working JS

I'm trying to implement a simple blurring algorithm averaging the colors from the surrounding pixels in a 3x3 area.

I loop throught the pixel array, in increments of 4. Then I have a function which takes 6 parameters:

r -> red value [0-255] int
g -> green value [0-255] int
b -> blue value [0-255] int
a -> alpha value(opacity) [0-255] int
d -> pixel array [r0,g0,b0,a0,r1,g1,b1,a1,r2... etc] array
i -> current index

And I generate 4 new value, new red, new green, new blue and new alpha and return them in an object.

This is the entire code:

//canvas setup
var width = 400;
var height = 400;

var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

canvas.width = width;
canvas.height = height;

//create image
var img = new Image();
img.src = 'images/input.jpg';

var pixels;

img.onload = function(){
    ctx.drawImage(img, 0, 0);
    pixels = ctx.getImageData(0, 0, width, height);
}

function action(pixels, callback){
    var newData = ctx.createImageData(width, height);

    for(var i = 0; i < pixels.data.length; i+=4){
        var r = pixels.data[i];
        var g = pixels.data[i+1];
        var b = pixels.data[i+2];
        var a = pixels.data[i+3];

        var channels = callback(r, g, b, a, pixels.data, i);

        newData.data[i] = channels.r;
        newData.data[i+1] = channels.g;
        newData.data[i+2] = channels.b;
        newData.data[i+3] = channels.a;

        pixels.data[i] = channels.r;
        pixels.data[i+1] = channels.g;
        pixels.data[i+2] = channels.b;
        pixels.data[i+3] = channels.a;
    }

    ctx.putImageData(newData, 0, 0);
}

function run(){
    action(pixels, function(r,g,b,a,d,i){

        var nr = (r
            + (d[i - 4] || r)
            + (d[i + 4] || r)
            + (d[i - 4 * width] || r)
            + (d[i + 4 * width] || r)
            + (d[i - 4 * width - 4] || r)
            + (d[i + 4 * width + 4] || r)
            + (d[i - 4 * width + 4] || r)
            + (d[i + 4 * width - 4] || r)
        ) / 9;

        var ng = (g
            + (d[i - 4] || g)
            + (d[i + 4] || g)
            + (d[i - 4 * width] || g)
            + (d[i + 4 * width] || g)
            + (d[i - 4 * width - 4] || g)
            + (d[i + 4 * width + 4] || g)
            + (d[i - 4 * width + 4] || g)
            + (d[i + 4 * width - 4] || g)
        ) / 9;

        var nb = (b
            + (d[i - 4] || b)
            + (d[i + 4] || b)
            + (d[i - 4 * width] || b)
            + (d[i + 4 * width] || b)
            + (d[i - 4 * width - 4] || b)
            + (d[i + 4 * width + 4] || b)
            + (d[i - 4 * width + 4] || b)
            + (d[i + 4 * width - 4] || b)
        ) / 9;

        return {r: nr, g: ng, b: nb, a: 255};
    });
}

As you can see, surrounding pixels value are hardcoded. You can test it here:

https://codepen.io/tyrellrummage/pen/Ewgzzx

If the run button does nothing, reload and try again (some issue with codepen cross-origin). Try hitting the run button several times to increase the passes of the algorithm.

You'll notice that it will completely grayscale the image after 2/3 passes. Thanks in advance!

You are indexing the wrong channels in the callback. And Not sure why you do the ||

Add i++ after each colour channel in the callback.

Also the mean value should be the mean of the square of each channels value as the value of each channel is the ~sqrt of the brightness. Not squaring the values will produce a mean that is darker than what it should be.

function run(){
    const w = width * 4;
    // the offset index for each pixel excluding the center pixel
    const grid = [-w - 4, -w, -w + 4, -4, 4, w - 4, w, w + 4];

    action(pixels,(r,g,b,a,dat,i) => {
        var idx, count;         
        r *= r;
        g *= g;
        b *= b;
        count = 1;
        for(idx = 0; idx < grid.length; idx ++){
            const off = grid[idx];
            if(i + off >= 0 && i + off < w * height){
                r += dat[i + off] * dat[i + off];
                g += dat[i + 1 + off] * dat[i + 1 + off];
                b += dat[i + 2 + off] * dat[i + 2 + off];
                a += dat[i + 3 + off];
                count ++;
            }
        }
        r = Math.sqrt(r / count);
        g = Math.sqrt(g / count);
        b = Math.sqrt(b / count);
        a = a / count;
        return {r,g,b,a};
     });
}

BTW move the line img.crossOrigin = "Anonymous"; above the line you set the img.src as when the image is in cached it gets loaded before the next line and the anon header does not get attached to the request.

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