Special FX, Blur

If you managed to understand the improved pixelate algorithm, blurring an image will not be a problem as it follows almost exactly the same procedure. To blur an image, you need to iterate through each pixel in a picture and read the colour value of all pixels surrounding it. The new colour for each pixel is an average of its own plus the colours of those around it, however we need to set the colour of each pixel individually rather than just using a filled rectangle as in the pixelate algorithm.

Without further ado, here is the code:

function blur (&$image) {
    $imagex = imagesx($image);
    $imagey = imagesy($image);
    $dist = 1;

    for ($x = 0; $x < $imagex; ++$x) {
        for ($y = 0; $y < $imagey; ++$y) {
            $newr = 0;
            $newg = 0;
            $newb = 0;

            $colours = array();
            $thiscol = imagecolorat($image, $x, $y);

            for ($k = $x - $dist; $k <= $x + $dist; ++$k) {
                for ($l = $y - $dist; $l <= $y + $dist; ++$l) {
                    if ($k < 0) { $colours[] = $thiscol; continue; }
                    if ($k >= $imagex) { $colours[] = $thiscol; continue; }
                    if ($l < 0) { $colours[] = $thiscol; continue; }
                    if ($l >= $imagey) { $colours[] = $thiscol; continue; }
                    $colours[] = imagecolorat($image, $k, $l);

            foreach($colours as $colour) {
                $newr += ($colour >> 16) & 0xFF;
                $newg += ($colour >> 8) & 0xFF;
                $newb += $colour & 0xFF;

            $numelements = count($colours);
            $newr /= $numelements;
            $newg /= $numelements;
            $newb /= $numelements;

            $newcol = imagecolorallocate($image, $newr, $newg, $newb);
            imagesetpixel($image, $x, $y, $newcol);

As you can see, that is almost identical to the pixelate function. The differences are that each pixel is looped through individually as opposed to incrementing the loop variables by $blocksize. Also, note that the $dist variable is used to decide how many adjacent pixels should be read. In the example above, $dist is set to 1, which means the algorithm averages the colour values of the current pixel, as well as the pixels immediately above, below, left, right, above-left, above-right, below-left, and below-right. Increasing that to two will read the values from the two pixels immediately above the current one, the two to the left, etc.

My test image was 700 pixels wide by 400 high, making a total of 280,000 pixels. With $dist set to 1, PHP will read average nine values for each pixel, giving a total of 2,520,000 pixel reads. Once you add into that all the looping, dividing, colour allocation, and array manipulation, it should become clear that blurring an image is a very slow process and not one you want be do doing each time a page is requested. Just increasing $dist to 2 will make the script take a lot longer to execute.

However, there are benefits to increasing the $dist value. For example, the larger $dist is, the more blurred the final image is. The reason for this is because a large block of pixels is averaged to get the colour for the same one pixel, which means it is less likely to have its original colour. For sufficiently large values of $dist, the colour of each pixel will be calculated as the average of every other pixel in the image, essentially making the entire image the same colour.

If you're interesting in developing your blur effect further, you might want to try optimizing the algorithm so that you do the blur twice: once horizontally and once vertically. Even though this means looping over the pixels twice, it's still faster than the sampling approach used above.


Next chapter: Special FX, Other special effects >>

Previous chapter: Special FX, Pixelate

Jump to:


Home: Table of Contents

Copyright ©2015 Paul Hudson. Follow me: @twostraws.