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:
ImageTools.Blur(ref yourBitmap, intBlurSide);
The rewritten blur
function looks like this:
private static void Blur(ref Bitmap image, Rectangle rectangle, Int32 blurSize) {
ExposedBitmap blurred = new ExposedBitmap(ref image);
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++)
{
int avgR = 0, avgG = 0, avgB = 0;
int blurPixelCount = 0;
int horizontalLocation;
int verticalLocation;
int pixelPointer;
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);
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