我最近被置于裁剪和调整图像大小的问题之前。我需要裁剪图像的“主要内容”,例如,如果我有一个类似于此的图像: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;
}
}
答案 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)
GetPixel可能是你的主要罪魁祸首(我建议运行一些分析测试来跟踪它),但你可以像这样重构算法:
如果您的图片通常主要是内容,这应该会大大减少比较次数。最糟糕的情况是完全空白的图像,对于这种情况,这可能不如穷举搜索效率低。
在极端情况下,图像处理也可以从并行性中获益(将图像拆分并在多核CPU上的多个线程中处理),但这是一项额外的工作,还有其他更简单的更改仍然。线程开销往往会限制此技术的适用性,如果您希望通过专门的传入数据重复处理(以弥补初始设置成本)来实现“实时”运行,则主要有用。
答案 2 :(得分:0)
这不会使订单变得更好......但是如果你平衡你的门槛,你就不需要做平方根,这是非常昂贵的。
这应该会显着提高速度。