2014-03-25 47 views
0

我无法找到答案,因此希望有人能帮助我。我正在尝试使用以下代码来序列化和反序列化图像。单元测试图像序列化

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); 
    } 

如果我将图像保存到文件系统,并进行直观比较,它看起来像一切都按我本来期望但单元测试将失败。我能想到的唯一情况是,由于用于加载图像的方法导致图像显示不同,导致出现小的变化。

任何人都可以阐明我如何能够成功地为此编写单元测试?

更新:

我发现这个问题Weird behavior during Image byte array serialization这是相似的。我试图使用从http://en.wikipedia.org/wiki/File:Snow_flake_icon.png使用的相同图像,但即使如此,我也看到了差异。在我指导下使用这个问题时,我仔细研究了正在创建的字节数组。

第一个主要区别是,在反序列化的图像中,我们前十三个字节不存在。还有额外的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 
+0

这些缺失字节的前8个是PNG头,其余5个可能也是图像头的一部分。我认为ImageConverter的使用看起来很可疑。难道你不能将图像保存到内存流中以从中获取字节数据吗? –

+0

@AndersForsgren ImageConverter仅用于创建散列以确定图像是否匹配而不是用于序列化。字节比较使用源图像的实际字节和使用MemoryStream的反序列化图像进行。 – kmcbrearty

回答

0

当您使用ImageConverter我想你考了太多的图像在.NET内部表示的,而非图像之一,在某种意义上“平等”。我不知道什么是

为了将图像转换为字节数组我会使用

using (MemoryStream ms = new MemoryStream()) 
{ 
    im.Save(ms); 
    byteArr = ms.ToArray(); 
} 

就像你在序列化方法做。你的例子中的字节数组在第二种情况下缺少PNG头,所以某种情况肯定会导致它在一种情况下转储PNG文件,而另一种情况则完全不同。为什么ImageConverter的行为如此我不知道。但LoadSave不提供相同的内存中的图像对象的事实是一个线索:

当您从磁盘加载此特定文件,序列化为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字符串(但我不会对下面给出的理由计数)。

如果您在观察窗口中查看以上对象im1im2,您会发现它们之间存在差异。当从byte64流加载文件时,与从文件直接加载文件时相比,元数据将有所不同。

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 ...),它可以序列化Image对象的完整内部状态,这不是您想要的。

如果您想比较图像数据,我建议您将图像投射到Bitmap,然后使用LockBits获取像素数据,然后对其进行散列。即使在这种情况下,您也必须记住,相同的图像不一定具有相同的像素二进制表示,100%透明的像素可以有几个RGB表示。