使用LockBits时的AccessViolation

我想使用LockBits方法更快地比较类似图像,如下所示

using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; public class CompareImages { public static void Main ( String[] args ) { Bitmap bm1 = new Bitmap ( "PB270029.JPG" ); Console.WriteLine ( bm1.PixelFormat.ToString() ); int width = bm1.Width; int height = bm1.Height; Console.WriteLine ( "width = " + width + " height = " + height ); Rectangle rect1 = new Rectangle ( 0, 0, width, height ); BitmapData bm1Data = bm1.LockBits ( rect1, ImageLockMode.ReadOnly, bm1.PixelFormat ); Console.WriteLine ( "stride = " + bm1Data.Stride ); IntPtr bm1Ptr = bm1Data.Scan0; int bytes = Math.Abs(bm1Data.Stride) * height; Console.WriteLine ( "bytes = " + bytes ); byte[] rgbValues1 = new byte [ bytes ]; Marshal.Copy ( bm1Ptr, rgbValues1, 0, bytes ); Console.WriteLine ( "After 1st Marshal.Copy ..." ); Bitmap bm2 = new Bitmap ( "PA050164.JPG" ); Rectangle rect2 = new Rectangle ( 0, 0, bm2.Width, bm2.Height ); BitmapData bm2Data = bm2.LockBits ( rect2, ImageLockMode.ReadOnly, bm2.PixelFormat ); IntPtr bm2Ptr = bm2Data.Scan0; byte[] rgbValues2 = new byte [ Math.Abs(bm2Data.Stride) * bm2.Height ]; Marshal.Copy ( bm2Ptr, rgbValues2, 0, rgbValues2.Length ); } } 

但在第二个Marshal.Copy期间发生了AccessViolationException:

 C:\CompareImages>csc CompareImages.cs Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1 Copyright (C) Microsoft Corporation. All rights reserved. C:\CompareImages>CompareImages.exe Format24bppRgb width = 3648 height = 2736 stride = 10944 bytes = 29942784 After 1st Marshal.Copy ... Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at System.Runtime.InteropServices.Marshal.CopyToManaged(IntPtr source, Object destination, Int32 startIndex, Int32 length) at CompareImages.Main(String[] args) 

我的计划有什么问题?

谢谢。

我几个小时以来一直在研究类似的问题,我想我发现了什么可能是你的问题。 我猜你的位图可能会以稍微不同的格式存储。 位图可以向前或向后存储。 向后存储时,步幅为负。 但是,Scan0将始终指向扫描的第一行,即第一个像素而不是数组中的第一个字节。

因此,在向后扫描位图中,Scan0 + Abs(Stride) – 1是数组中的最后一个字节。 Scan0 + Stride将始终是第二行的开头,因此如果stride为负,则向后工作,而positive将向前工作。

如果您使用负步幅执行Marshal.Copy(bm2Ptr,rgbValues2,0,rgbValues2.Length),则会在输入访问冲突区域之前复制最后一条扫描线。 以下代码将任何位图转换为向后扫描字节[](只是因为这是我正在使用的)。 我猜你现在已经修复/解决了你的问题,但希望这有助于其他人。

  private byte[] BitmapToByteArray2(Bitmap bmp) { // Lock the bitmap's bits. Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat); int absStride = Math.Abs(bmpData.Stride); int bytes = absStride * bmp.Height; // Declare an array to hold the bytes of the bitmap. byte[] rgbValues = new byte[bytes]; for (int i = 0; i < bmp.Height; i++) { IntPtr pointer = new IntPtr(bmpData.Scan0.ToInt32() + (bmpData.Stride * i)); System.Runtime.InteropServices.Marshal.Copy(pointer, rgbValues, absStride * (bmp.Height - i - 1), absStride); } // Unlock the bits. bmp.UnlockBits(bmpData); return rgbValues; } 

我测试了代码,对我来说它有效……

首先,我使用了我的一些随机图片,然后使用您的图片尺寸更大的完整白色图片。 也许您可以提供有关这两张图片的更多信息,它们是否具有相同的尺寸或像素格式?

我看到的唯一可能的错误是你没有在复制后调用UnlockBits。

这个网站很好地解释了LockBitfunction。

它适合我。 我试图使图像文件无效,但是引发了一个不同的exception。 它确实需要一些清理。

 using (Bitmap bm1 = new Bitmap("PB270029.JPG")) { Console.WriteLine(bm1.PixelFormat.ToString()); int width = bm1.Width; int height = bm1.Height; Console.WriteLine("width = " + width + " height = " + height); Rectangle rect1 = new Rectangle(0, 0, width, height); BitmapData bm1Data = bm1.LockBits(rect1, ImageLockMode.ReadOnly, bm1.PixelFormat); try { Console.WriteLine("stride = " + bm1Data.Stride); IntPtr bm1Ptr = bm1Data.Scan0; int bytes = Math.Abs(bm1Data.Stride) * height; Console.WriteLine("bytes = " + bytes); byte[] rgbValues1 = new byte[bytes]; Marshal.Copy(bm1Ptr, rgbValues1, 0, bytes); Console.WriteLine("After 1st Marshal.Copy ..."); } finally { bm1.UnlockBits(bm1Data); } } using (Bitmap bm2 = new Bitmap("PA050164.JPG")) { Rectangle rect2 = new Rectangle(0, 0, bm2.Width, bm2.Height); BitmapData bm2Data = bm2.LockBits(rect2, ImageLockMode.ReadOnly, bm2.PixelFormat); try { IntPtr bm2Ptr = bm2Data.Scan0; byte[] rgbValues2 = new byte[Math.Abs(bm2Data.Stride) * bm2.Height]; Marshal.Copy(bm2Ptr, rgbValues2, 0, rgbValues2.Length); } finally { bm2.UnlockBits(bm2Data); } }