8

Im reading DIP 2nd edition by Gonzalez and Woods and try to my hands dirty with Laplacian mask (page 129&130) using wxImage.

float kernel [3][3]= {{1, 1, 1},{1,-8, 1},{1, 1, 1}};   

here is the processing loops:

unsigned char r,g,b;                    

float rtotal, gtotal, btotal; rtotal = gtotal = btotal = 0.0;   
//ignore the border pixel              

for(int i = 1; i<imgWidth-1; i++)
{

   for(int j = 1; j<imgHeight-1; j++) 
    {

     rtotal = gtotal=btotal =0.0;


       for(int y = -1; y<=1;y++)

       {

            for(int x = -1; x<=1;x++)

            {

            // get each channel pixel value

            r = Image->GetRed(i+y,j+x);

            g = Image->GetGreen(i+y,j+x);

            b = Image->GetBlue(i+y,j+x);

            // calculate each channel surrouding neighbour pixel value base   

            rtotal += r* kernel[y+1][x+1];

            gtotal += g* kernel[y+1][x+1] ;

            btotal += b* kernel[y+1][x+1];

            }

    }
            //edit1: here is how to sharpen the image
            // original pixel - (0.2 * the sum of pixel neighbour)
            rtotal = loadedImage->GetRed(x,y) - 0.2*rtotal;

    gtotal = loadedImage->GetGreen(x,y) - 0.2*gtotal;

    btotal = loadedImage->GetBlue(x,y) - 0.2*btotal;
    // range checking

    if (rtotal >255) rtotal = 255;

       else if (rtotal <0) rtotal = 0;

    if(btotal>255) btotal = 255;

       else if(btotal < 0) btotal = 0;

    if(gtotal > 255) gtotal = 255;

       else if (gtotal < 0 ) gtotal =0;

    // commit new pixel value

    Image->SetRGB(i,j, rtotal, gtotal, btotal);

I applied that to the North Pole picture (grey image) and all I get is a blob of black and white pixels!

Any ideas where may I have missed something in the for loops?

Edit1: Finally get the answer after looking around on google. This dsp stuff is definitely tricky! I added to the code above, it will sharpen the image.

Cheers

bili
  • 610
  • 2
  • 9
  • 20

3 Answers3

5

First, the result of convolving with a Laplacian can have negative values. Consider a pixel with a value of 1 surrounded by 0's. The result of the convolution at that pixel will be -8.

Second, the range of the result will be between [-8 * 255, 8 * 255], which definitely does not fit into 8 bits. Essentially, when you do your range checking, you are losing most of the information, and most of your resulting pixels will end up either being 0 or 255.

What you have to do is store the result in an array of a type that is signed and wide enough to handle the range. Then, if you wish to output an 8-bit image, you would need to rescale the values so that -8 * 255 maps to 0, and 8 * 255 maps to 255. Or you can rescale it so that the least value maps to 0 and the greatest value maps to 255.

Edit: in this specific case, you can do the following:

rtotal = (rtotal + 8 * 255) / (16 * 255) * 255;

which simplifies to

rtotal = (rtotal + 8 * 255) / 16;

This would map rtotal into a range between 0 and 255 without truncation. You should do the same for gtotal and btotal.

Dima
  • 38,860
  • 14
  • 75
  • 115
  • Thanks for the answer. But what I dont understand is that when I do the neighbour computation then stores that into r/g/btotal which is a float value it should be big enough right? As far as I can see, one neighbour max value can be 8*255 or -8*255 like you said above which is (-)2040 *9 (nine neighbours all together) = (-)18360. I look up float range in c++ and it says the range of float is 7digits. However, now I do fully understand why I got a big blog of white and black pixel is because I missed out other calculation. See my edition OP. Thanks! – bili Sep 17 '11 at 00:52
  • The fact that you are using a float for the r/g/btotal is fine. The problem happens when you truncate the value of r/g/btotal to be between 0 and 255. Instead of setting everything below 0 to 0 and everything above 255 to 255 you should be scaling the value. You are truncating the range, while you should be compressing the range. – Dima Sep 17 '11 at 14:28
  • Yes, I get it now. I should scale the value instead of truncating it. tyvm! – bili Sep 17 '11 at 14:32
2

I think your problem is that r, g and b are type unsigned int and that, depending on which compiler you are using and how it is optimising, you are implicitly casting them to floats in the lines rtotal += r* kernel[y+1][x+1]; etc. But if the compiler casts differently to your expectations then computing the middle value will not work because unsigned int can't be negative.

Solution: change r, g and b to float.

It won't make any difference but there is a tiny error in the lines r = Image->GetRed(i+y,j+x); because i is looping over the horizontal and j is looping to vertical.

koan
  • 3,596
  • 2
  • 25
  • 35
  • When multiplying a float by an unsigned char, the compiler will always promote the unsigned char to a float. But not fitting into 8 bits at the end and the negative values are definitely the problem. – Dima Sep 16 '11 at 22:08
  • the reason r,g,b are unsigned char is because GetRed/Green/Blue functions return unsigned char. – bili Sep 17 '11 at 00:26
1

Are you not supposed to divide by the number of pixels in the mask after computing the weighted sum, thus producing a weighted average? Without this, the sum of nine pixel values (even when multiplied with not-too-bright mask values) will easily exceed 255.

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • 2
    The sum of the values in the mask matrix is zero, so no, there will not be any overall gain that would cause overflow. You do need to take care to use a larger type in intermediate computations, though, or else you could overflow an 8-bit value while you're computing the sum. For each pixel, the Laplacian replaces it with the sum of all of its neighbors minus eight times the original pixel value, something of a differentiation operation. It is used in edge detection. – Jason R Sep 16 '11 at 19:47
  • @Jason R: Make that an answer--I think that's exactly what happened here. – Drew Hall Sep 16 '11 at 19:58
  • @Jason R: i take it from your last two sentences is that for output pixel g(x,y) = sum of all of its neighbours (9 in total) - 8*f(x,y) while f(x,y) is the input pixel? – bili Sep 16 '11 at 20:15
  • @Jason R: If you have a pixel with the value of 0 surrounded by 8 pixels all with the value of 255, you get 8 * 255 as a result. Conversely, if it is a 255 surrounded by 0's, you get -8 * 255. This is definitely overflow. Only when the filter integrates to 1 do you not get overall gain. – Dima Sep 16 '11 at 22:19
  • @Dima: You are right. The DC gain of the filter is zero (hence its use as a differentiator), which is found by the sum of all of the matrix's elements. However, for a case like you described, you will definitely see gain and therefore potentia overflow. That's what I get for answering too late on a Friday afternoon. – Jason R Sep 17 '11 at 02:53