How to apply blur effect on a bitmap image in C#?
17,211
Solution 1
Updated code (now much faster, requires use of UNSAFE keyword)
static void Main(string[] args)
{
Bitmap bitmap = new Bitmap("C:\\Users\\erik\\test.png");
bitmap = Blur(bitmap, 10);
bitmap.Save("C:\\Users\\erik\\test2.png");
}
private static Bitmap Blur(Bitmap image, Int32 blurSize)
{
return Blur(image, new Rectangle(0, 0, image.Width, image.Height), blurSize);
}
private unsafe static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image.Width, image.Height);
// make an exact copy of the bitmap provided
using (Graphics graphics = Graphics.FromImage(blurred))
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
// Lock the bitmap's bits
BitmapData blurredData = blurred.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, blurred.PixelFormat);
// Get bits per pixel for current PixelFormat
int bitsPerPixel = Image.GetPixelFormatSize(blurred.PixelFormat);
// Get pointer to first line
byte* scan0 = (byte*)blurredData.Scan0.ToPointer();
// look at every pixel in the blur rectangle
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;
// 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 < image.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
{
// Get pointer to RGB
byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;
avgB += data[0]; // Blue
avgG += data[1]; // Green
avgR += data[2]; // Red
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = 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 < image.Width && x < rectangle.Width; x++)
{
for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
{
// Get pointer to RGB
byte* data = scan0 + y * blurredData.Stride + x * bitsPerPixel / 8;
// Change values
data[0] = (byte)avgB;
data[1] = (byte)avgG;
data[2] = (byte)avgR;
}
}
}
}
// Unlock the bits
blurred.UnlockBits(blurredData);
return blurred;
}
Took 2.356 seconds
to process 256x256
image with blur value 10
.
Original Code (from Github - slightly altered)
static void Main(string[] args)
{
Bitmap bitmap = new Bitmap("C:\\Users\\erik\\test.png");
bitmap = Blur(bitmap, 10);
bitmap.Save("C:\\Users\\erik\\test2.png");
}
private static Bitmap Blur(Bitmap image, Int32 blurSize)
{
return Blur(image, new Rectangle(0, 0, image.Width, image.Height), blurSize);
}
private static Bitmap Blur(Bitmap image, Rectangle rectangle, Int32 blurSize)
{
Bitmap blurred = new Bitmap(image.Width, image.Height);
// make an exact copy of the bitmap provided
using (Graphics graphics = Graphics.FromImage(blurred))
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
// look at every pixel in the blur rectangle
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;
// 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 < image.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < image.Height); y++)
{
Color pixel = blurred.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = 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 < image.Width && x < rectangle.Width; x++)
for (int y = yy; y < yy + blurSize && y < image.Height && y < rectangle.Height; y++)
blurred.SetPixel(x, y, Color.FromArgb(avgR, avgG, avgB));
}
}
return blurred;
}
Took 7.594 seconds
to process 256x256
image with blur value 10
.
Orignal Image
Blurred Image (blur level 10)
Image from: https://www.pexels.com/search/landscape/
Solution 2
If you are using XAML check it from Microsoft. Also study this code it your problem can be solved.
Author by
Anton Lieskovsky
Updated on July 02, 2022Comments
-
Anton Lieskovsky almost 2 years
How can I apply blur effect on an image in C# without using a library?
-
Anton Lieskovsky almost 7 yearswithout XAML - I need it as script
-
Anton Lieskovsky almost 7 yearsgreat :) thank you
-
AFract almost 7 yearsSimple and efficient. As the OP didn't gave much details, it's probably fine. But I'm afraid this kind of implementation to be a bit slow, especially with high "blurSize" values :)
-
AFract almost 7 yearsIn fact I have just tested it, and it's not usable with values higher than 10. Slow, and it creates some artifacts with weird vertical streaks. For high values a gaussian blur (maybe like as you can find it in some libraries like AForge ?) would probably be more suited.
-
erikvimz almost 7 yearsI agree. But OP didn't have any specific requirements. I believe this will suffice.
-
AFract almost 7 years@ErikKralj I am agree too ! But I find the behavior of the algorithm with high values higher than 5 very intringing, it really gives something suprising! I looked at the code and I don't figure why. I suggest you to test on this image : pexels.com/photo/view-of-mountain-range-during-sunset-30865 with values 10 and 20 to see about what I am speaking :).
-
AFract almost 7 years(I should have said "with values between 5 and 10", going higher is useless. Apart the vertical streaks, the image becomes darker and darker. The image I gave in example is relevant because there is a lot of contrasts and color changes on relatively small parts of the image.
-
AFract almost 7 yearsHeyhey I've just figured why it does this. There's indeed a bug in algorithm ! Color pixel = blurred.GetPixel(x, y); should be Color pixel = image.GetPixel(x, y); :). The effect is better once it's fixed ! I just posted a comment on original github page.
-
erikvimz almost 7 years@AFract Your modification didn't make a difference for me. I got some free time so I took another look at the algorithm and implemented the use of
LockBits
andUnlockBits
, it now works much faster. -
AFract almost 7 yearsIf you don't see the difference, you obviously have not done the test with the image I suggested above. Try on it with blurSize up to 10 : from 5 the difference in resulting image is obvious. My remark was for the original code, your version with bitlock has nothing to do with it, I didn't test it (yet).
-
Geo Concepts over 6 yearsHi, can I use it with asp.net asp:Image and or with .ashx if yes, how please?
-
Roel about 5 yearsWhen I want to blur a small part of the image, it does not seam to work or is that not supported?
-
Roel about 5 yearsIt does not work if you want to blur a small area of the image.
-
FindOutIslamNow almost 5 yearsYour modified version gives me exception AcessViolation while the original code is fine
-
cheesus over 3 yearsYou should definitely update your code to fix the bug found by @AFract. If you read from the same image as you are writing during the Gaussian blur, you will read pixels that have already been updated. Imagine a single black pixel on an otherwise white image. The pixel should be blurred only in its surroundings. However, if you go line-by-line through the image, and you read the pixels that have already been updated, you get pixels far away from the original black pixel that "get some blurred black".
-
Fiach Reid almost 3 yearsI had to modify the for loop to change the clause x < rectangle.Width to x < rectangle.Width + xx and y < rectangle.Height to y < rectangle.Height + yy to correct the code for blurring small areas