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