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

Original Image

Blurred Image (blur level 10)

Blurred Image

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.

Share:
17,211
Anton Lieskovsky
Author by

Anton Lieskovsky

Updated on July 02, 2022

Comments

  • Anton Lieskovsky
    Anton Lieskovsky almost 2 years

    How can I apply blur effect on an image in C# without using a library?

  • Anton Lieskovsky
    Anton Lieskovsky almost 7 years
    without XAML - I need it as script
  • Anton Lieskovsky
    Anton Lieskovsky almost 7 years
    great :) thank you
  • AFract
    AFract almost 7 years
    Simple 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
    AFract almost 7 years
    In 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
    erikvimz almost 7 years
    I agree. But OP didn't have any specific requirements. I believe this will suffice.
  • AFract
    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
    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
    AFract almost 7 years
    Heyhey 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
    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 and UnlockBits, it now works much faster.
  • AFract
    AFract almost 7 years
    If 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
    Geo Concepts over 6 years
    Hi, can I use it with asp.net asp:Image and or with .ashx if yes, how please?
  • Roel
    Roel about 5 years
    When I want to blur a small part of the image, it does not seam to work or is that not supported?
  • Roel
    Roel about 5 years
    It does not work if you want to blur a small area of the image.
  • FindOutIslamNow
    FindOutIslamNow almost 5 years
    Your modified version gives me exception AcessViolation while the original code is fine
  • cheesus
    cheesus over 3 years
    You 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
    Fiach Reid almost 3 years
    I 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