C# - 使用Marshal.Copy不能始终如一地将Bitmap转换为byte []?

时间:2015-11-18 17:30:38

标签: c# image image-processing memory-management bitmap

我一直在努力实现这里看到的图像比较算法:http://www.dotnetexamples.com/2012/07/fast-bitmap-comparison-c.html

我遇到的问题是,当我尝试使用下面粘贴的方法(上面的链接中的略微修改版本)一个接一个地比较大量图像时,我的结果似乎是不准确的。特别是,如果我尝试比较太多不同的图像,即使相同的图像偶尔会被检测为不同的图像。问题似乎是数组中的某些字节是不同的,正如您在屏幕截图中看到的那样,我将两个相同的图像进行比较(这种情况发生在我反复比较大约100张图像的数组中的图像时 - 但是实际上阵列中只有3个独特的图像:

private bool byteCompare(Bitmap image1, Bitmap image2) {
        if (object.Equals(image1, image2))
            return true;
        if (image1 == null || image2 == null)
            return false;
        if (!image1.Size.Equals(image2.Size) || !image1.PixelFormat.Equals(image2.PixelFormat))
            return false;

        #region Optimized code for performance

        int bytes = image1.Width * image1.Height * (Image.GetPixelFormatSize(image1.PixelFormat) / 8);

        byte[] b1bytes = new byte[bytes];
        byte[] b2bytes = new byte[bytes];

        Rectangle rect = new Rectangle(0, 0, image1.Width - 1, image1.Height - 1);

        BitmapData bmd1 = image1.LockBits(rect, ImageLockMode.ReadOnly, image1.PixelFormat);
        BitmapData bmd2 = image2.LockBits(rect, ImageLockMode.ReadOnly, image2.PixelFormat);

        try
        {
            Marshal.Copy(bmd1.Scan0, b1bytes, 0, bytes);
            Marshal.Copy(bmd2.Scan0, b2bytes, 0, bytes);

            for (int n = 0; n < bytes; n++)
            {
                if (b1bytes[n] != b2bytes[n])  //This line is where error occurs
                    return false;
            }
        }
        finally
        {
            image1.UnlockBits(bmd1);
            image2.UnlockBits(bmd2);
        }

        #endregion

        return true;
    }

byte compare error

我添加了一条评论,以显示此错误发生在方法中的位置。我认为它与未正确分配的内存有关,但我无法弄清楚错误的来源是什么。

我还应该提一下,当我将图像转换为字节数组时,我没有遇到任何问题:

ImageConverter converter = new ImageConverter();
byte[] b1bytes = (byte[])converter.ConvertTo(image1, typeof(byte[]));

然而,这种方法要慢得多。

2 个答案:

答案 0 :(得分:3)

如果(Width * bytesperpixel) != Stride,则每行末尾都会有未使用的字节,这些字节不保证具有任何特定值,实际上可以填充随机垃圾。

你需要逐行迭代,每次都按Stride递增,只检查实际上对应每行像素的字节。

答案 1 :(得分:0)

获得BitmapData对象后,可以在BitmapData对象的Stride属性中找到Stride。确保为两个图像提取。

然后,您必须遍历数据中的所有像素,以便您可以准确地确定每条线的图像宽度结束的位置以及步幅的剩余数据开始。

另请注意,这仅适用于高彩色图像。仍然可以比较8位图像(虽然您也需要比较它们的调色板),但是对于低于8的图像,您需要进行位移以从图像中获取实际的调色板偏移。

一个简单的解决方法是在新的32bpp图像上绘制图像,有效地将其转换为高色。

public static Boolean CompareHiColorImages(Byte[] imageData1, Int32 stride1, Byte[] imageData2, Int32 stride2, Int32 width, Int32 height, PixelFormat pf)
{
    Int32 byteSize = Image.GetPixelFormatSize(pf) / 8;
    for (Int32 y = 0; y < height; y++)
    {
        for (Int32 x = 0; x < width; x++)
        {
            Int32 offset1 = y * stride1 + x * byteSize;
            Int32 offset2 = y * stride2 + x * byteSize;
            for (Int32 n = 0; n > byteSize; n++)
                if (imageData1[offset1 + n] != imageData2[offset2 + n])
                    return false;
        }
    }
    return true;
}