单元测试图像序列化
本文关键字:序列化 图像序列 图像 单元测试 | 更新日期: 2023-09-27 17:53:17
我没能找到这个问题的答案,希望有人能帮助我。我试图使用以下代码来序列化和反序列化图像。
public override string Serialize(Image image)
{
ImageFormat format = ImageFormat.Png;
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, format);
byte[] imageBytes = ms.ToArray();
string base64String = Convert.ToBase64String(imageBytes);
return base64String;
}
}
public override Image Deserialize(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new Exception("Invalid serialized string. Unable to deserialize image");
byte[] imageBytes = Convert.FromBase64String(value);
using (MemoryStream ms = new MemoryStream(imageBytes))
{
Image image = Image.FromStream(ms);
return image;
}
}
从一个视觉测试,这段代码工作如预期,但我无法通过单元测试验证这一点。我试图使用下面的代码来比较图像,我已经能够成功地进行单元测试,如果图像是相同的。
public static bool IsEqualTo(this Image sourceImage, Image compareImage)
{
if (sourceImage.Size != compareImage.Size)
return false;
byte[] sourceHash = sourceImage.CreateHash();
byte[] compareHash = compareImage.CreateHash();
return sourceHash.IsEqualTo(compareHash);
}
private static byte[] CreateHash(this Image image)
{
ImageConverter converter = new ImageConverter();
byte[] byteArray = (byte[])converter.ConvertTo(image, typeof(byte[]));
SHA256Managed shaM = new SHA256Managed();
byte[] hash = shaM.ComputeHash(byteArray);
return hash;
}
private static bool IsEqualTo(this byte[] source, byte[] compare)
{
if (source.Length != compare.Length)
return false;
for (int i = 0; i < source.Length; i++)
if (source[i] != compare[i])
return false;
return true;
}
完成这两个部分后,我将尝试编写一个单元测试,该测试假设序列化成功发生,并将简单地验证我继续获得相同的序列化字符串。然后使用序列化字符串初始化一个新图像,我使用以下代码对其进行比较:
[Fact]
public void Deserialize_Success()
{
Image expected = this.LoadImage();
string imageString = this.GetImageString();
ImageSerializer serializer = new ImageSerializer();
Image actual = serializer.Deserialize(imageString);
bool match = expected.IsEqualTo(actual);
Assert.True(match);
}
如果我将映像保存到文件系统并执行可视化比较,看起来一切都像我预期的那样工作,但是单元测试将失败。我能想到的唯一一件事是,由于加载图像的方法导致了一个小的差异,这使得它看起来不同。
谁能告诉我如何才能成功地为此编写单元测试?
更新:我发现问题的奇怪行为在图像字节数组序列化这是相似的。我试图使用相同的图像,他们使用http://en.wikipedia.org/wiki/File:Snow_flake_icon.png,但我看到的差异,甚至与此。以这个问题为指导,我仔细研究了正在创建的字节数组。
第一个主要区别是,在反序列化的图像中,前13个字节不存在。还有29个额外字节的额外方差,所有方差都在前46个字节中。我的问题是,我不认为我可以假设反序列化的图像总是有13个字节的偏移,我可以忽略前46个字节。
详细信息如下:
Expected Length: 17932 Actual Length: 17919
expected[0]=137 Actual Not Present
expected[1]=80 Actual Not Present
expected[2]=78 Actual Not Present
expected[3]=71 Actual Not Present
expected[4]=13 Actual Not Present
expected[5]=10 Actual Not Present
expected[6]=26 Actual Not Present
expected[7]=10 Actual Not Present
expected[8]=0 Actual Not Present
expected[9]=0 Actual Not Present
expected[10]=0 Actual Not Present
expected[11]=13 Actual Not Present
expected[12]=73 Actual Not Present
expected[13]=72 actual[13]=72
expected[14]=68 actual[14]=68
expected[15]=82 actual[15]=82
expected[16]=0 actual[16]=0
expected[17]=0 actual[17]=0
expected[18]=1 actual[18]=1
expected[19]=0 actual[19]=0
expected[20]=0 actual[20]=0
expected[22]=1 actual[22]=1
expected[24]=8 actual[24]=8
expected[25]=6 actual[25]=6
expected[26]=0 actual[26]=0
expected[27]=0 actual[27]=0
expected[28]=0 actual[28]=0
expected[29]=92 actual[29]=92
expected[30]=114 actual[30]=114
expected[31]=168 actual[31]=168
expected[32]=102 actual[32]=102
expected[35]=0 actual[35]=0
expected[36]=1 actual[36]=4
expected[37]=115 actual[37]=103
expected[38]=82 actual[38]=65
expected[39]=71 actual[39]=77
expected[40]=66 actual[40]=65
expected[42]=174 actual[42]=0
expected[43]=206 actual[43]=177
expected[44]=28 actual[44]=143
expected[45]=233 actual[45]=11
当你使用ImageConverter
时,我认为你测试了太多。net中图像的内部表示,而不是图像在某种意义上"相等"。我不确定什么是
要将图像转换为字节数组,我将使用
using (MemoryStream ms = new MemoryStream())
{
im.Save(ms);
byteArr = ms.ToArray();
}
就像你在serialize方法中做的那样。在第二种情况下,你的例子中的字节数组缺少PNG头,所以肯定有什么东西导致它在一种情况下转储PNG文件,而在另一种情况下则完全不同。为什么ImageConverter的行为像这样,我不知道。但是Load
和Save
不给出相同的内存Image对象的事实是一个线索:
当您从磁盘加载这个特定的文件,序列化到base64,然后再次加载时,元数据存在差异。
var im1 = Image.FromFile(@"C:'Snow_flake_icon.png");
string s1 = Serialize(im1);
var im2 = Deserialize(s1);
string s2 = Serialize(im2);
bool equalBase64 = s1 == s2; // true
我们可以看到,在这种情况下,重新序列化反序列化的图像会得到相同的base64字符串(但考虑到以下原因,我不会指望这一点)。
如果您在监视窗口中查看上面的对象im1
和im2
,您将看到它们之间存在差异。从byte64流加载文件与直接从file加载文件时,元数据会有所不同。
im1的元数据(在图像的PropertyItem
属性中找到):
0x5110 (PixelUnit)
0x5111 (Pixels per unit X)
0x5112 (Pixels per unit Y)
从base64字符串加载的图像im2具有以下属性:
0x5110 (PixelUnit)
0x5111 (Pixels per unit X)
0x5112 (Pixels per unit Y)
0x0303 (sRGBRenderingIntent)
0x0301 (Gamma)
因此,Load
/Save
对即使对于PNG或BMP等无损格式也不一定是对称的,并且使用ImageConverter转换为字节数组似乎更不可靠。我希望在框架中至少存在一个序列化方法(BinaryFormatter, ImageConverter, Image. save…)来序列化图像对象的完整内部状态,这不是你想要的。
如果你想比较图像数据,我建议你将图像转换为Bitmap
,然后使用LockBits
来获取像素数据,然后对其进行哈希。即使在这种情况下,您也必须记住,相同的图像不一定具有相同的像素二进制表示,100%透明的像素可以具有多个rgba表示。