C#找到图像之间的差异

时间:2015-07-01 13:29:54

标签: c# image screenshot

我试图比较截图并只写出差异。

这是我的代码

public Bitmap Difference(Bitmap bmp0, Bitmap bmp1)
{
        Bitmap bmp2;
        int Bpp = bmp0.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4;

        bmp2 = new Bitmap(bmp0.Width, bmp0.Height, bmp0.PixelFormat);

        var bmpData0 = bmp0.LockBits(
                        new Rectangle(0, 0, bmp0.Width, bmp0.Height),
                        ImageLockMode.ReadOnly, bmp0.PixelFormat);
        var bmpData1 = bmp1.LockBits(
                        new Rectangle(0, 0, bmp1.Width, bmp1.Height),
                        ImageLockMode.ReadOnly, bmp1.PixelFormat);
        var bmpData2 = bmp2.LockBits(
                        new Rectangle(0, 0, bmp2.Width, bmp2.Height),
                        ImageLockMode.ReadWrite, bmp2.PixelFormat);

       // MessageBox.Show(bmpData0.Stride.ToString());
       int len = bmpData0.Height * bmpData0.Stride;

       //   MessageBox.Show(bmpData0.Stride.ToString());
       bool changed = false;

       byte[] data0 = new byte[len];
       byte[] data1 = new byte[len];
       byte[] data2 = new byte[len];

       Marshal.Copy(bmpData0.Scan0, data0, 0, len);
       Marshal.Copy(bmpData1.Scan0, data1, 0, len);
       Marshal.Copy(bmpData2.Scan0, data2, 0, len);

       for (int i = 0; i < len; i += Bpp)
       {
            changed = ((data0[i] != data1[i])
                          || (data0[i + 1] != data1[i + 1])
                          || (data0[i + 2] != data1[i + 2]));

            // this.Invoke(new Action(() => this.Text = changed.ToString()));

            data2[i] = changed ? data1[i] : (byte)2;   // special markers
            data2[i + 1] = changed ? data1[i + 1] : (byte)3;   // special markers
            data2[i + 2] = changed ? data1[i + 2] : (byte)7;   // special markers

            if (Bpp == 4) 
               data2[i + 3] = changed ? (byte)255 : (byte)42;  // special markers
    }

    //  this.Invoke(new Action(() => this.Text = changed.ToString()));
    Marshal.Copy(data2, 0, bmpData2.Scan0, len);
    bmp0.UnlockBits(bmpData0);
    bmp1.UnlockBits(bmpData1);
    bmp2.UnlockBits(bmpData2);

    return bmp2;
}


    Bitmap shot = new Bitmap(SystemInformation.VirtualScreen.Width,
                    SystemInformation.VirtualScreen.Height,
                    PixelFormat.Format24bppRgb);

    public Bitmap screenshot()
    {


        Graphics screenGraph = Graphics.FromImage(shot);
        screenGraph.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                   Screen.PrimaryScreen.Bounds.Y,
                                   0,
                                   0,
                                   SystemInformation.VirtualScreen.Size,
                                   CopyPixelOperation.SourceCopy);

        return shot;

    }

我的电话:

private void Form1_Load(object sender, EventArgs e)
{
        Bitmap prev = screenshot();

        Thread.Sleep(1000);
        Bitmap  curr = screenshot();

        pictureBox1.Image=Difference(prev,curr);
}

我在此行中获得Bitmap region is already locked. 错误

   var bmpData1 = bmp1.LockBits(
                        new Rectangle(0, 0, bmp1.Width, bmp1.Height),
                        ImageLockMode.ReadOnly, bmp1.PixelFormat);

这是我的截图代码,一个简单的.net屏幕截图方法:

它真的很奇怪,因为我甚至没有在screenshot方法中使用LockBits所以我不知道它为什么会抛出这个错误......

1 个答案:

答案 0 :(得分:1)

如果您的位图是“自下而上”而不是“自上而下”,则Stride属性将为负值。

见这里: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.stride%28v=vs.110%29.aspx

从这篇文章:
“步幅是单行像素(扫描线)的宽度,四舍五入到四字节边界。如果步幅为正,则位图为自上而下。如果步幅为负,则位图为底部-up“。

使用

int len = bmpData0.Height * Math.Abs(bmpData0.Stride);

如果您使用的是自下而上的位图,则需要使用不同的机制将位图复制到缓冲区。关于如何在这个问题中处理这个问题有一些不同的建议,请参阅前三个答案:

How can I copy the pixel data from a Bitmap with negative stride?



从最新的编辑到您的问题,现在您的问题是您正在为截图使用相同的Bitmap实例。在screenshot()方法中移动'shot'声明,以便为每个屏幕截图使用不同的实例:

public Bitmap screenshot()
{

     Bitmap shot = new Bitmap(SystemInformation.VirtualScreen.Width,
                SystemInformation.VirtualScreen.Height,
                PixelFormat.Format24bppRgb);
    Graphics screenGraph = Graphics.FromImage(shot);
    screenGraph.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                               Screen.PrimaryScreen.Bounds.Y,
                               0,
                               0,
                               SystemInformation.VirtualScreen.Size,
                               CopyPixelOperation.SourceCopy);

    return shot;
}