Thread: Blur and sharpen a bitmap

  1. #1
    Registered User
    Join Date
    Feb 2002
    Posts
    37

    Blur and sharpen a bitmap

    Is there a tutorial or an algorithm that describes how you implement a blur or sharpen function for bitmaps in standard C++.
    I am not talking about Microsoft or UNIX stuff but independent stuff.

    I know how to rotate, darken, invert and resize a bitmap image.

    Thanks in advance.

  2. #2
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    Blur shouldn't be that hard. Just create a new image where every pixel is the mean of all surrounding pixels in the original image.

    Here's a page that explains how to implement a blur or sharpen filter:
    http://www.gamedev.net/reference/art...rticle1994.asp
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  3. #3
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    You could implement the sharpen filter something like this:
    Code:
    void sharpenImage(Image& input, Image& output)
    {
      for x = 1 ... width
      for y = 1 ... height
      {
        output[x,y] = 5*input[x,y] - input[x+1,y] - input[x-1,y]
                               - input[x,y+1] - input[x,y-1];
      }
    }
    But it'd probably be better to write a more general kind of filter function, which supports more kinds of filters (given in matrix form).
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  4. #4
    Registered User
    Join Date
    Feb 2002
    Posts
    37
    Code:
    void sharpen()
    {
         int sharpen[3][3] = {{0, -1, 0}, {-1, 5, -1}, {0, -1, 0}};
         bitmap2.initFromBMP("test.bmp");     // Reads the bitmap file
    
         for(int i=0; i < bitmap2.getHeight(); i++)
             for(int j=0; j < bitmap2.getWidth(); j++)
                  for (int k = 0; k < 3; k++)
                      for (int l = 0; l < 3; l++)
                      {
                           Pixel p = bitmap2.getCopyOfPixel(i,j); // Gets the pixel from the specific position.
                           p.r = p.r * sharpen[k][l];   // Color red
                           p.g = p.g * sharpen[k][l];  // Color green
                           p.b = p.b * sharpen[k][l];  // Color blue
                           bitmap2.setPixel(i,j,p);      // Inserts the pixel at the right place.
                      }
    }
    It just returns a black image. I know I am doing something wrong.

  5. #5
    S Sang-drax's Avatar
    Join Date
    May 2002
    Location
    Göteborg, Sweden
    Posts
    2,072
    >>I know I am doing something wrong.

    Yes.

    1. You should add the different components from the filter together.

    2. You must create a new image instead of processing the original. Otherwise it won't work.

    3. The values returned from the filter won't be in the 0..255 interval. You need to normalize the values.

    4. Try doing it with a black&white image first.
    Last edited by Sang-drax : Tomorrow at 02:21 AM. Reason: Time travelling

  6. #6
    Registered User
    Join Date
    Feb 2002
    Posts
    37
    Code:
    void MyImageProcessingApp::sharpen()
    {
         bitmap1.initFromBMP("test.bmp");  // Reads the Bitmap file
         t = new Bitmap(bitmap1.getHeight(), bitmap1.getWidth()); // Dynamically createas an empty Bitmap
         int sumr;
         int sumg;
         int sumb;
         int sharpen[3][3] = {{0,-1,0}, {-1,5,-1}, {0,-1,0}}; // The sharpen filter
         Pixel color;
         unsigned char r;
         unsigned char g;
         unsigned char b;
    
         for (int i = 1; i < bitmap1.getHeight() - 1; i++)
         {
              for (int j = 1; j < bitmap1.getWidth() - 1; j++)
              {
    	for (int k = 0; k < 3; k++)
    	{
    	      for (int l = 0; l < 3; l++)
    	      {
    	           color = bitmap1.getCopyOfPixel(i, j); // gets an individual pixel
    	           r = color.r;
    	           g = color.g;
    	           b = color.b;
    	           sumr += r * sharpen[k][l];
    	           sumg += g * sharpen[k][l];
    	           sumb += b * sharpen[k][l];
    	     }
    	}
    
    	color.r = sumr;
    	color.g = sumg;
    	color.b = sumb;
    			
    	t->setPixel(i, j, color);  // Inserts the pixel
              }
         }
    }
    Now I get result but the whole image is a mess big time and I still don't understand why. I am sure it is very simple to fix it, but.....

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Code:
    struct RGBA
    {
       BYTE red;
       BYTE green;
       BYTE blue;
       BYTE alpha;
    };
    
    float BInterp(float v1,float v2,float v3,float v4,float f1,float f2)
    {
      asm {
         sub   esp,8
    
         fld [v2]
         fsub [v1]
         fmul [f1]
         fadd [v1]
         fstp [esp-4]
    
         fld [v4]
         fsub [v3]
         fmul [f1]
         fadd [v3]
         fstp [esp-8]
    
         fld [esp-8]
         fsub [esp-4]
         fmul [f2]
         fadd [esp-4]
    
         add esp,8
         }
    }
    
    void FilterImage(RGBA **pOutImage,RGBA *pInImage,DWORD dwWidth,DWORD dwHeight,float fFilterCoef)
    {
        *pOutImage=new RGBA[dwHeight*dwWidth];
        RGBA *ptrOutImage=*pOutImage;
    
        
       for (DWORD i=0;i<dwHeight-1;i++)
       {
          for (DWORD j=0;j<dwWidth-1;j++)
          {
             DWORD offset1=i*dwWidth+j;
             DWORD offset2=offset1+1;
             DWORD offset3=offset1+dwWidth;
             DWORD offset4=offset3+1;
           
             RGBA finalcolor;
             RGBA1=pInImage[offset1];
             RGBA2=pInImage[offset2];
             RGBA3=pInImage[offset3];
             RGBA4=pInImage[offset4];
    
             finalcolor.red=BI(RGBA1.red,
                                  RGBA2.red,
                                  RGBA3.red,
                                  RGBA4.red,
                                  fFilterCoef,
                                  fFilterCoef);
             finalcolor.green=BI(RGBA1.green,
                                  RGBA2.green,
                                  RGBA3.green,
                                  RGBA4.green,
                                  fFilterCoef,
                                  fFilterCoef);
             finalcolor.blue=BI(RGBA1.blue,
                                  RGBA2.blue,
                                  RGBA3.blue,
                                  RGBA4.blue,
                                  fFilterCoef,
                                  fFilterCoef);
            finalcolor.alpha=BI(RGBA1.alpha,
                                 RGBA2.alpha,
                                 RGBA3.alpha,
                                 RGBA4.alpha,
                                 fFilterCoef,
                                 fFilterCoef);
            ptrOutImage[offset1] =finalcolor;
          }
       }
    }
    Not the fastest code in the world, but it will do a bilinear filter on any 32-bit true color image, including the alpha channel. fFilterCoef should always satisfy 0<x<1.
    Last edited by VirtualAce; 03-09-2005 at 08:08 AM.

  8. #8
    Registered User
    Join Date
    Feb 2002
    Posts
    37
    Bubba thanks but what I am trying to do is a solution that is close to standard C++ as much as it can be. I am aware of getCopyOfPixel() and setPixel() are not standard. It feels like I have hit the wall with this one.

  9. #9
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    Quote Originally Posted by Mikro
    Code:
    void MyImageProcessingApp::sharpen()
    {
         bitmap1.initFromBMP("test.bmp");  // Reads the Bitmap file
         t = new Bitmap(bitmap1.getHeight(), bitmap1.getWidth()); // Dynamically createas an empty Bitmap
         int sumr;
         int sumg;
         int sumb;
         int sharpen[3][3] = {{0,-1,0}, {-1,5,-1}, {0,-1,0}}; // The sharpen filter
         Pixel color;
         unsigned char r;
         unsigned char g;
         unsigned char b;
    
         for (int i = 1; i < bitmap1.getHeight() - 1; i++)
         {
              for (int j = 1; j < bitmap1.getWidth() - 1; j++)
              {
    	for (int k = 0; k < 3; k++)
    	{
    	      for (int l = 0; l < 3; l++)
    	      {
    	           color = bitmap1.getCopyOfPixel(i, j); // gets an individual pixel
    	           r = color.r;
    	           g = color.g;
    	           b = color.b;
    	           sumr += r * sharpen[k][l];
    	           sumg += g * sharpen[k][l];
    	           sumb += b * sharpen[k][l];
    	     }
    	}
    
    	color.r = sumr;
    	color.g = sumg;
    	color.b = sumb;
    			
    	t->setPixel(i, j, color);  // Inserts the pixel
              }
         }
    }
    Now I get result but the whole image is a mess big time and I still don't understand why. I am sure it is very simple to fix it, but.....
    First of all, you keep calling getCopyOfPixel(i, j) or whatever. That's the same pixel you are sampling every time! You need to sample the correct pixel as if the matrix was overlying on top of your pixels. You also need to keep an additional sum variable to store the values from the matrix. So if you matrix was

    [0 1 0]
    [1 0 1]
    [0 1 0]

    the value for the new sum of the matrix would be 4. Just a simple line of code is all that takes:

    Code:
    ksum += Kernel[krow][kcol];
    or whatever your variable names are.

    Then, after your matrix loop, you set your pixel to (sum / ksum) for each r,g,b component respectively. Hopefully that makes sense to you.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  10. #10
    Registered User
    Join Date
    Feb 2002
    Posts
    37
    Code:
    void MyImageProcessingApp::sharpen()
    {
        bmp->initFromBMP("test.bmp"); 
        Pixel **matrix = new Pixel*[bmp->getHeight()];
    
        for (int i = 0; i < bmp->getHeight(); i++)
    	matrix[i] = new Pixel[bmp->getWidth()];
    
        t = new Bitmap(bmp->getHeight(), bmp->getWidth()); // Dynamically createas an empty Bitmap
        int sumr = 0;
        int sumg = 0;
        int sumb = 0;
        int sharpsum = 0;
        int sharpen[3][3] = {{0,-1,0}, {-1,5,-1}, {0,-1,0}}; // The sharpen filter
        unsigned char r;
        unsigned char g;
        unsigned char b;
    
        for (int i = 0; i < 3; i++)
    	for (int j = 0; j < 3; j++)
    		sharpsum += sharpen[i][j];
    
        for (int i = 1; i < bmp->getHeight() - 1; i++)
    	for (int j = 1; j < bmp->getWidth() - 1; j++)
    		matrix[i][j] = bmp->getCopyOfPixel(i, j); // gets an individual pixel and insert it at position i, j in the matrix.
    
        for (int i = 1; i < bmp->getHeight() - 1; i++)
    	for (int j = 1; j < bmp->getWidth() - 1; j++)
            {
    		for (int k = 0; k < 3; k++)
    		{
    			for (int l = 0; l < 3; l++)
    			{
    				sumr += matrix[i][j].r * sharpen[k][l];
    				sumg += matrix[i][j].g * sharpen[k][l];
    				sumb += matrix[i][j].b * sharpen[k][l];
    			}
    		}
    
    		matrix[i][j].r = sumr / sharpsum;
    		matrix[i][j].g = sumg / sharpsum;
    		matrix[i][j].b = sumb / sharpsum;
    			
    		t->setPixel(i, j, matrix[i][j]);  // Inserts the pixel
            }
    }
    Still I am unable to get this function to work.

  11. #11
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    You still are not doing what I was originally saying. Also, why do you have a matrix now that you fill? This is unnecessary.

    Also, what are you going to do about the corners of your image? You can't ignore them.

    Take this filter for example:

    columns numbered 0,1,2
    rows numbered 0,1,2

    [0 1 0]
    [1 1 1]
    [0 1 0]

    ksum = 5 for this filter. Let's run through pixel filtering pixel at (0,0).

    you would make pixel (0,0) correspond to the center of your matrix (i.e. col #1 row #1).

    assume row and col are filter loop indices and x and y are image loop indices. You should try going from -1 to 1 on your filter indices rather than 0 to 3, makes life easier. Perform bounds checking before you access pixels that are off the image.

    Psuedo-code
    Code:
    for y = 0 to height
      for x = 0 to width
    
        for row = -1 to 1
          for col = -1 to 1
    
            if((y + row) >= 0 && (y + row) < height)
              if((x + col) >= 0 && (x + col) < width)
                sum += filter[row + 1][col + 1] * pixel(x + i, y + j)
                ksum += filter[row + 1][col + 1];
              end if  
            end if
          end for
        end for
    
        setpixel(x, y, sum / ksum); // check for div 0
      end for
    end for
    You should be doing something like that. Don't just take this code and use it, understand why it works and then write it yourself.
    Last edited by MrWizard; 03-11-2005 at 01:21 PM.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  12. #12
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    also make sure you reset the variables that are collecting the sums!

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Thiose filters are just averaging nearby pixels. The bilinear filter will produce better results.

  14. #14
    Registered User
    Join Date
    Feb 2002
    Posts
    37
    Huh, I didn't manage to get this thing to work so I asked two of my friends if they were able to pull it off and neiter of them succeded. I am beginning to wonder if there is something wrong with the complier or the library.

    I would be very greatfull if somebody could check the files that I am working with and I have uploaded the project here,
    http://www.angelfire.com/theforce2/sgisun/bmp.zip

    OS: Windows 2000 sp4
    CPU: AMD Athlon64
    Compiler: MS Visual Studio.net 2003

  15. #15
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    Quote Originally Posted by Bubba
    Thiose filters are just averaging nearby pixels. The bilinear filter will produce better results.
    What? If you are referring to my filter I used in my example then you should know it was just for educational purposes. He already knew the filter he was going to use so I just picked arbitrary numbers. He originally asked how to sharpen and blur an image. I believe he means by way of a convolution kernel. Which is way more flexible than "bilinear filtering".

    OP:
    I'll look at your source at some point and get back to you.

    Found your problem. You have the line:

    Code:
    p = bmp->getCopyOfPixel(x, y);
    To retrieve a pixel from the source image but you are using the wrong variable. You need to use:

    Code:
    p = bitmap.getCopyOfPixel(x, y);
    Also make sure to reset rsum, gsum, bsum, ksum all to 0 after you set the pixel in the destination image so they are ready for the next pass. This should at least get you in the right direction.
    Last edited by MrWizard; 03-12-2005 at 07:03 PM.
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

Popular pages Recent additions subscribe to a feed