Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Very Fast Image Blur in C# using Direct Bit Manipulation in Pure .NET

5.00/5 (5 votes)
17 Apr 2023CPOL1 min read 16.2K   352  
Very fast image processing without use of unsafe code
In this tip, you will learn how to implement very fast Image blur in C# using direct bit manipulation in pure .NET without the need for unsafe code.

Introduction

There's a C# example on Stack Overflow on how to do an image blur (you can find it here). It seems to be the best example on how to do this in C# available, but there are a couple of issues with it: It's slow, and it uses unsafe code.

I spent a little time with it, doing quite a bit of refactoring and converting it from unsafe to vanilla C#, and also rewriting it to use direct bit manipulation using a managed pointer to the bitmap's underlying array. This resulted in very fast (much faster than unsafe code) image processing.

Background

I stumbled across this Codeproject article on Fast pointerless image processing years ago. Mr Dunlap had a great way of directly accessing bitmap bits, but I hadn't seen anyone else on Codeproject doing anything with it. The code in this project is based on his idea.

Using the Code

Using the blur function is simple; Just call it as seen below:

C#
ImageTools.Blur(ref yourBitmap, intBlurSide);

The rewritten blur function looks like this:

C#
private static void Blur(ref Bitmap image, Rectangle rectangle, Int32 blurSize) {
            ExposedBitmap blurred = new ExposedBitmap(ref image);

            // Store height & width locally (improves performance)
            int height = blurred.Height;
            int width  = blurred.Width;

            for (int xx = rectangle.X; xx < rectangle.X + rectangle.Width; xx++)
            {
                for (int yy = rectangle.Y; yy < rectangle.Y + rectangle.Height; yy++)
                {
                    //byte red, green, blue;
                    int avgR = 0, avgG = 0, avgB = 0;
                    int blurPixelCount = 0;
                    int horizontalLocation;
                    int verticalLocation;
                    int pixelPointer;

                    // Average the color of the red, 
                    // green and blue for each pixel in the
                    // blur size while making sure 
                    // you don't go outside the image bounds:
                    for (int x = xx; (x < xx + blurSize && x < width); x++)
                    {
                        horizontalLocation = x * blurred.bytesPerPixel;
                        for (int y = yy; (y < yy + blurSize && y < height); y++)
                        {
                            verticalLocation = y * blurred.stride;
                            pixelPointer = verticalLocation + horizontalLocation;

                            avgB += blurred.pinnedArray.bytes[pixelPointer];
                            avgG += blurred.pinnedArray.bytes[pixelPointer + 1];
                            avgR += blurred.pinnedArray.bytes[pixelPointer + 2];

                            blurPixelCount++;
                        }
                    }

                    byte bavgr = (byte)(avgR / blurPixelCount);
                    byte bavgg = (byte)(avgG / blurPixelCount);
                    byte bavgb = (byte)(avgB / blurPixelCount);

                    // Now that we know the average for the blur size, 
                    // set each pixel to that color
                    for (int x = xx; x < xx + blurSize && x < width && 
                             x < rectangle.Width; x++)
                    {
                        horizontalLocation = x * blurred.bytesPerPixel;
                        for (int y = yy; y < yy + blurSize && 
                                 y < height && y < rectangle.Height; y++)
                        {
                            verticalLocation = y * blurred.stride;
                            pixelPointer = verticalLocation + horizontalLocation;

                            blurred.pinnedArray.bytes[pixelPointer]     = bavgb;
                            blurred.pinnedArray.bytes[pixelPointer + 1] = bavgg;
                            blurred.pinnedArray.bytes[pixelPointer + 2] = bavgr;
                        }
                    }
                }
            }

            image = blurred.exBitmap;
        }

My intention is for interested parties to download the project and have a look at the code itself.

History

  • 17th April, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)