这可能会或可能不会完美工作,但在这里。如果您看到如此合适,可以通过添加AsParallel
来将其并行化。
我也在这里复制并粘贴了几行,所以请随时让我知道或编辑,如果我有任何错别字或不匹配的变量。但这是它的要点。这应该是相当快的,在合理的范围内。基本上,由于这可能有点难以理解,所以想法是“锁定”这些位,然后使用该指针将它们复制到byte[]
。这有效地复制出所有RGB(A)值,然后您可以很容易地访问它们。当你阅读的时候,这个速度比GetPixel
快了很多,因为你需要一点点时间才能获取像素,但这只是简单的读取内存。
一旦我将它们放入byte[]
s中,只需比较每个像素坐标就足够简单了。我选择使用LINQ,以便在需要时很容易并行化,但是您可能会或可能不会选择实际实现它。我不确定你是否需要。
我在这里做了一些假设,我认为这是公平的,因为听起来像您的实现具有来自单一来源的所有图像。也就是说,我认为这些图像的大小和格式是相同的。因此,如果事实并非如此,那么您需要在此处添加一些额外的代码,但这仍然非常简单。
private byte[] UnlockBits(Bitmap bmp, out int stride)
{
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
stride = bmpData.Stride;
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] ret = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, ret, 0, bytes);
bmp.UnlockBits(bmpData);
return ret;
}
private bool AreArraysEqual(byte[] a, byte[] b, int offset, int length)
{
for (int v = 0; v < length; v++)
{
int c = v + offset;
if (a[c] != b[c])
{
return false;
}
}
return true;
}
private IEnumerable<KeyValuePair<Point, Tuple<Color, Color>>> GetDifferences(Bitmap a, Bitmap b)
{
if (a.PixelFormat != b.PixelFormat)
throw new ArgumentException("Unmatched formats!");
if (a.Size != b.Size)
throw new ArgumentException("Unmatched length!");
int stride;
byte[] rgbValuesA = UnlockBits(a, out stride);
byte[] rgbValuesB = UnlockBits(b, out stride);
if (rgbValuesA.Length != rgbValuesB.Length)
throw new ArgumentException("Unmatched array lengths (unexpected error)!");
int bytesPerPixel = Image.GetPixelFormatSize(a.PixelFormat)/8;
return Enumerable.Range(0, a.Height).SelectMany(y =>
Enumerable.Range(0, a.Width)
.Where(x => !AreArraysEqual(rgbValuesA,
rgbValuesB,
(y * stride) + (x * bytesPerPixel),
bytesPerPixel))
.Select(x =>
{
Point pt = new Point(x, y);
int pixelIndex = (y * stride) + (x * bytesPerPixel);
Color colorA = ReadPixel(rgbValuesA, pixelIndex, bytesPerPixel);
Color colorB = ReadPixel(rgbValuesB, pixelIndex, bytesPerPixel);
return new KeyValuePair<Point, Tuple<Color, Color>>(pt, colorA, colorB);
}
}
private Color ReadPixel(byte[] bytes, int offset, int bytesPerPixel)
{
int argb = BitConverter.ToInt32(pixelBytes, offset);
if (bytesPerPixel == 3) // no alpha
argb |= (255 << 24);
return Color.FromArgb(argb);
}
public IEnumerable<KeyValuePair<Point, Color>> GetNewColors(Bitmap _new, Bitmap old)
{
return GetDifferences(_new, old).Select(c => new KeyValuePair<Point, Color>(c.Key, c.Value.Item1));
}
在真正的实现,你可能要多一点彻底,比我想想字节顺序和像素格式,但这应该工作或多或少作为概念验证,我相信应该处理的多数的实际案例。
而@TaW在评论中说,你也可以尝试消隐(可能将alpha设置为零)任何没有改变的东西。您也可以从像素解锁中受益。再次,有可能教程告诉你如何去做。但其中大部分可能保持不变。
如何获得可接受尺寸的中心矩形并比较该区域内的随机像素? – PeteGO 2014-10-06 20:15:00
其中一个想法是先检查每个[10th,5th,2nd]等像素,如果确定了一个更改,则会优化您检查的区域。 – user1274820 2014-10-06 20:15:08
'GetPixel'也是一个超级慢的方法。尝试一个'UnlockBits'方法。如果你看起来有很多的教程,它会快得多。 – 2014-10-06 20:17:39