# Thread: Blur and sharpen a bitmap

1. ## 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.

2. 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

3. 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).

4. 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. >>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.

6. 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. 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]
fstp [esp-4]

fld [v4]
fsub [v3]
fmul [f1]
fstp [esp-8]

fld [esp-8]
fsub [esp-4]
fmul [f2]

}
}

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.

8. 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. 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.

10. 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. 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.

12. also make sure you reset the variables that are collecting the sums!

13. Thiose filters are just averaging nearby pixels. The bilinear filter will produce better results.

14. 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. 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.