使用LockBits时访问冲突

时间:2011-01-27 13:32:10

标签: c#

我想使用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)

我的课程有什么问题?

感谢。

3 个答案:

答案 0 :(得分:8)

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

因此,在向后扫描位图中,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;
    }

答案 1 :(得分:0)

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

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

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

这个site很好地解释了LockBit功能。

答案 2 :(得分:0)

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

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);
    }
}