如何最佳地确定图像中的边缘?

时间:2010-05-24 15:22:25

标签: c# image edge-detection

我最近被置于裁剪和调整图像大小的问题之前。我需要裁剪图像的“主要内容”,例如,如果我有一个类似于此的图像:alt text http://blstb.msn.com/i/8C/10F73EB7EE231B1EA8E65EFA7D69B.jpg

结果应该是msn内容没有白边(左和右)的图像。

我在X轴上搜索第一个和最后一个颜色变化,在Y轴上搜索相同的颜色。问题是逐行遍历图像需要一段时间...对于2000x1600px的图像,返回CropRect =>需要2秒钟的时间。 x1,y1,x2,y2数据。

我试图为每个坐标设置一个遍历并停止在找到的第一个值但它在所有测试用例中都不起作用。有时候返回的数据不是预期的,并且操作的持续时间是相似的。

知道如何缩短遍历时间并围绕“主要内容”发现矩形吗?

public static CropRect EdgeDetection(Bitmap Image, float Threshold)
        {
            CropRect cropRectangle = new CropRect();
            int lowestX = 0;
            int lowestY = 0;
            int largestX = 0;
            int largestY = 0;

            lowestX = Image.Width;
            lowestY = Image.Height;

            //find the lowest X bound;
            for (int y = 0; y < Image.Height - 1; ++y)
            {
                for (int x = 0; x < Image.Width - 1; ++x)
                {
                    Color currentColor = Image.GetPixel(x, y);
                    Color tempXcolor = Image.GetPixel(x + 1, y);
                    Color tempYColor = Image.GetPixel(x, y + 1);
                    if ((Math.Sqrt(((currentColor.R - tempXcolor.R) * (currentColor.R - tempXcolor.R)) +
                        ((currentColor.G - tempXcolor.G) * (currentColor.G - tempXcolor.G)) +
                        ((currentColor.B - tempXcolor.B) * (currentColor.B - tempXcolor.B))) > Threshold)) 
                    {
                        if (lowestX > x)
                            lowestX = x;

                        if (largestX < x)
                            largestX = x;
                    }

                    if ((Math.Sqrt(((currentColor.R - tempYColor.R) * (currentColor.R - tempYColor.R)) +
                        ((currentColor.G - tempYColor.G) * (currentColor.G - tempYColor.G)) +
                        ((currentColor.B - tempYColor.B) * (currentColor.B - tempYColor.B))) > Threshold))
                    {
                        if (lowestY > y)
                            lowestY = y;

                        if (largestY < y)
                            largestY = y;
                    }
                }                
            }

            if (lowestX < Image.Width / 4)
                cropRectangle.X = lowestX - 3 > 0 ? lowestX - 3 : 0;
            else
                cropRectangle.X = 0;

            if (lowestY < Image.Height / 4)
                cropRectangle.Y = lowestY - 3 > 0 ? lowestY - 3 : 0;
            else
                cropRectangle.Y = 0;

            cropRectangle.Width = largestX - lowestX + 8 > Image.Width ? Image.Width : largestX - lowestX + 8;
            cropRectangle.Height = largestY + 8 > Image.Height ? Image.Height - lowestY : largestY - lowestY + 8;
            return cropRectangle;
        }
    }

3 个答案:

答案 0 :(得分:3)

一种可能的优化方法是使用Lockbits直接访问颜色值,而不是通过速度较慢的GetPixel。

如果您搜索Lockbits,则第一次点击是http://www.bobpowell.net/lockingbits.htm。这是一个很好的参考。

另一方面,我的测试表明,如果你尝试编写一个与GetPixel等效的GetPixelFast并将其作为替代品放入,那么与Lockbits相关的开销会使该方法变慢。相反,您需要确保所有像素访问都在一次点击而不是多次点击中完成。如果你没有锁定/解锁每个像素,这应该很适合你的代码。

这是一个例子

BitmapData bmd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat);

byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);

//                           Blue                    Green                   Red 
Color c = Color.FromArgb(row[x * pixelSize + 2], row[x * pixelSize + 1], row[x * pixelSize]);

b.UnlockBits(bmd);

还有两点需要注意:

  1. 此代码不安全,因为它使用指针
  2. 此方法取决于位图数据中的像素大小,因此您需要从bitmap.PixelFormat
  3. 派生pixelSize。

答案 1 :(得分:2)

GetPixel可能是你的主要罪魁祸首(我建议运行一些分析测试来跟踪它),但你可以像这样重构算法:

  1. 从左到右和从右到左扫描第一行(y = 0)并记录第一个和最后一个边缘位置。没有必要检查所有像素,因为你想要极端边缘。
  2. 扫描所有后续行,但现在我们只需向外搜索(从中心向边缘),从我们上一个已知的最小边缘开始。我们希望找到极端的界限,所以我们只需要在我们能找到新极值的地区进行搜索。
  3. 重复列的前两个步骤,建立初始极值,然后使用这些极值迭代绑定搜索。
  4. 如果您的图片通常主要是内容,这应该会大大减少比较次数。最糟糕的情况是完全空白的图像,对于这种情况,这可能不如穷举搜索效率低。

    在极端情况下,图像处理也可以从并行性中获益(将图像拆分并在多核CPU上的多个线程中处理),但这是一项额外的工作,还有其他更简单的更改仍然。线程开销往往会限制此技术的适用性,如果您希望通过专门的传入数据重复处理(以弥补初始设置成本)来实现“实时”运行,则主要有用。

答案 2 :(得分:0)

这不会使订单变得更好......但是如果你平衡你的门槛,你就不需要做平方根,这是非常昂贵的。

这应该会显着提高速度。