1

I made a program that implements an edge detection algorithm, but it takes a long time to process. I've read about using lockbits, and unsafe state instead of getpixel and setpixel, but I still don't understand how to use it.

This is my example code:

private Bitmap SobelEdgeDetect(Bitmap original)
        {
            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);


                    else
                        bb.SetPixel(i, j, Color.Transparent);
                }
            }
            return bb;
        }

I am using the fastbitmap class, which I implement like this:

private Bitmap SobelEdgeDetectTwo(Bitmap original)
        {
            int width = original.Width;
            int height = original.Height;
            Bitmap result = new Bitmap(width,height);
            FastBitmap b = new FastBitmap(original);
            FastBitmap bb = new FastBitmap(result);

            b.LockBitmap();
            bb.LockBitmap();

            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++)
                {
                    var pixel = b.GetPixel(i,j);
                    allPixR[i, j] = pixel.Red;
                    allPixG[i, j] = pixel.Green;
                    allPixB[i, j] = pixel.Blue;

                }
            }

            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 < width - 1; i++)
            {
                for (int j = 1; j < 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)
                    {
                        PixelData p = new PixelData(Color.Black);
                        bb.SetPixel(i, j, p);

                    }
                    else
                    {
                        PixelData p = new PixelData(Color.Transparent);
                        bb.SetPixel(i, j, p);

                    }

                }
            }


            b.UnlockBitmap();
            bb.UnlockBitmap();

            return result;
        }

However, the image doesn't change at all. Could you give me advice about which part of my code is wrong?

Jim Simson
  • 2,774
  • 3
  • 22
  • 30
christ2702
  • 473
  • 6
  • 12
  • 26

2 Answers2

4

It's easiest to use a class like FastBitmap. Just add a FastBitmap and use GetPixel() on that class instead of on your Bitmap, the rest can be the same.

Something like this:

Bitmap dstBmp = new Bitmap(width, height, original.PixelFormat);
FastBitmap fastBitmap = new FastBitmap(dstBmp);
fastBitmap.LockBitmap();
//...
var pixel = fastBitmap.GetPixel(x,y);
//...
fastBitmap.UnlockBitmap();
Carra
  • 17,808
  • 7
  • 62
  • 75
  • +1. Thanks for FastBitmap. Always used LockBits manually after discovered the terrible slow down of Bitmap.GetPixel – Samuel May 25 '13 at 08:14
  • I made a program that draws fractals a while ago and discovered that yes, Set and GetPixel are incredibly slow. But it's easy to get around it with a class like FastBitmap, and it hides the pointer logic :) – Carra May 25 '13 at 08:17
  • i've solved my problem about the error. i can implement the FastBitmap class to my function above, but the image doesn't change at all. – christ2702 May 25 '13 at 11:00
  • How can i set the transparent color? – christ2702 May 26 '13 at 07:38
  • 1
    i tested it stopwatch . before 600 ms to 1100 ms . now its 60 to 100ms. **this fastbitmap.cs 10xFASTER than normal bitmap**. the code run before i press F5 !! thank you. .you may want to change Red , Green Blue to R , G B in the Fastbitmap.cs if you dont wanna change your code – bh_earth0 Nov 01 '15 at 12:53
3

Ok, let's see what we can do - a quick Google found this, which can be simply adapted to your function something like this

private Bitmap SobelEdgeDetect(Bitmap original)
{
    int width = original.Width;
    int height = original.Height;

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

    BitmapData bmpData = original.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, original.PixelFormat);
    int position;
    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 } };
    byte Threshold = 128;

    Bitmap dstBmp = new Bitmap(width, height, original.PixelFormat);
    BitmapData dstData = dstBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, dstBmp.PixelFormat);

    unsafe
    {
        byte* ptr = (byte*)bmpData.Scan0.ToPointer();
        byte* dst = (byte*)dstData.Scan0.ToPointer();

        for (int i = 1; i < height - 1; i++)
        {
            for (int j = 1; j < width - 1; j++)
            {
                int NewX = 0, NewY = 0;

                for (int ii = 0; ii < 3; ii++)
                {
                    for (int jj = 0; jj < 3; jj++)
                    {
                        int I = i + ii - 1;
                        int J = j + jj - 1;
                        byte Current = *(ptr + (I * width + J) * OneColorBits);
                        NewX += gx[ii, jj] * Current;
                        NewY += gy[ii, jj] * Current;
                    }
                }
                position = ((i * width + j) * OneColorBits);
                if (NewX * NewX + NewY * NewY > Threshold * Threshold)
                    dst[position] = dst[position + 1] = dst[position + 2] = 255;
                else
                    dst[position] = dst[position + 1] = dst[position + 2] = 0;
            }
        }
    }
    original.UnlockBits(bmpData);
    dstBmp.UnlockBits(dstData);

    return dstBmp;
}

It's not complete copy/paste solution but you should be able to see how the original author is accessing the pixel data by using LockBits in exactly the way you need. The rest is up to you ;-)

You will need to set the unsafe option in your project properties as I explained in my answer to your previous question.

Community
  • 1
  • 1
Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
  • finally, i can implement it. but, i always get the 'white' color instead of 'transparent'. can you help me how to fix this? i want to use Color.Transparent in the image result – christ2702 May 26 '13 at 07:43