Click here to Skip to main content
Click here to Skip to main content

Image Processing for Dummies with C# and GDI+ Part 2 - Convolution Filters

By , 7 Nov 2005
 

Sample Image

Overview

Welcome back for the second installment in this series.  This installment serves as an introduction to the world of convolution filters.  It is also the first version of our program that offers one level of undo.  We'll build on that later, but for now I thought it mandatory that you be able to undo your experiments without having to reload the image every time.

So what is a convolution filter ? Essentially, it's a matrix, as follows:

Sample Image

The idea is that the pixel we are processing, and the eight that surround it, are each given a weight.  The total value of the matrix is divided by a factor, and optionally an offset is added to the end value.  The matrix above is called an identity matrix, because the image is not changed by passing through it.  Usually the factor is the value derived from adding all the values in the matrix together, which ensures the end value will be in the range 0-255.  Where this is not the case, for example, in an embossing filter where the values add up to 0, an offet of 127 is common.  I should also mention that convolution filters come in a variety of sizes, 7x7 is not unheard of, and edge detection filters in particular are not symmetrical.  Also, the bigger the filter, the more pixels we cannot process, as we cannot process pixels that do not have the number of surrounding pixels our matrix requires.  In our case, the outer edges of the image to a depth of one pixel will go unprocessed.

A Framework

First of all we need to establish a framework from which to write these filters, otherwise we'll find ourselves writing the same code over and again.  As our filter now relies on surrounding values to get a result, we are going to need a source and a destination bitmap.  I tend to create a copy of the bitmap coming in and use the copy as the source, as it is the one getting discarded in the end.  To facilitate this, I define a matrix class as follows:

public class ConvMatrix
{
    public int TopLeft = 0, TopMid = 0, TopRight = 0;
    public int MidLeft = 0, Pixel = 1, MidRight = 0;
    public int BottomLeft = 0, BottomMid = 0, BottomRight = 0;
    public int Factor = 1;
    public int Offset = 0;
    public void SetAll(int nVal)
    {
        TopLeft = TopMid = TopRight = MidLeft = Pixel = MidRight = 
                  BottomLeft = BottomMid = BottomRight = nVal;
    }
}

I'm sure you noticed that it is an identity matrix by default.  I also define a method that sets all the elements of the matrix to the same value.

The pixel processing code is more complex than our last article, because we need to access nine pixels, and two bitmaps.  I do this by defining constants for jumping one and two rows ( because we want to avoid calculations as much as possible in the main loop, we define both instead of adding one to itself, or multiplying it by 2 ).  We can then use these values to write our code.  As our initial offset into the different color is 0, 1, and 2, we end up with 3 and 6 added to each of those values to create indices for three pixels across, and use our constants to add the rows.  In order to ensure we don't have any values jumping from the bottom of the image to the top, we need to create one int, which is used to calculate each pixel value, then clamped and stored.  Here is the entire function:

public static bool Conv3x3(Bitmap b, ConvMatrix m)
{
    // Avoid divide by zero errors
    if (0 == m.Factor)
        return false; Bitmap 
    
    // GDI+ still lies to us - the return format is BGR, NOT RGB. 
    bSrc = (Bitmap)b.Clone();
    BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), 
                        ImageLockMode.ReadWrite, 
                        PixelFormat.Format24bppRgb); 
    BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), 
                       ImageLockMode.ReadWrite, 
                       PixelFormat.Format24bppRgb); 
    int stride = bmData.Stride; 
    int stride2 = stride * 2; 

    System.IntPtr Scan0 = bmData.Scan0; 
    System.IntPtr SrcScan0 = bmSrc.Scan0; 

    unsafe { 
        byte * p = (byte *)(void *)Scan0; 
        byte * pSrc = (byte *)(void *)SrcScan0; 
        int nOffset = stride - b.Width*3; 
        int nWidth = b.Width - 2; 
        int nHeight = b.Height - 2; 
        
        int nPixel; 
        
        for(int y=0;y < nHeight;++y) 
        { 
            for(int x=0; x < nWidth; ++x ) 
            {
                nPixel = ( ( ( (pSrc[2] * m.TopLeft) + 
                    (pSrc[5] * m.TopMid) + 
                    (pSrc[8] * m.TopRight) + 
                    (pSrc[2 + stride] * m.MidLeft) + 
                    (pSrc[5 + stride] * m.Pixel) + 
                    (pSrc[8 + stride] * m.MidRight) + 
                    (pSrc[2 + stride2] * m.BottomLeft) + 
                    (pSrc[5 + stride2] * m.BottomMid) + 
                    (pSrc[8 + stride2] * m.BottomRight)) 
                    / m.Factor) + m.Offset); 
                    
                if (nPixel < 0) nPixel = 0; 
                if (nPixel > 255) nPixel = 255; 
                p[5 + stride]= (byte)nPixel; 
                
                nPixel = ( ( ( (pSrc[1] * m.TopLeft) + 
                    (pSrc[4] * m.TopMid) + 
                    (pSrc[7] * m.TopRight) + 
                    (pSrc[1 + stride] * m.MidLeft) + 
                    (pSrc[4 + stride] * m.Pixel) + 
                    (pSrc[7 + stride] * m.MidRight) + 
                    (pSrc[1 + stride2] * m.BottomLeft) + 
                    (pSrc[4 + stride2] * m.BottomMid) + 
                    (pSrc[7 + stride2] * m.BottomRight)) 
                    / m.Factor) + m.Offset); 
                    
                if (nPixel < 0) nPixel = 0; 
                if (nPixel > 255) nPixel = 255; 
                p[4 + stride] = (byte)nPixel; 
                
                nPixel = ( ( ( (pSrc[0] * m.TopLeft) + 
                               (pSrc[3] * m.TopMid) + 
                               (pSrc[6] * m.TopRight) + 
                               (pSrc[0 + stride] * m.MidLeft) + 
                               (pSrc[3 + stride] * m.Pixel) + 
                               (pSrc[6 + stride] * m.MidRight) + 
                               (pSrc[0 + stride2] * m.BottomLeft) + 
                               (pSrc[3 + stride2] * m.BottomMid) + 
                               (pSrc[6 + stride2] * m.BottomRight)) 
                    / m.Factor) + m.Offset); 
                    
                if (nPixel < 0) nPixel = 0; 
                if (nPixel > 255) nPixel = 255; 
                p[3 + stride] = (byte)nPixel; 
                
                p += 3; 
                pSrc += 3; 
            } 
            
            p += nOffset; 
            pSrc += nOffset; 
        } 
    } 
    
    b.UnlockBits(bmData); 
    bSrc.UnlockBits(bmSrc); 
    return true; 
}

Not the sort of thing you want to have to write over and over, is it ? Now we can use our ConvMatrix class to define filters, and just pass them into this function, which does all the gruesome stuff for us.

Smoothing

Given what I've told you about the mechanics of this filter, it is obvious how we create a smoothing effect.  We ascribe values to all our pixels, so that the weight of each pixel is spread over the surrounding area.   The code looks like this:

public static bool Smooth(Bitmap b, int nWeight /* default to 1 */)
{
    ConvMatrix m = new ConvMatrix();
    m.SetAll(1);
    m.Pixel = nWeight;
    m.Factor = nWeight + 8;

    return  BitmapFilter.Conv3x3(b, m);
}

As you can see, it's simple to write the filters in the context of our framework.  Most of these filters have at least one parameter, unfortunately C# does not have default values, so I put them in a comment for you.  The net result of apply this filter several times is as follows:

My daughter, Hannah      She's so smooth

Gaussian Blur

Gaussian Blur filters locate significant color transitions in an image, then create intermediary colors to soften the edges.  The filter looks like this:

Gaussian Blur
1 2 1
2 4 2
1 2 1 /16+0

The middle value is the one you can alter with the filter provided, you can see that the default value especially makes for a circular effect, with pixels given less weight the further they go from the edge.  In fact, this sort of smoothing generates an image not unlike an out of focus lens.

My daughter, Hannah      Gaussian Blur

Sharpen

On the other end of the scale, a sharpen filter looks like this:

Sharpen
0 -2 0
-2 11 -2
0 -2 0 /3+0

If you compare this to the gaussian blur you'll note it's almost an exact opposite. It sharpens an image by enhancing the difference between pixels. The greater the difference between the pixels that are given a negative weight and the pixel being modified, the greater the change in the main pixel value.  The degree of sharpening can be adjusted by changing the centre weight.  To show the effect better I have started with a blurred picture for this example.

My daughter, Hannah      Gaussian Blur, then Sharpened

Mean Removal

The Mean Removal filter is also a sharpen filter, it looks like this:

Mean Removal
-1 -1 -1
-1 9 -1
-1 -1 -1 /1+0

Unlike the previous filter, which only worked in the horizontal and vertical directions, this one spreads it's influence diagonally as well, with the following result on the same source image.  Once again, the central value is the one to change in order to change the degree of the effect.

My daughter, Hannah      Gaussian Blur, then Mean Removal

Embossing

Probably the most spectacular filter you can do with a convolution filter is embossing.  Embossing is really just an edge detection filter.  I'll cover another simple edge detection filter after this and you'll notice it's quite similar.  Edge detection generally works by offsetting a positive and a negative value across an axis, so that the greater the difference between the two pixels, the higher the value returned.  With an emboss filter, because our filter values add to 0 instead of 1, we use an offset of 127 to brighten the image, otherwise much of it would clamp to black.

The filter I have implemented looks like this:

Emboss Laplascian
-1 0 -1
0 4 0
-1 0 -1 /1+127

and it looks like this:

My daughter, Hannah      Hannah Embossed

As you might have noticed, this emboss works in both diagonal directions.  I've also included a custom dialog where you can enter your own filters, you might like to try some of these for embossing:

Horz/Vertical
0 -1 0
-1 4 -1
0 -1 0 /1+127
All Directions
-1 -1 -1
-1 8 -1
-1 -1 -1 /1+127
Lossy
1 -2 1
-2 4 -2
-2 1 -2 /1+127
Horizontal Only
0 0 0
-1 2 -1
0 0 0 /1+127
Vertical Only
0 -1 0
0 0 0
0 1 0 /1+127

The horizontal and vertical only filters differ for no other reason than to show two variations.  You can actually rotate these filters as well, by rotating the values around the central point.  You'll notice the filter I have used is the horz/vertical filter rotated by one degree, for example. 

Let's not get carried away

Although this is kinda cool, you will notice if you run Photoshop that it offers a lot more functionality than the emboss I've shown you here.  Photoshop creates an emboss using a more specifically written filter, and only part of that functionality can be simulated using convolution filters.  I have spent some time writing a more flexible emboss filter, once we've covered bilinear filtering and the like, I may write an article on a more complete emboss filter down the track.

Edge Detection

Finally, just a simple edge detection filter, as a foretaste of the next article, which will explore a number of ways to detect edges.  The filter looks like this:

Edge Detect
1 1 1
0 0 0
-1 -1 -1 /1+127

Like all edge detection filters, this filter is not concerned with the value of the pixel being examined, but rather in the difference between the pixels surrounding it.  As it stands it will detect a horizontal edge, and, like the embossing filters, can be rotated.  As I said before, the embossing filters are essentially doing edge detection, this one just heightens the effect. 

 

My daughter, Hannah      Hannahs Edges

What's in store

The next article will be covering a variety of edge detection methods.  I'd also encourage you to search the web for convolution filters.  The comp.graphics.algorithms newsgroup tends to lean towards 3D graphics, but if you search an archive like google news for 'convolution' you'll find plenty more ideas to try in the custom dialog.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Christian Graus
Software Developer (Senior)
Australia Australia
Member
Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and now I work for the new owners.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberthezollie3 May '13 - 2:45 
This is good, but i think, you would have to use floats...
Beacause of Median filter, you need it. I wrote it to CUDA enabled gpu Smile | :)
QuestionGreat article ... butmemberM i s t e r L i s t e r10 Feb '13 - 6:50 
Christian.
 
Loved the article and wanted to run the sample project... but I am on Windows 8 and didn't want to download the .net 3.5 framework.
 
Are you going to update for Windows 8 ??
AnswerRe: Great article ... butmvpChristian Graus10 Feb '13 - 9:59 
http://msdn.microsoft.com/en-AU/library/hh506443.aspx[^] Also, you can just download the source code and build it for .NET 4.5.
Christian Graus
 
Driven to the arms of OSX by Vista.
 
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.

QuestionHow to use the library to create stereo images from a depth map and a related image?memberLarissa Schön3 May '12 - 12:16 
Hello!
 
I have a depthmap and a fitting picture. I want to displace the picture according to the depthmap to the left for picture1 and to the right for picture2 for them to be viewed using a stereo viewer/anaglyph.
 
How would you do that using your library?
AnswerRe: How to use the library to create stereo images from a depth map and a related image?mvpChristian Graus3 May '12 - 12:52 
I'm not sure you could use more than the basic code for how to access pixels and change them, to do this. I would expect it's a specialised task, as I assume you're going to offset individual pixels based on their depth, and then have to decide which pixel takes priority when two get displaced to the same location.
Christian Graus
 
Driven to the arms of OSX by Vista.
 
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.

GeneralRe: How to use the library to create stereo images from a depth map and a related image?memberLarissa Schön4 May '12 - 0:32 
<i>and then have to decide which pixel takes priority when two get displaced to the same location.</i>
 
Which way of doing that is the most efficient?
GeneralMy vote of 5membermanoj kumar choubey20 Feb '12 - 21:03 
Nice
QuestionConvolution Matrix Class doing nothing?memberMember 856784721 Jan '12 - 6:27 
Hello
 
I am attempting to create a convolution matrix function in c# that will take in a bitmap, an array, and a weight integer and output a bitmap. The idea is an expansion of the code listed
here
[^]
 
No matter what I do, I cannot seem to get the function to return anything. Help?
private static Bitmap ConvolutionMatrix3x3(Bitmap InputImage, int[,] Matrix, int MatrixOffset)
        {
            int Width = InputImage.Width;
            int Height = InputImage.Height;
            
            Bitmap InputImageSrc = (Bitmap)InputImage.Clone();
            BitmapData InputImageData = InputImage.LockBits(new Rectangle(0, 0, InputImage.Width, InputImage.Height),
                                ImageLockMode.ReadWrite,
                                PixelFormat.Format24bppRgb);
            BitmapData InputImageSourceData = InputImageSrc.LockBits(new Rectangle(0, 0, InputImageSrc.Width, InputImageSrc.Height),
                               ImageLockMode.ReadWrite,
                               PixelFormat.Format24bppRgb);
            int stride = InputImageData.Stride;
            int stride2 = stride * 2;
            int Length = InputImageData.Stride * InputImageData.Height;
            byte[] stream = new byte[Length];
            System.IntPtr Scan0 = InputImageData.Scan0;
            System.IntPtr ReserveScan0 = InputImageData.Scan0;
            System.IntPtr SrcScan0 = InputImageSourceData.Scan0;
 
            Bitmap ReturnBitmap = new Bitmap(InputImage.Width, InputImage.Height);
            BitmapData ReturnBitmapData = ReturnBitmap.LockBits(new Rectangle(0, 0, InputImage.Width, InputImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
            
            
            
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                byte** Reserve = &p;
                byte* pSrc = (byte*)(void*)SrcScan0;
                int nOffset = stride - InputImage.Width * 3;
                int nWidth = InputImage.Width - 2;
                int nHeight = InputImage.Height - 2;
                
                int nPixel;
 
                for (int y = 0; y < nHeight; ++y)
                {
                    for (int x = 0; x < nWidth; ++x)
                    {
                        nPixel = (((pSrc[2] * Matrix[0,0]) +
                            (pSrc[5] * Matrix[1,0]) +
                            (pSrc[8] * Matrix[2,0]) +
                            (pSrc[2 + stride] * Matrix[0,1]) +
                            (pSrc[5 + stride] * Matrix[1,1]) +
                            (pSrc[8 + stride] * Matrix[2,1]) +
                            (pSrc[2 + stride2] * Matrix[0,2]) +
                            (pSrc[5 + stride2] * Matrix[1,2]) +
                            (pSrc[8 + stride2] * Matrix[2,2]))
                            / (Matrix[0,0] + Matrix[0,1] + Matrix[0,2] + 
                               Matrix[1,0] + Matrix[1,1] + Matrix[1,2] + 
                               Matrix[2,0] + Matrix[2,1] + Matrix[2,2] ) + MatrixOffset);
 
                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[5 + stride] = (byte)nPixel;
 
                        nPixel = (((pSrc[1] * Matrix[0, 0]) +
                            (pSrc[4] * Matrix[1, 0]) +
                            (pSrc[7] * Matrix[2, 0]) +
                            (pSrc[1 + stride] * Matrix[0, 1]) +
                            (pSrc[4 + stride] * Matrix[1, 1]) +
                            (pSrc[7 + stride] * Matrix[2, 1]) +
                            (pSrc[1 + stride2] * Matrix[0, 2]) +
                            (pSrc[4 + stride2] * Matrix[1, 2]) +
                            (pSrc[7 + stride2] * Matrix[2, 2]))
                            / (Matrix[0, 0] + Matrix[0, 1] + Matrix[0, 2] +
                               Matrix[1, 0] + Matrix[1, 1] + Matrix[1, 2] +
                               Matrix[2, 0] + Matrix[2, 1] + Matrix[2, 2]) + MatrixOffset);
 
                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[4 + stride] = (byte)nPixel;
 
                        nPixel = (((pSrc[0] * Matrix[0, 0]) +
                            (pSrc[3] * Matrix[1, 0]) +
                            (pSrc[6] * Matrix[2, 0]) +
                            (pSrc[0 + stride] * Matrix[0, 1]) +
                            (pSrc[3 + stride] * Matrix[1, 1]) +
                            (pSrc[6 + stride] * Matrix[2, 1]) +
                            (pSrc[0 + stride2] * Matrix[0, 2]) +
                            (pSrc[3 + stride2] * Matrix[1, 2]) +
                            (pSrc[6 + stride2] * Matrix[2, 2]))
                            / (Matrix[0, 0] + Matrix[0, 1] + Matrix[0, 2] +
                               Matrix[1, 0] + Matrix[1, 1] + Matrix[1, 2] +
                               Matrix[2, 0] + Matrix[2, 1] + Matrix[2, 2]) + MatrixOffset);
 
                        if (nPixel < 0) nPixel = 0;
                        if (nPixel > 255) nPixel = 255;
                        p[3 + stride] = (byte)nPixel;
 
                        p += 3;
                        pSrc += 3;
                    }
 
                    p += nOffset;
                    pSrc += nOffset;
                }
                System.IntPtr Reserve2 = (IntPtr)Reserve;
                ReturnBitmapData.Scan0 = Reserve2;
                for (int n = 0; n < Length; n++)
                {
                    Marshal.WriteByte(ReturnBitmapData.Scan0, n, stream[n]);
                }
            
            }
            ReturnBitmap.UnlockBits(ReturnBitmapData);
            InputImage.UnlockBits(InputImageData);
            InputImageSrc.UnlockBits(InputImageSourceData);
            return ReturnBitmap; 
        }

QuestionProblem in image processing...Can you help me..?memberKiranVaghela20 Dec '11 - 20:31 
Hello Graus, I am Kiran. I have a problem in image processing. Your article is very excellent, but I can not modify it to fulfill my requirement. I have attache one image, I want to remove the dots from the image and other black portion and clean the image. Is it possible? How I can send an image...
Kiran Vaghela

AnswerRe: Problem in image processing...Can you help me..?mvpChristian Graus21 Dec '11 - 8:01 
There's no way to send an image through the site. How big are the dots ? There's a noise removal article in this series, it may do what you want. Black and white dots are called 'salt and pepper' noise. The way you remove them, is to use a blend of the surrounding colors to replace sudden instances of extreme black or white. It's a smoothing filter, so it will blur the whole image a little, you can make it adaptive to only work where it notices noise, but I don't really cover that.
Christian Graus
 
Driven to the arms of OSX by Vista.
 
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.

GeneralMy vote of 4membermarekbar21820 Dec '11 - 8:42 
Hi, If I want to calculate factor then I should add all ingredients of kernel ?
I see that you add sometimes a number to the sum - what is it?
QuestionRegarding the Code Reuse in Commercial ApplicationmemberMember 821674031 Oct '11 - 7:58 
Dear Christian Graus,
 
Please Specify the Licensing for this Code.Can i reuse this code for a commercial application.Expecting to hear from you Soon.
 
Thank You.
AnswerRe: Regarding the Code Reuse in Commercial ApplicationmvpChristian Graus31 Oct '11 - 10:06 
You can use this code anywhere you like, I don't even care if you mention where it came from.
Christian Graus
 
Driven to the arms of OSX by Vista.
 
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.

QuestionHow to apply feather effect around the shape inside Image?memberdeepak_rai5 Feb '11 - 2:22 
Hi,
 
Can anybody help me How to apply the feather effect around a shape which is inside the Image?
 
Thanks
Deepak Rai.
QuestionLicense?memberkiddailey27 Nov '10 - 21:24 
What is the licensing for this code? Looked through all the posts and didn't see anyone asking the question or a mention of it, so thought I'd ask.
AnswerRe: License?mvpChristian Graus10 Feb '13 - 10:00 
Sorry, you can use this code anywhere you like, in any form you like.
Christian Graus
 
Driven to the arms of OSX by Vista.
 
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.

GeneralThanks! Great code!membershuvozula27 May '10 - 7:36 
After looking at your code, I realized how inefficient my code was. I love your Conv3x3 function. Made my life a lot easier after I followed the way you have implemented your code Big Grin | :-D
QuestionHow to frame a photo?memberfgoldenstein20 Mar '10 - 14:15 
Very nice article! I'm trying to develop a photo editor software for a photo proccessing store. I was asked to develop a function to frame a photo with different styles and colors. Can you help me? do you know any open source for photo printing?
Thanks in advance.
QuestionFinal BitmapmemberMatheusMK327 Nov '09 - 6:53 
Very nice, but, how do i extract the final Bitmap???
 
public void showSignature(object sender, MyForumSignatureEventArgs e)
{
signatureLabel.Text = e.Signature("MatheusMK3", "codeproject.com", "Forum");
}

Generalhelp regarding Sharpness and Smoothing with Scroll Barmemberprashant_14412 Nov '09 - 2:39 
Very good Article.
 
Hi i am creating function for Sharpen & Smoothing.
I make some changes on your filter matrix.
I googled and found some filter matrix for sharpen.All are performing fine.
So i want to ask you which is good and true.
I tried below matrix.
[0 -2 0] with dividing factor 8.
[-2 11 -2]
[0 -2 0]
 
[0 -1 0] with dividing factor 4.
[-1 5 -1]
[0 -1 0]
 
1. Can i apply here Floating point values?
2. I want to use it with scroll bar like below link example have?
So what value i have to change from matrix for that?
 
Click here for example
Please help...
prashant
 
adad

GeneralLarge image issuesmembertlhintoq23 Oct '09 - 17:31 
Consistently images of 1000x1000 work great.
Consistently images of 3400x4000 break at:
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
With a generic GDI+ error.
 
Has anyone else seen such behavior or know a cause or better yet, a fix?
GeneralFunction Conv3x3 has a BUG!!! [modified]memberMember 349875913 Sep '09 - 13:25 
There is a bug in Conv3x3(Bitmap b, ConvMatrix m) function
 
It assumes it should not go beyond the edge of the image by doing this:
int nWidth = b.Width - 2;
int nHeight = b.Height - 2;
 
int nPixel;
 
for(int y=0;y &lt; nHeight;++y)
{
  for(int x=0; x &lt; nWidth; ++x )
  {
  ...
where 2 stands for the remaining 2 pixels of the matrix. This is good but at the end of each row we see
p += nOffset;
pSrc += nOffset;
to correct for the offset but we don't see
p += 6;
pSrc += 6;
to correct for the 2 matrix pixels mentioned before (2 pixels by 3 bytes = 6)
 
so the right function is:
public static bool Conv3x3(Bitmap b, ConvMatrix m)
{
  // Avoid divide by zero errors
  if (0 == m.Factor) return false;
 
  Bitmap bSrc = (Bitmap)b.Clone(); 
 
  // GDI+ still lies to us - the return format is BGR, NOT RGB.
  BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 
  int stride = bmData.Stride;
  int stride2 = stride * 2;
  System.IntPtr Scan0 = bmData.Scan0;
  System.IntPtr SrcScan0 = bmSrc.Scan0;
 
  unsafe
  {
    byte * p = (byte *)(void *)Scan0;
    byte * pSrc = (byte *)(void *)SrcScan0;
 
    int nOffset = stride - b.Width*3;
    int nWidth = b.Width - 2;
    int nHeight = b.Height - 2;
 
    int nPixel;
 
    for(int y=0;y < nHeight;++y)
    {
      for(int x=0; x < nWidth; ++x )
      {
        nPixel = ( ( ( (pSrc[2] * m.TopLeft) + (pSrc[5] * m.TopMid) + (pSrc[8] * m.TopRight) +
          (pSrc[2 + stride] * m.MidLeft) + (pSrc[5 + stride] * m.Pixel) + (pSrc[8 + stride] * m.MidRight) +
          (pSrc[2 + stride2] * m.BottomLeft) + (pSrc[5 + stride2] * m.BottomMid) + (pSrc[8 + stride2] * m.BottomRight)) / m.Factor) + m.Offset); 
 
        if (nPixel < 0) nPixel = 0;
        if (nPixel > 255) nPixel = 255;
 
        p[5 + stride]= (byte)nPixel;
 
        nPixel = ( ( ( (pSrc[1] * m.TopLeft) + (pSrc[4] * m.TopMid) + (pSrc[7] * m.TopRight) +
          (pSrc[1 + stride] * m.MidLeft) + (pSrc[4 + stride] * m.Pixel) + (pSrc[7 + stride] * m.MidRight) +
          (pSrc[1 + stride2] * m.BottomLeft) + (pSrc[4 + stride2] * m.BottomMid) + (pSrc[7 + stride2] * m.BottomRight)) / m.Factor) + m.Offset); 
 
        if (nPixel < 0) nPixel = 0;
        if (nPixel > 255) nPixel = 255;
          
        p[4 + stride] = (byte)nPixel;
 
        nPixel = ( ( ( (pSrc[0] * m.TopLeft) + (pSrc[3] * m.TopMid) + (pSrc[6] * m.TopRight) +
          (pSrc[0 + stride] * m.MidLeft) + (pSrc[3 + stride] * m.Pixel) + (pSrc[6 + stride] * m.MidRight) +
          (pSrc[0 + stride2] * m.BottomLeft) + (pSrc[3 + stride2] * m.BottomMid) + (pSrc[6 + stride2] * m.BottomRight)) / m.Factor) + m.Offset); 
 
        if (nPixel < 0) nPixel = 0;
        if (nPixel > 255) nPixel = 255;
 
        p[3 + stride] = (byte)nPixel;
        p += 3;
        pSrc += 3;
      }
      p += 6;
      pSrc += 6;
 

      p += nOffset;
      pSrc += nOffset;
    }
  }
 
  b.UnlockBits(bmData);
  bSrc.UnlockBits(bmSrc);
 
  return true;
}

 
The problem of the current function is that it will apply the matrix of the most right image pixels column and most left column, while those to columns are not next to each other. Another side effect of this bug is that if the image is big the number of columns will accumulate and at the end we may have several rows at the bottom of not processed pixels.
 
modified on Sunday, September 13, 2009 7:36 PM

Generaltransparent ImagesmemberJbscoelho4 Aug '09 - 0:58 
Hi,
 
How can i change images with transparent background like PNG files!???
 
How can i detect if a certain pixel got alpha channel???
 
hope u can really help me...
 
By the way.. great coding... was very helpfull for my understand and coding in VB.net.
 
best regards,
 
Bruno Coelho
QuestionHelp in Convolution Matrix 5x5.memberAmila/1718 Mar '09 - 1:44 
Hi,
 
I need a small help in my work. I managed to code the convolution matrix of 5x5 Smile and implement it. It runs perfectly except for one problem. The end result of the image turns to be a shade of red. Please could anyone tell me what this problem is caused by and how I can solve it.
 
Thank You in advance.
 
TC
GeneralHelp in Convolution Matrix 5x5.memberAmila/1718 Mar '09 - 1:42 
Hi,
 
I need a small help in my work. I managed to code the convolution matrix of 5x5 Smile | :) and implement it. It runs perfectly except for one problem. The end result of the image turns to be a shade of red. Please could anyone tell me what this problem is caused by and how I can solve it.
 
Thank You in advance.
 
TC

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 7 Nov 2005
Article Copyright 2002 by Christian Graus
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid