C# 如何找出两幅图像之间的差异?
我正在开发一个屏幕共享应用程序。在这个项目中,我需要通过互联网传输图像。很明显,我不能每隔几秒钟通过互联网发送一张新照片,这会非常慢。 我想向客户端发送服务器屏幕的一个图像,然后再发送一个新的图像,而不是只发送自上一个图像(客户端已经拥有的图像)以来已更改的像素 我编写了以下代码:C# 如何找出两幅图像之间的差异?,c#,C#,我正在开发一个屏幕共享应用程序。在这个项目中,我需要通过互联网传输图像。很明显,我不能每隔几秒钟通过互联网发送一张新照片,这会非常慢。 我想向客户端发送服务器屏幕的一个图像,然后再发送一个新的图像,而不是只发送自上一个图像(客户端已经拥有的图像)以来已更改的像素 我编写了以下代码: private List<Color> CompareBitmaps(Image old, Image _new) { List<Color> returnList = new Lis
private List<Color> CompareBitmaps(Image old, Image _new)
{
List<Color> returnList = new List<Color>();
for(int i = 0; i < old.Width; i++)
for (int j = 0; j < old.Height; j++)
{
if (((Bitmap)old).GetPixel(i, j) != ((Bitmap)_new).GetPixel(i, j))
{
returnList.Add(((Bitmap)_new).GetPixel(i, j));
}
}
return returnList;
}
private List comparebitmap(图像旧,图像新)
{
List returnList=新列表();
for(int i=0;i
然而,它的工作方式太慢了
我在寻找一个更快的算法,一个更复杂的算法
注意:我不想要一个这样的内置库。我需要一个算法。您需要返回所有更改的像素,因此复杂性必须为m*n
我不确定您想用这段代码做什么,因为您没有保存已更改的索引 您可能希望为列表使用其他构造函数,因为List() 这意味着您将重新分配列表的内部缓冲区,如果许多像素已更改,则可能会多次重新分配 也许记录已更改的平均像素数并将列表的初始容量设置为该数字可以加快代码的速度。至少,您可以这样说,例如,每帧10%的像素变化:
List<Color> returnList = new List<Color>( (int)( 0.10 * numberOfPixel ) );
List returnList=新列表((int)(0.10*numberOfPixel));
这可能会也可能不会很好地工作,但现在开始。如果您认为合适,可以通过添加aspallel
将其并行化
我还复制并粘贴了几行在这里,所以请随时让我知道或编辑,如果我有任何打字错误或不匹配的变量。但这是它的要点。这应该是相当快的,在合理的范围内
从本质上讲,由于这可能有点难以理解,因此我们的想法是“锁定”位,然后使用该指针将它们复制到字节[]
。这有效地复制了所有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));
}
private byte[]解锁位(位图bmp,out-int-stride)
{
BitmapData bmpData=bmp.LockBits(新矩形(Point.Empty,bmp.Size),System.Drawing.Imaging.ImageLockMode.ReadOnly,bmp.PixelFormat);
IntPtr ptr=bmpData.Scan0;
步幅=bmpData.步幅;
int bytes=Math.Abs(bmpData.Stride)*bmp.Height;
字节[]ret=新字节[字节];
System.Runtime.InteropServices.Marshal.Copy(ptr,ret,0,字节);
bmp.UnlockBits(bmpData);
返回ret;
}
专用布尔区域阵列相等(字节[]a,字节[]b,整数偏移量,整数长度)
{
对于(int v=0;v
可枚举范围(0,a.宽度)
其中(x=>!AreArraysEqual(rgbValuesA,
rgbValuesB,
(y*步幅)+(x*字节/像素),
字节/像素)
.选择(x=>
{
点pt=新点(x,y);
int pixelIndex=(y*步幅)+(x*字节/像素);
Color colorA=读取像素(RGB值SA、像素索引、字节/像素);
Color colorB=读取像素(rgbValuesB,像素索引,字节/像素);
返回新的KeyValuePair(pt、colorA、colorB);
}
}
专用颜色读取像素(字节[]字节,int偏移量,int字节/像素)
{
int argb=位转换器.ToInt32(像素字节,偏移量);
if(bytesperpoixel==3)//无al
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));
}
public static Bitmap Difference(Bitmap bmp0, Bitmap bmp1, bool restore)
{
int Bpp = 4; // assuming an effective pixelformat of 32bpp
var bmpData0 = bmp0.LockBits(
new Rectangle(0, 0, bmp0.Width, bmp0.Height),
ImageLockMode.ReadWrite, bmp0.PixelFormat);
var bmpData1 = bmp1.LockBits(
new Rectangle(0, 0, bmp1.Width, bmp1.Height),
ImageLockMode.ReadOnly, bmp1.PixelFormat);
int len = bmpData0.Height * bmpData0.Stride;
byte[] data0 = new byte[len];
byte[] data1 = new byte[len];
Marshal.Copy(bmpData0.Scan0, data0, 0, len);
Marshal.Copy(bmpData1.Scan0, data1, 0, len);
for (int i = 0; i < len; i += Bpp)
{
if (restore)
{
bool toberestored = (data1[i ] != 2 && data1[i+1] != 3 &&
data1[i+2] != 7 && data1[i+2] != 42);
if (toberestored)
{
data0[i ] = data1[i]; // Blue
data0[i+1] = data1[i+1]; // Green
data0[i+2] = data1[i+2]; // Red
data0[i+3] = data1[i+3]; // Alpha
}
}
else
{
bool changed = ((data0[i ] != data1[i ]) ||
(data0[i+1] != data1[i+1]) || (data0[i+2] != data1[i+2]) );
data0[i ] = changed ? data1[i ] : (byte)2; // special markers
data0[i+1] = changed ? data1[i+1] : (byte)3; // special markers
data0[i+2] = changed ? data1[i+2] : (byte)7; // special markers
data0[i+3] = changed ? (byte)255 : (byte)42; // special markers
}
}
Marshal.Copy(data0, 0, bmpData0.Scan0, len);
bmp0.UnlockBits(bmpData0);
bmp1.UnlockBits(bmpData1);
return bmp0;
}