Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest Sobel Edge Detection C#

I want to make a program that implements sobel edge detection. This is my code :

private Bitmap SobelEdgeDetect(Bitmap ori)
{
        Bitmap b = original;
        Bitmap bb = original;
        int width = b.Width;
        int height = b.Height;
        int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
        int[,] gy = new int[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };

        int[,] allPixR = new int[width, height];
        int[,] allPixG = new int[width, height];
        int[,] allPixB = new int[width, height];

        int limit = 128 * 128;

        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                allPixR[i, j] = b.GetPixel(i, j).R;
                allPixG[i, j] = b.GetPixel(i, j).G;
                allPixB[i, j] = b.GetPixel(i, j).B;
            }
        }

        int new_rx = 0, new_ry = 0;
        int new_gx = 0, new_gy = 0;
        int new_bx = 0, new_by = 0;
        int rc, gc, bc;
        for (int i = 1; i < b.Width - 1; i++)
        {
            for (int j = 1; j < b.Height - 1; j++)
            {

                new_rx = 0;
                new_ry = 0;
                new_gx = 0;
                new_gy = 0;
                new_bx = 0;
                new_by = 0;
                rc = 0;
                gc = 0;
                bc = 0;

                for (int wi = -1; wi < 2; wi++)
                {
                    for (int hw = -1; hw < 2; hw++)
                    {
                        rc = allPixR[i + hw, j + wi];
                        new_rx += gx[wi + 1, hw + 1] * rc;
                        new_ry += gy[wi + 1, hw + 1] * rc;

                        gc = allPixG[i + hw, j + wi];
                        new_gx += gx[wi + 1, hw + 1] * gc;
                        new_gy += gy[wi + 1, hw + 1] * gc;

                        bc = allPixB[i + hw, j + wi];
                        new_bx += gx[wi + 1, hw + 1] * bc;
                        new_by += gy[wi + 1, hw + 1] * bc;
                    }
                }
                if (new_rx * new_rx + new_ry * new_ry > limit || new_gx * new_gx + new_gy * new_gy > limit || new_bx * new_bx + new_by * new_by > limit)
                    bb.SetPixel(i, j, Color.Black);

                //bb.SetPixel (i, j, Color.FromArgb(allPixR[i,j],allPixG[i,j],allPixB[i,j]));
                else
                    bb.SetPixel(i, j, Color.Transparent);
            }
        }
        return bb;

  }

i want to using lockbits so my program can run faster, but i actually still not understand how to use it. could anyone give some explanation or example code?

like image 225
christ2702 Avatar asked Mar 24 '23 22:03

christ2702


1 Answers

You really need to use LockBits instead of GetPixel and SetPixel.

So you create a BitmapData object that contains all of the pixel data:

// lock the input bitmap's bits  
System.Drawing.Imaging.BitmapData bmpData =
    original.LockBits(new Rectangle(0, 0, original.Width, original.Height), 
    System.Drawing.Imaging.ImageLockMode.Read, original.PixelFormat);

Then, you can get the address of the first scan line (i.e. the first row of pixels):

IntPtr ptr = bmpData.Scan0;

Now you have two choices. If you're happy to mark your function as unsafe then you can access the pixels directly using pointer arithmetic, like this

byte* pPixels = (byte*)ptr.ToPointer();

which gives you a pointer to the first byte of an RGB pixel (assuming 24bpp). Then you can access an individual pixel at (x,y) using pointer arithmetic. First you determine how many bytes per pixel (if you don't already know)

int nBytesPerPixel = Image.GetPixelFormatSize(original.PixelFormat) / 8;

Then calculate the index to the pixel you want

byte* pPixelAtXY = pPixels + (y * bmpData.Stride) + (x * nBytesPerPixel);

This gives you unsafe access to the pixels in a Bitmap which you can do for the input and output bitmaps for fastest speed. Note that to use unsafe code, you need to mark your function as unsafe and edit your project properties.

If you don't want to use unsafe code, you can still speed things up by copying all of the pixel data to a byte array before processing and then copy it back afterwards. As the MSDN example showed

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap. 
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];

// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

// Set every third value to 255. A 24bpp bitmap will look red.   
for (int counter = 2; counter < rgbValues.Length; counter += 3)
    rgbValues[counter] = 255;

// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

Whichever method you use, when you have finished with the pixel data, you must release it using UnlockBits

original.UnlockBits(bmpData);
like image 120
Roger Rowland Avatar answered Apr 02 '23 00:04

Roger Rowland