【问题标题】:Edge Detection with Lockbits C#使用 Lockbits C# 进行边缘检测
【发布时间】:2013-05-25 06:59:42
【问题描述】:

我做了一个实现边缘检测算法的程序, 但是需要很长时间来处理。 我读过关于使用 lockbits 和 unsafe state 而不是 getpixel 和 setpixel,但我仍然不明白如何使用它。

这是我的示例代码:

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;
        }

我正在使用 fastbitmap 类,我是这样实现的:

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;
        }

但是,图像根本没有改变。 你能给我建议一下我的代码的哪一部分是错误的吗?

【问题讨论】:

    标签: c# image-processing edge-detection lockbits


    【解决方案1】:

    使用像FastBitmap 这样的类是最简单的。只需添加一个 FastBitmap 并在该类而不是您的位图上使用 GetPixel(),其余部分可以相同。

    类似这样的:

    Bitmap dstBmp = new Bitmap(width, height, original.PixelFormat);
    FastBitmap fastBitmap = new FastBitmap(dstBmp);
    fastBitmap.LockBitmap();
    //...
    var pixel = fastBitmap.GetPixel(x,y);
    //...
    fastBitmap.UnlockBitmap();
    

    【讨论】:

    • +1。感谢 FastBitmap。在发现 Bitmap.GetPixel 的可怕速度变慢后,总是手动使用 LockBits
    • 我不久前做了一个绘制分形的程序,发现是的,Set 和 GetPixel 非常慢。但是使用 FastBitmap 之类的类很容易绕过它,并且它隐藏了指针逻辑:)
    • 我已经解决了我的错误问题。我可以在上面的函数中实现 FastBitmap 类,但图像根本没有改变。
    • 如何设置透明色?
    • 我测试了秒表。在 600 毫秒到 1100 毫秒之前。现在是 60 到 100 毫秒。 这个 fastbitmap.cs 比普通位图快 10 倍。代码在我按 F5 之前运行!谢谢你。 .如果您不想更改代码,您可能想在 Fastbitmap.cs 中将 Red 、 Green Blue 更改为 R 、 G B
    【解决方案2】:

    好的,让我们看看我们能做什么 - 一个快速的谷歌找到了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;
    }
    

    这不是完整的复制/粘贴解决方案,但您应该能够以您需要的方式使用LockBits 查看原作者如何访问像素数据。剩下的就看你了;-)

    您需要在项目属性中设置unsafe 选项,正如我在my answer 中对您之前的问题所解释的那样。

    【讨论】:

    • 终于,我可以实现它了。但是,我总是得到“白色”而不是“透明”。你能帮我解决这个问题吗?我想在图像结果中使用 Color.Transparent
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    • 2013-03-28
    相关资源
    最近更新 更多