Tuesday, January 5, 2010

Pretty fast Gaussian blur in Javascript

I'm currently porting the image segmentation code described here, and it includes a gaussian blur. The gaussian blur code alone seemed useful enough to warrant a post, so here ya go. It's exponentially faster than the one here, which is the only other Javascript gaussian blur example I could find. Includes improvements by Einar Lielmanis.


Demo     Source code

4 comments:

  1. Nice. If you're still feeling adventurous, you may want to try van Vliet's approximation of the Gaussian filter [http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.12.2826]. A quick Google found some sample code:

    http://albumshaper.sourcearchive.com/documentation/2.1-5/blur_8cpp-source.html

    I think the GIMP uses this for it's IIR G-blur too.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Hi,

    I implemented the algorithm mentioned by Dan in a form that is suitable for Paul's demo (see below). If you find any way to improve the performance, please let me know. Sorry for the deleted postings, I keep finding tiny improvements. :)

    Best, Martin


    function smooth(src, dst, sigma)
    {

    var width = src.width;
    var width4 = 4 * width;
    var height = src.height;

    var srcData = src.getContext("2d").getImageData(0, 0, width, height);
    var data = srcData.data;
    var ctx = dst.getContext("2d");

    // compute coefficients as a function of sigma
    var q;
    if (sigma < 0.0) {
    sigma = 0.0;
    }
    if (sigma >= 2.5) {
    q = 0.98711 * sigma - 0.96330;
    }
    else if (sigma >= 0.5) {
    q = 3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * sigma);
    }
    else {
    q = 2 * sigma * (3.97156 - 4.14554 * Math.sqrt(1.0 - 0.26891 * 0.5));
    }

    //compute b0, b1, b2, and b3
    var qq = q * q;
    var qqq = qq * q;
    var b0 = 1.57825 + (2.44413 * q) + (1.4281 * qq ) + (0.422205 * qqq);
    var b1 = ((2.44413 * q) + (2.85619 * qq) + (1.26661 * qqq)) / b0;
    var b2 = (-((1.4281 * qq) + (1.26661 * qqq))) / b0;
    var b3 = (0.422205 * qqq) / b0;
    var bigB = 1.0 - (b1 + b2 + b3);

    // horizontal
    for (var c = 0; c < 3; c++) {
    for (var y = 0; y < height; y++) {
    // forward
    var index = y * width4 + c;
    var indexLast = y * width4 + 4 * (width - 1) + c;
    var pixel = data[index];
    var ppixel = pixel;
    var pppixel = ppixel;
    var ppppixel = pppixel;
    for (; index <= indexLast; index += 4) {
    pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
    data[index] = pixel;
    ppppixel = pppixel;
    pppixel = ppixel;
    ppixel = pixel;
    }
    // backward
    index = y * width4 + 4 * (width - 1) + c;
    indexLast = y * width4 + c;
    pixel = data[index];
    ppixel = pixel;
    pppixel = ppixel;
    ppppixel = pppixel;
    for (; index >= indexLast; index -= 4) {
    pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
    data[index] = pixel;
    ppppixel = pppixel;
    pppixel = ppixel;
    ppixel = pixel;
    }
    }
    }

    // vertical
    for (var c = 0; c < 3; c++) {
    for (var x = 0; x < width; x++) {
    // forward
    var index = 4 * x + c;
    var indexLast = (height - 1) * width4 + 4 * x + c;
    var pixel = data[index];
    var ppixel = pixel;
    var pppixel = ppixel;
    var ppppixel = pppixel;
    for (; index <= indexLast; index += width4) {
    pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
    data[index] = pixel;
    ppppixel = pppixel;
    pppixel = ppixel;
    ppixel = pixel;
    }
    // backward
    index = (height - 1) * width4 + 4 * x + c;
    indexLast = 4 * x + c;
    pixel = data[index];
    ppixel = pixel;
    pppixel = ppixel;
    ppppixel = pppixel;
    for (; index >= indexLast; index -= width4) {
    pixel = bigB * data[index] + b1 * ppixel + b2 * pppixel + b3 * ppppixel;
    data[index] = pixel;
    ppppixel = pppixel;
    pppixel = ppixel;
    ppixel = pixel;
    }
    }
    }

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

    ReplyDelete