【问题标题】:How to determine edges in an image optimally?如何最佳地确定图像中的边缘?
【发布时间】:2020-12-29 19:57:42
【问题描述】:

我最近遇到了裁剪和调整图像大小的问题。我需要裁剪图像的“主要内容”,例如,如果我有一张与此类似的图像:
(来源:msn.com

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

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

我尝试对每个坐标进行遍历并在找到的第一个值处停止,但它不适用于所有测试用例。有时返回的数据不是预期的数据,并且操作的持续时间相似。 .

知道如何减少遍历时间和发现围绕“主要内容”的矩形吗?

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

【问题讨论】:

    标签: c# image edge-detection


    【解决方案1】:

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

    Bob Powell page on LockBits 是一个很好的参考。

    另一方面,我的测试表明,如果您尝试编写与 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 派生 pixelSize

    【讨论】:

      【解决方案2】:

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

      1. 从左到右和从右到左扫描第一行 (y = 0),并记录第一个和最后一个边缘位置。无需检查所有像素,因为您需要极端边缘。
      2. 扫描所有后续行,但现在我们只需要向外搜索(从中心到边缘),从我们最后一个已知的最小边缘开始。我们想要找到极值边界,所以我们只需要在可以找到新极值的区域中搜索即可。
      3. 对列重复前两个步骤,建立初始极值,然后使用这些极值迭代地限制搜索。

      如果您的图片通常主要是内容,这应该会大大减少比较次数。最坏的情况是一个完全空白的图像,这可能会比穷举搜索效率低。

      在极端情况下,图像处理也可以从并行性中受益(拆分图像并在多核 CPU 上的多个线程中处理它),但这是相当多的额外工作,还有其他更简单的改变仍然使。线程开销往往会限制这种技术的适用性,如果您希望“实时”运行这个东西,并专门重复处理传入的数据(以弥补初始设置成本),这将很有帮助。

      【讨论】:

        【解决方案3】:

        这不会使订单变得更好...但是如果您将阈值平方,则无需进行平方根,这非常昂贵。

        这应该会显着提高速度。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-07-18
          • 1970-01-01
          • 1970-01-01
          • 2016-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多