0

I am not good at JavaScript and have been trying to get Ivan Kuckir's Fastest Gaussian Blur code to work with no success. When I load the page, it becomes unresponsive so that I will have to close the window somehow. The code I use follows. What am doing wrong?

<html>
<head>
<script src="Path_To_GaussianBlur.js"></script>
</head>

<body>
<img id="sourceImage" src="SomeImage_200px_x_200px.jpg" />
<canvas id="canvas1" width="200" height="200" style="border: 1px solid blue"></canvas>
<canvas id="canvas2" width="200" height="200" style="border: 1px solid red"></canvas>

<script>
    document.getElementById("sourceImage").onload = function () {

        var c1 = document.getElementById("canvas1");
        var c2 = document.getElementById("canvas2");

        var ctx1 = c1.getContext("2d");
        var ctx2 = c2.getContext("2d");

        var img = document.getElementById("sourceImage");

        ctx1.drawImage(img, 0, 0);
        ctx2.drawImage(img, 0, 0);

        var source = ctx1.getImageData(0, 0, c1.width, c1.height);
        var target = ctx2.getImageData(0, 0, c2.width, c2.height);

        for (var i = 0; i < source.data.length; i += 4) {
            // red chanel
            gaussBlur_4(source.data[i], target.data[i], c1.width, c1.height, 2);
            // green channel
            gaussBlur_4(source.data[i + 1], target.data[i + 1], c1.width, c1.height, 2);
            // blue channel
            gaussBlur_4(source.data[i + 2], target.data[i + 2], c1.width, c1.height, 2);
        }

        ctx2.putImageData(target, 0, 0);
    };
</script>
</body>
</html>
Kamran
  • 782
  • 10
  • 35
  • It is happening because this algorithm is quite bit and I guess there is some infinite recursion, due that javascript execution never halts and halts/hangs browsers... – Parag Bhayani May 28 '16 at 21:37
  • It seems that I am calling `gaussBlur_4(...)` method with wrong arguments! – Kamran May 28 '16 at 21:45
  • I also think so @kamran – Parag Bhayani May 28 '16 at 21:50
  • I have shorten your canvas to 10px by 10px and algorithm is working fine, then I have resized it to 20px by 20px and it started halting the browser, but it did work eventually, and then for bigger size it crashed browser, so this gaussian algorithm is really so heavy, it is crashing th browser, may be you need to find out some lighter algos for your solution or some modification in gaussian algorithm to make it lighter – Parag Bhayani May 28 '16 at 22:13
  • @ParagBhayani But this is supposed to be a super fast algorithm and the author has gained a time in order of milliseconds for a big image. Have you taken a look at: http://blog.ivank.net/fastest-gaussian-blur.html ? – Kamran May 28 '16 at 22:35
  • Yahh, I also tried to understand the algorithm, and seems pretty complex, and I am still in doubt how it can be so fast... on the other hand probably we are missing something in passing the parameters to the function correctly... @kamran – Parag Bhayani May 28 '16 at 22:40
  • 1
    Its slow because you are applying a blur to the whole image for every pixel and every colour channel. You need to separate out the 3 colour channels (one array for each channel) and then call gaussBlur_4 ONCE only for each channel – Blindman67 May 29 '16 at 06:20
  • @Blindman67 Could you please clarify with a code example? – Kamran May 29 '16 at 06:47
  • @Blindman67 is right. Kamran, you should call the algorithm just once on each channel. You should also separate your channels - each color into separate array, you have RGBA format now. – Ivan Kuckir May 29 '16 at 22:19

1 Answers1

1

As stated in comments you need to split the rgba array into separate channels. You can do this in several ways, here is one:

Split

var idata = ctx.getImageData(0, 0, w, h), // assumes ctx/w/h to be defined
    rgba = idata.data,
    len = w * h,
    rSrc = new Uint8Array(len),           // source arrays
    gSrc = new Uint8Array(len),
    bSrc = new Uint8Array(len),
    aSrc = new Uint8Array(len),
    // define target arrays the same way as above
    i = 0, offset = 0;

for(; i < len; i++) {
  rSrc[i] = rgba[offset++];
  gSrc[i] = rgba[offset++];
  bSrc[i] = rgba[offset++];
  aSrc[i] = rgba[offset++];
}

Now you can pass each of those arrays to the blur function using target arrays set up in a similar manner as the source arrays, then merge back the result to rgba format.

Apply

gaussBlur_4(rSrc, rTrg, w, h, radius);
gaussBlur_4(gSrc, gTrg, w, h, radius);
gaussBlur_4(bSrc, bTrg, w, h, radius);
gaussBlur_4(aSrc, aTrg, w, h, radius);

Additional tip: if you're using images (as in photos) you can skip blurring the alpha channel as there is none (or technically, is fully opaque in canvas).

Merge

To merge, simply do:

for(i = 0, offset = 0; i < len; i++) {
  rgba[offset++] = rTrg[i];
  rgba[offset++] = gTrg[i];
  rgba[offset++] = bTrg[i];
  rgba[offset++] = aTrg[i]; // or just increase offset if you skipped alpha
}

ctx.putImageData(idata, 0, 0);

Due to license conflict on SO a running example can be found in this fiddle (and here is an alternative split/merge using 32-bits approach).

Community
  • 1
  • 1
  • 1
    Nice answer! Let me add, that if you want to blur with transparency, you should multiply RGB by alpha before blurring, and then divide it back after blurring. – Ivan Kuckir Aug 25 '17 at 10:33