【问题标题】:C# - Converting Bitmap to byte[] using Marshal.Copy not working consistently?C# - 使用 Marshal.Copy 将位图转换为字节 [] 无法始终如一地工作?
【发布时间】:2016-02-20 13:44:39
【问题描述】:

我一直在尝试实现此处看到的图像比较算法: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;
    }

我添加了一条注释以显示该错误发生在方法中的哪个位置。我认为这与未正确分配内存有关,但我无法弄清楚错误的根源是什么。

我可能还应该提到,当我将图像转换为这样的字节数组时,我没有遇到任何问题:

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

但是,这种方法要慢得多。

【问题讨论】:

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


    【解决方案1】:

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

    您需要逐行迭代,每次以 Stride 递增,并且只检查实际对应于每行像素的字节。

    【讨论】:

    • 但是如果它们是相同的图像,它们不应该都是相同的吗?
    • 在特定选择的Stride 上对齐行是许多位图格式的运行时优化,可以使某些操作更快。浪费的字节与图像的内容无关,如果图像是另一种格式(例如在磁盘上),甚至可能不存在。
    • 我明白了.. 这可以在我的方法中轻松实现吗?我不确定我是否完全理解您的意思
    • 您介意详细说明一下,或者给我一些提示,告诉我如何做到这一点吗?我想给你正确的答案,你似乎知道它是什么,但我无法用你给我的东西弄清楚..
    • 我已经按照您告诉我的内容尽了最大努力,但我不确定我是否做得正确。你介意看看这个方法中比较两个图像的部分并告诉我我是否做得正确吗? codebeautify.org/csharpviewer/7bb721
    【解决方案2】:

    获得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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-12
      • 2016-10-24
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-10
      相关资源
      最近更新 更多