这可能会或可能不会完美地工作,但这里就是这样。如果您认为合适,可以通过添加 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 设置为零)任何未更改的内容。您也可以从像素解锁中受益。同样,可能有教程会告诉您如何操作。但其中大部分可能保持不变。