Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么位图与自身不相等?_C#_Gdi+_Memcmp - Fatal编程技术网

C# 为什么位图与自身不相等?

C# 为什么位图与自身不相等?,c#,gdi+,memcmp,C#,Gdi+,Memcmp,这有点令人费解。下面的代码是一个小测试应用程序的一部分,用于验证代码更改没有引入回归。为了加快速度,我们使用了memcmpwhich(毫不奇怪) 然而,我们有一些测试图像显示了一个相当令人惊讶的问题:memcmp位图数据告诉我们它们不相等,但是逐像素比较根本没有发现任何差异。我的印象是,在位图上使用锁位可以得到图像的实际原始字节。对于24bpp位图,很难想象像素相同但底层像素数据不同的情况 一些令人惊讶的事情: 差异总是单个字节,在一个图像中为00,在另一个图像中为FF 如果将LockBits的

这有点令人费解。下面的代码是一个小测试应用程序的一部分,用于验证代码更改没有引入回归。为了加快速度,我们使用了
memcmp
which(毫不奇怪)

然而,我们有一些测试图像显示了一个相当令人惊讶的问题:
memcmp
位图数据告诉我们它们不相等,但是逐像素比较根本没有发现任何差异。我的印象是,在
位图
上使用
锁位
可以得到图像的实际原始字节。对于24bpp位图,很难想象像素相同但底层像素数据不同的情况

一些令人惊讶的事情:

  • 差异总是单个字节,在一个图像中为
    00
    ,在另一个图像中为
    FF
  • 如果将
    LockBits
    PixelFormat
    更改为
    Format32bppRgb
    Format32bppArgb
    ,则比较成功
  • 如果将第一个
    LockBits
    调用返回的
    BitmapData
    作为第四个参数传递给第二个参数,则比较成功
  • 如上所述,逐像素比较也成功
  • 我在这里有点困惑,因为坦率地说,我无法想象为什么会发生这种情况

    (减少)代码如下。只需使用
    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);
    }
    
    使用系统;
    使用系统图;
    使用系统、绘图、成像;
    使用System.Runtime.InteropServices;
    班级计划
    {
    公共静态void Main(字符串[]args)
    {
    位图标题=新位图(参数[0]);
    WriteLine(CompareImageResult(标题,新位图(标题));
    }
    专用静态字符串CompareImageResult(位图bmp,应为位图)
    {
    字符串retval=“”;
    不安全的
    {
    var rect=新矩形(0,0,bmp.Width,bmp.Height);
    var resultData=bmp.LockBits(rect,ImageLockMode.ReadOnly,bmp.PixelFormat);
    var expectedData=expected.LockBits(rect,ImageLockMode.ReadOnly,expected.PixelFormat);
    尝试
    {
    if(memcmp(resultData.Scan0、expectedData.Scan0、resultData.Stride*resultData.Height)!=0)
    retval+=“位图数据不匹配\n”;
    }
    最后
    {
    bmp.UnlockBits(resultData);
    预期。解锁位(预期数据);
    }
    }
    对于(var x=0;x
    只是一个有根据的猜测:

    24位(3字节)在32/64位硬件上有点笨拙


    使用这种格式,缓冲区将被刷新为4字节的倍数,留下1个或多个字节作为“不在乎”。它们可以包含随机数据,软件不必将其归零。这将使memcmp失败

    看看这个,它以图形的方式展示了一个锁位缓冲区——它显示了步幅的行数以及步幅末尾可以出现的填充位置(如果需要的话)

    一个步幅可能与32位(即字)边界对齐(出于效率目的)…步幅末尾额外的未使用空间用于对齐下一个步幅

    这就是在比较过程中给你的随机行为…填充区域中的虚假数据


    当您使用自然字对齐的Format32bppRgb和Format32bppArgb时,我猜您的末尾没有任何多余的未使用位,这就是它工作的原因。

    当我打印数据的长度以进行比较时,它等于宽度×高度×3,所以我猜这个猜测不适用。编辑,别客气。显然,不总是这样。133×12图像的长度为4800。结尾似乎有一点空白。尽管如此,数据本身似乎被压缩了。@Joey为了证实这一点,试着在每一行中循环并记住步幅。如果没有失败,那么Henk是正确的。@Joey更正了评论:你不是比较整个步幅,而是比较宽度*3。@Joey将PixelFormat设置为32 bpp值有效的原因是步幅将是4的偶数倍。听起来不错;我的第一个测试图像(大小完全匹配)是40像素宽,这很合适。为了补充答案:你必须分别比较每一条扫描线的宽度x高度字节。不能只对整个像素值数组执行
    memcmp
    ,因为每个扫描行可以有1、2或3个未使用的字节。有人知道为什么#3也可以工作吗?