单元测试图像序列化

本文关键字:序列化 图像序列 图像 单元测试 | 更新日期: 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的行为像这样,我不知道。但是LoadSave不给出相同的内存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字符串(但考虑到以下原因,我不会指望这一点)。

如果您在监视窗口中查看上面的对象im1im2,您将看到它们之间存在差异。从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表示。