The Glacial Box Blur Algorithm Implementation

Computing the new average by going over the entire box for each pixel is obviously wrong.. you should subtract the edge column that's leaving and add the one that's entering the box.. but, at least, it works 😊



function doBlur(){
  var w = img.getWidth();
  var h = img.getHeight();
  var x,y;
  var imgBlur = new SimpleImage(width=img.getWidth(), height=img.getHeight());
  var p, pb, pij, avgR,avgG,avgB,total;
  for( p of img.values() ){
    avgR = 0;
    avgG = 0;
    avgB = 0;
    total = 0;
    x = p.getX();
    y = p.getY();
    for( let i = Math.max(0,x-BOX); i <= Math.min(w-1,x+BOX);i++ ){
      for( let j = Math.max(0,y-BOX); j <= Math.min(h-1,y+BOX);j++){
        pij = img.getPixel(i,j);
        avgR += pij.getRed();
        avgG += pij.getGreen();
        avgB += pij.getBlue();
        total++;
      }
    }
    pb = imgBlur.getPixel(x,y);
    pb.setRed(avgR/total);
    pb.setGreen(avgG/total);
    pb.setBlue(avgB/total);
  }
  imgBlur.drawTo(canvas);
}

For this particular image, this algorithm takes almost a minute with BOX=20. What if you want it to run faster - for that you need the genius of Yoel Nainggolan. From chatGPT :

To speed up the box blur algorithm, you can make use of a technique called "box filter separability". The idea is to break down the 2D blur operation into two 1D blur operations, one in the horizontal direction and one in the vertical direction. This can significantly reduce the number of calculations required, as we only need to perform the blur operation for each pixel once in each direction.

function doBlur() {
    if (img == null) {
        return;
    }

    var w = img.getWidth();
    var h = img.getHeight();
    var imgBlur = new SimpleImage(w, h);

    for (var y = 0; y < h; y++) {
        for (var x = 0; x < w; x++) {
            var avgR = 0;
            var avgG = 0;
            var avgB = 0;
            var count = 0;

            for (var i = Math.max(0, x - BOX); i <= Math.min(w - 1, x + BOX); i++) {
                var p = img.getPixel(i, y);
                avgR += p.getRed();
                avgG += p.getGreen();
                avgB += p.getBlue();
                count++;
            }

            var pb = imgBlur.getPixel(x, y);
            pb.setRed(avgR / count);
            pb.setGreen(avgG / count);
            pb.setBlue(avgB / count);
        }
    }

    for (var x = 0; x < w; x++) {
        for (var y = 0; y < h; y++) {
            var avgR = 0;
            var avgG = 0;
            var avgB = 0;
            var count = 0;

            for (var j = Math.max(0, y - BOX); j <= Math.min(h - 1, y + BOX); j++) {
                var p = imgBlur.getPixel(x, j);
                avgR += p.getRed();
                avgG += p.getGreen();
                avgB += p.getBlue();
                count++;
            }

            var pb = img.getPixel(x, y);
            pb.setRed(avgR / count);
            pb.setGreen(avgG / count);
            pb.setBlue(avgB / count);
        }
    }

    img.drawTo(canvas);
}

Comments

Popular posts from this blog

How Should You Put Your Text in a Box?