应用下载的纹理并通过EncodeToPNG转换回PNG后,PNG文件会在Unity中增长

时间:2018-11-08 21:01:33

标签: c# image unity3d encoding

仅出于测试目的,我通过http下载了PNG文件(在这种情况下,是通过API从JIRA服务器下载的)

对于http请求,我有一个非常“标准”的类 HttpFileManager 我只是为了完整性而添加:

public static class HttpFileManager
{

    public void DownloadImage(string url, Action<Texture> successCallback = null, Credentials credentials = null, Action<UnityWebRequest> errorCallback = null)
    {
        StartCoroutine(DownloadImageProcess(url, successCallback, credentials, errorCallback));
    }

    private static IEnumerator DownloadImageProcess(string url, Action<Texture> successCallback, Credentials credentials, Action<UnityWebRequest> errorCallback)
    {
        var www = UnityWebRequestTexture.GetTexture(url);

        if (credentials != null)
        {
            // This simply adds some headers to the request required for JIRA api
            // it is not relevant for this question
            AddCredentials(www, credentials);
        }

        yield return www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError)
        {
            Debug.LogErrorFormat("Download from {0} failed with {1}", url, www.error);
            errorCallback?.Invoke(www);
        }
        else
        {
            Debug.LogFormat("Download from {0} complete!", url);
            successCallback?.Invoke(((DownloadHandlerTexture) www.downloadHandler).texture);
        }
    }

    public static void UploadFile(byte[] rawData, string url, Action<UnityWebRequest> successcallback, Credentials credentials, Action<UnityWebRequest> errorCallback)

    private static IEnumerator UploadFileProcess(byte[] rawData, string url, Action<UnityWebRequest> successCallback, Credentials credentials, Action<UnityWebRequest> errorCallback)
    {
        var form = new WWWForm();
        form.AddBinaryData("file",rawData,"Test.png");

        var www = UnityWebRequest.Post(url, form);

        www.SetRequestHeader("Accept", "application/json");

        if (credentials != null)
        {
            // This simply adds some headers to the request required for JIRA api
            // it is not relevant for this question
            AddCredentials(www, credentials);
        }

        yield return www.SendWebRequest();

        if (www.isNetworkError || www.isHttpError)
        {
            Debug.LogErrorFormat("Upload to {0} failed with code {1} {2}", url, www.responseCode, www.error);
            errorCallback?.Invoke(www);
        }
        else
        {
            Debug.LogFormat("Upload to {0} complete!", url);
            successCallback?.Invoke(www);
        }
    }
}

后来我做了我的脚本

public Texture TestTexture;

// Begin the download
public void DownloadTestImage()
{
    _httpFileManager.DownloadImage(ImageGetURL, DownloadImageSuccessCallback, _credentials);
}

// After Download store the Texture
private void DownloadImageSuccessCallback(Texture newTexture)
{
    TestTexture = newTexture;
}

// Start the upload
public void UploadTestImage()
{
    var data = ((Texture2D) TestTexture).EncodeToPNG();

    _httpFileManager.UploadFile(data, ImagePostUrl, UploadSuccessCallback, _credentials);
}

// After Uploading
private static void UploadSuccessCallback(UnityWebRequest www)
{
    Debug.Log("Upload worked!");
}

在简历中,问题出在for和back转换中

(DownloadHandlerTexture) www.downloadHandler).texture

((Texture2D) TestTexture).EncodeToPNG();

结果看起来像这样

顶部是原始图像;在底部重新上传的影片。

您可以看到它从40kb增长到59kb,因此增长了1,475倍。较大的文件也是如此,因此844kb增长到1,02Mb

所以我的问题是

为什么EncodeToPNG()之后上传的图片比原始图片大?

是否有可能/应该在PNG数据上使用压缩以存档相同的压缩级别(如果根本就是压缩)?

首先,我想可能是不同的色彩深度,但是两个图像都是RGBA-32bit

enter image description here


更新

这是两张图片

原始(40kb)(摘自here

enter image description here

重新上传(59kb)

enter image description here


更新2

我用JPG文件和EncodeToJPG()重复了测试,结果似乎更加糟糕:

enter image description here

顶部是原始图像;在底部重新上传的影片。

这次,它从27kb变为了98kb,所以要考虑因素2,63。奇怪的是,无论我将98kb的{​​{1}}参数作为什么,文件大小都是恒定的quality

2 个答案:

答案 0 :(得分:9)

如果您精确地检查了两个PNG文件,您会注意到其中的区别。 它们都具有相同的分辨率,相同的位深度,一定数量的通道,并且都没有隔行扫描。

但是,原始图像仅包含一个IDAT section,其中包含41370字节的编码数据。

源自Unity的图像包含8个IDAT部分:7 x 8192字节和1 2860字节,共60204字节。

在PNG规范中,有一条注释:

  

允许使用多个IDAT块,以便编码器可以在固定数量的内存中工作;通常,块大小将与编码器的缓冲区大小相对应。

此外,对于相同的源图像,这些IDAT节中包含的数据不一定完全相同。那些IDAT节保存的原始字节数据首先是pre-filtered,然后是encoded,使用的是zlib压缩。

因此,PNG编码器可以从5种可用的算法中选择预过滤算法:

Type    Name

0       None
1       Sub
2       Up
3       Average
4       Paeth

此外,zlib压缩可以配置为压缩窗口大小,也可以由PNG编码器选择。

检查zlib流会得到以下结果:

  • 两个文件均使用相同窗口大小为32k的“压缩”压缩
  • 压缩标志不同,原始文件的压缩级别为1(快速算法),而Unity编码文件的压缩级别为0(最快算法)。

这说明了二进制数据和数据大小的差异。

似乎您无法控制Unity的PNG编码器,因此很遗憾,您不能强迫它选择其他zlib算法。

我想,JPEG文件也会发生同样的情况-编码器只是选择了一种更快的算法来生成更大的文件。

如果要完全控制Unity中的PNG编码,则需要实现自己的PNG编码器。例如。 here on Unity forum,有一个使用zlib.net库的PNG编码器示例。您可以微调编码,例如通过指定zlib压缩算法。

答案 1 :(得分:0)

阅读here 看来,虽然可以导入png,但项目中实际拥有的内容取决于纹理或sprite文件,并且添加的数据会增加其大小。您看到的其他字节来自编辑器附加到png的信息,以简化统一的加载过程,并允许您为导入设置不同的选项。您的文件大小可能会根据您选择的设置而发生巨大变化。