2010-05-24 62 views
0

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

结果应该是带有msn内容而没有白色边距的图像(左侧&右侧)。

我在X轴上搜索第一个和最后一个颜色变化,在Y轴上搜索同样的东西。问题是,逐行遍历图像需要一段时间..对于2000x1600px的图像,需要2秒才能返回CropRect => 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; 
     } 
    } 
+0

无论你的优化,我会坚持一个Debug.Assert(content!= null)...读入你将会:-) – 2010-05-24 15:37:38

回答

3

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

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

另一方面,我的测试显示,与Lockbits相关的开销使得该方法在尝试编写GetPixelFast等效于GetPixel并放入替代品时变慢。相反,您需要确保所有像素访问都是在一次点击中完成的,而不是多次点击。如果不锁定/解锁每个像素,这应该与您的代码非常吻合。

下面是一个例子

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. 这种方法依赖于像素大小的位图数据中,所以你将需要从位图派生pixelSize.PixelFormat
0

这不会使其更好的顺序......但如果你方的门槛,你会不会需要做的平方根,这是非常昂贵的。

这应该会显着提高速度。

2

GetPixel可能你的主要元凶(我建议运行一些剖析测试,以追查),但你可以重组的算法是这样的:

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

如果您的图像通常以内容为主,这应该大大减少比较次数。最糟糕的情况是完全空白的图像,对此,这可能比穷举搜索效率低。

在极端情况下,图像处理也可以受益于并行性(将图像分割并在多核CPU上的多线程中处理它),但这是相当多的额外工作,还有其他更简单的更改你还是做。线程开销往往会限制这种技术的适用性,如果您希望通过对输入数据进行专门的重复处理(以弥补初始设置成本)来“实时”运行此事件,那么主要有用。