为什么位图比较不等于自身?

Joe*_*oey 6 c# gdi+ memcmp

这有点令人费解.以下代码是一个小测试应用程序的一部分,用于验证代码更改未引入回归.为了使它快速,我们使用memcmp似乎是比较两个相同大小的图像(不出所料)的最快方式.

但是,我们有一些测试图像显示出一个相当令人惊讶的问题:memcmp在位图数据上告诉我们它们不相等,但是,逐像素比较根本没有发现任何差异.我的印象是,当你使用LockBitsa时,Bitmap你会获得图像的实际原始字节.对于24 bpp位图,有点难以想象像素相同但基础像素数据不相同的情况.

一些令人惊讶的事情:

  1. 差异始终00一个图像和另一个图像FF中的单个字节.
  2. 如果一个人改变了PixelFormatLockBitsFormat32bppRgb或者Format32bppArgb,比较成功.
  3. 如果将BitmapData第一次LockBits调用返回的返回值作为第四个参数传递给第二个参数,则比较成功.
  4. 如上所述,逐像素比较也成功.

我有点难过,因为坦白说我无法想象为什么会这样.

(简化)代码如下.只需编译csc /unsafe并传递一个24bpp的PNG图像作为第一个参数.

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

class Program
{
    public static void Main(string[] args)
    {
        Bitmap title = new Bitmap(args[0]);
        Console.WriteLine(CompareImageResult(title, new Bitmap(title)));
    }

    private static string CompareImageResult(Bitmap bmp, Bitmap expected)
    {
        string retval = "";

        unsafe
        {
            var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            var resultData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
            var expectedData = expected.LockBits(rect, ImageLockMode.ReadOnly, expected.PixelFormat);

            try
            {
                if (memcmp(resultData.Scan0, expectedData.Scan0, resultData.Stride * resultData.Height) != 0)
                    retval += "Bitmap data did not match\n";
            }
            finally
            {
                bmp.UnlockBits(resultData);
                expected.UnlockBits(expectedData);
            }
        }

        for (var x = 0; x < bmp.Width; x++)
            for (var y = 0; y < bmp.Height; y++)
                if (bmp.GetPixel(x, y) != expected.GetPixel(x, y))
                {
                    Console.WriteLine("Pixel diff at {0}, {1}: {2} - {3}", x, y, bmp.GetPixel(x, y), expected.GetPixel(x, y));
                    retval += "pixel fail";
                }

        return retval != "" ? retval : "success";
    }

    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int memcmp(IntPtr b1, IntPtr b2, long count);
}
Run Code Online (Sandbox Code Playgroud)

Col*_*ith 7

看看这个,它图示了一个LockBits缓冲区 - 它显示了Strides的行和Padide可以出现在Stride的末尾(如果需要的话).

步幅可能与32位(即单词)边界对齐(出于效率目的)......并且步幅末端的额外未使用空间是使下一个Stride对齐.

这就是在比较期间给你的随机行为...... Padding区域中的虚假数据.

当你使用自然字对齐的Format32bppRgb和Format32bppArgb时,我想你最后没有任何额外的未使用位,这就是它工作的原因.

  • 要添加答案:您必须分别比较每条扫描线,*width x height*bytes.你不能只对整个像素值数组执行`memcmp`,因为每条扫描线可能有1,2或3个未使用的字节. (2认同)