仅出于测试目的,我通过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
更新
这是两张图片
原始(40kb)(摘自here)
重新上传(59kb)
更新2
我用JPG文件和EncodeToJPG()
重复了测试,结果似乎更加糟糕:
顶部是原始图像;在底部重新上传的影片。
这次,它从27kb
变为了98kb
,所以要考虑因素2,63
。奇怪的是,无论我将98kb
的{{1}}参数作为什么,文件大小都是恒定的quality
。
答案 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
流会得到以下结果:
这说明了二进制数据和数据大小的差异。
似乎您无法控制Unity的PNG编码器,因此很遗憾,您不能强迫它选择其他zlib
算法。
我想,JPEG文件也会发生同样的情况-编码器只是选择了一种更快的算法来生成更大的文件。
如果要完全控制Unity中的PNG编码,则需要实现自己的PNG编码器。例如。 here on Unity forum,有一个使用zlib.net
库的PNG编码器示例。您可以微调编码,例如通过指定zlib
压缩算法。
答案 1 :(得分:0)
阅读here 看来,虽然可以导入png,但项目中实际拥有的内容取决于纹理或sprite文件,并且添加的数据会增加其大小。您看到的其他字节来自编辑器附加到png的信息,以简化统一的加载过程,并允许您为导入设置不同的选项。您的文件大小可能会根据您选择的设置而发生巨大变化。