圖片通常是移動端流量耗費最多的部分,並且占據著重要的視覺空間。合理的圖片格式選用和優化可以為你節省帶寬、提升視覺效果。在這篇文章裡我會分析一下目前主流和新興的幾種圖片格式的特點、性能分析、參數調優,以及相關開源庫的選擇。
Index
幾種圖片格式簡介
移動端圖片類型的支持情況
靜態圖片的編碼與解碼
JPEG
PNG
WebP
BPG
動態圖片的編碼與解碼
GIF
APNG
WebP
BPG
動圖性能對比
首先談一下大家耳熟能詳的幾種老牌的圖片格式吧:
JPEG 是目前最常見的圖片格式,它誕生於 1992 年,是一個很古老的格式。它只支持有損壓縮,其壓縮算法可以精確控制壓縮比,以圖像質量換得存儲空間。由於它太過常見,以至於許多移動設備的 CPU 都支持針對它的硬編碼與硬解碼。
PNG 誕生在 1995 年,比 JPEG 晚幾年。它本身的設計目的是替代 GIF 格式,所以它與 GIF 有更多相似的地方。PNG 只支持無損壓縮,所以它的壓縮比是有上限的。相對於 JPEG 和 GIF 來說,它最大的優勢在於支持完整的透明通道。
GIF 誕生於 1987 年,隨著初代互聯網流行開來。它有很多缺點,比如通常情況下只支持 256 種顏色、透明通道只有 1 bit、文件壓縮比不高。它唯一的優勢就是支持多幀動畫,憑借這個特性,它得以從 Windows 1.0 時代流行至今,而且仍然大受歡迎。
在上面這些圖片格式誕生後,也有不少公司或團體嘗試對他們進行改進,或者創造其他更加優秀的圖片格式,比如 JPEG 小組的 JPEG 2000、微軟的 JPEG-XR、Google 的 WebP、個人開發者發布的 BPG、FLIF 等。它們相對於老牌的那幾個圖片格式來說有了很大的進步,但出於各種各樣的原因,只有少數幾個格式能夠流行開來。下面三種就是目前實力比較強的新興格式了:
APNG 是 Mozilla 在 2008 年發布的一種圖片格式,旨在替換掉畫質低劣的 GIF 動畫。它實際上只是相當於 PNG 格式的一個擴展,所以 Mozilla 一直想把它合並到 PNG 標准裡面去。然而 PNG 開發組並沒有接受 APNG 這個擴展,而是一直在推進它自己的 MNG 動圖格式。MNG 格式過於復雜以至於並沒有什麼系統或浏覽器支持,而 APNG 格式由於簡單容易實現,目前已經漸漸流行開來。Mozilla 自己的 Firefox 首先支持了 APNG,隨後蘋果的 Safari 也開始有了支持, Chrome 目前也已經嘗試開始支持 ,可以說未來前景很好。
WebP 是 Google 在 2010 年發布的圖片格式,希望以更高的壓縮比替代 JPEG。它用 VP8 視頻幀內編碼作為其算法基礎,取得了不錯的壓縮效果。它支持有損和無損壓縮、支持完整的透明通道、也支持多幀動畫,並且沒有版權問題,是一種非常理想的圖片格式。借由 Google 在網絡世界的影響力,WebP 在幾年的時間內已經得到了廣泛的應用。看看你手機裡的 App:微博、微信、QQ、淘寶、網易新聞等等,每個 App 裡都有 WebP 的身影。Facebook 則更進一步,用 WebP 來顯示聊天界面的貼紙動畫。
BPG 是著名程序員 Fabrice Bellard 在去年 (2014年) 發布的一款超高壓縮比的圖片格式。這個程序員有些人可能感覺面生,但說起他的作品 FFmpeg、QEMU 大家想必是都知道的。BPG 使用 HEVC (即 H.265) 幀內編碼作為其算法基礎,就這點而言,它毋庸置疑是當下最為先進的圖片壓縮格式。相對於 JP2、JPEG-XR、WebP 來說,同等體積下 BPG 能提供更高的圖像質量。另外,得益於它本身基於視頻編碼算法的特性,它能以非常小的文件體積保存多幀動畫。 Fabrice Bellard 聰明的地方在於,他知道自己一個人無法得到各大浏覽器廠商的支持,所以他還特地開發了 Javascript 版的解碼器,任何浏覽器只要加載了這個 76KB 大小的 JS 文件,就可以直接顯示 BPG 格式的圖片了。目前阻礙它流行的原因就是 HEVC 的版權問題和它較長的編碼解碼時間。盡管這個圖片格式才剛剛發布一年,但已經有不少廠子開始試用了,比如阿裡和騰訊。
目前主流的移動端對圖片格式的支持情況如何呢?我們分別來看一下 Android 和 iOS 目前的圖片編解碼架構吧:
Android 的圖片編碼解碼是由 Skia 圖形庫負責的,Skia 通過掛接第三方開源庫實現了常見的圖片格式的編解碼支持。目前來說,Android 原生支持的格式只有 JPEG、PNG、GIF、BMP 和 WebP (Android 4.0 加入),在上層能直接調用的編碼方式也只有 JPEG、PNG、WebP 這三種。目前來說 Android 還不支持直接的動圖編解碼。
iOS 底層是用 ImageIO.framework 實現的圖片編解碼。目前 iOS 原生支持的格式有:JPEG、JPEG2000、PNG、GIF、BMP、ICO、TIFF、PICT,自 iOS 8.0 起,ImageIO 又加入了 APNG、SVG、RAW 格式的支持。在上層,開發者可以直接調用 ImageIO 對上面這些圖片格式進行編碼和解碼。對於動圖來說,開發者可以解碼動畫 GIF 和 APNG、可以編碼動畫 GIF。
兩個平台在導入第三方編解碼庫時,都多少對他們進行了一些修改,比如 Android 對 libjpeg 等進行的調整以更好的控制內存,iOS 對 libpng 進行了修改以支持 APNG,並增加了多線程編解碼的特性。除此之外,iOS 專門針對 JPEG 的編解碼開發了 AppleJPEG.framework,實現了性能更高的硬編碼和硬解碼,只有當硬編碼解碼失敗時,libjpeg 才會被用到。
由於我目前主要是做 iOS 開發,所以下面的性能評測都是基於 iPhone 的,主要測試代碼可以在這裡看到。測試素材很少,只有兩個:
第一張是Dribbble 的 Logo,包含 Alpha 通道,用於測試簡單的、圖形類的圖像。
第二張經典的 Lena 圖,用於測試照片類的、具有豐富細節的圖像。
每個圖像都有 64x64、128x128、256x256、512x512 四種分辨率。
測試素材過少可能導致某些測試不夠准確,但作為參考大致是沒問題的。
目前比較知名的 JPEG 庫有以下三個:
libjpeg:開發時間最早,使用最廣泛的 JPEG 庫。由於 JPEG 標准過於復雜和模糊,並沒有其他人去實現,所以這個庫是 JPEG 的事實標准。
libjpeg-turbo:一個致力於提升編解碼速度的 JPEG 庫。它基於 libjpeg 進行了改造,用 SIMD 指令集 (MMX、SSE2、NEON) 重寫了部分代碼,官網稱相對於 libjpeg 有 2 到 4 倍的性能提升。
MozJPEG: 一個致力於提升壓縮比的 JPEG 庫。它是 Mozilla 在 2014 年發布的基於 libjpeg-turbo 進行改造的庫,相對於 libjpeg 有 5% ~ 15% 的壓縮比提升,但相應的其編碼速度也慢了很多。
除了上面這三個庫,蘋果自己也開發了一個 AppleJPEG,但並沒有開源。其調用了芯片提供的 DSP 硬編碼和硬解碼的功能。雖然它不如上面這三個庫功能完善,但其性能非常高。在我的測試中,其編解碼速度通常是 libjpeg-turbo 的 1~2 倍。可惜的是,目前開發者並不能直接訪問這個庫。
下面是 ImageIO (AppleJPEG/libpng) 在 iPhone 6 上的編解碼性能:
可以看到,JPEG 編碼中 quality 越小,圖片體積就越小,質量越也差,編碼時間也越短。解碼時間並沒有很大的差距,可能是其大部分時間消耗在了函數調用、硬件調用上。蘋果在自己的相冊 Demo 中提供的 quality 的默認值是 0.9,在這個值附近,圖像質量和體積、編碼解碼時間之間都能取得不錯的平衡。
相對於 JPEG 來說,PNG 標准更為清晰和簡單,因此有很多公司或個人都有自己的 PNG 編碼解碼實現。但目前使用最廣的還是 PNG 官方發布的 libpng 庫。iOS 和 Android 底層都是調用這個庫實現的 PNG 編解碼。
下面是 PNG 在 iPhone 6 上的編解碼性能:
可以看到,在編解碼圖形類型(顏色少、細節少)的圖片時,PNG 和 JPEG 差距並不大;但是對於照片類型(顏色和細節豐富)的圖片來說,PNG 在文件體積、編解碼速度上都差 JPEG 不少了。
和 JPEG 不同,PNG 是無損壓縮,其並不能提供壓縮比的選項,其壓縮比是有上限的。目前網上有很多針對 PNG 進行優化的工具和服務,旨在提升 PNG 的壓縮比。下面是常見的幾個 PNG 壓縮工具的性能對比:
pngcrush 是 Xcode 自帶的 PNG 壓縮工具,相對於設計師用 Photoshop 生成的圖片來說,它能取得不錯的壓縮效果。ImageOptim 則更進一步,對每張圖用多種縮算法進行比對,選擇壓縮比更高的結果,進一步縮小了文件體積。TinyPNG.com 相對於其他工具來說,壓縮比高得不像話。它啟用了類似 GIF 那樣的顏色索引表對 PNG 進行壓縮,所以會導致顏色豐富的圖片丟失掉一部分細節。如果使用 TinyPNG 的話,最好在壓縮完成後讓設計師看一下顏色效果是否可以接受。
WebP 標准是 Google 定制的,迄今為止也只有 Google 發布的 libwebp 實現了該的編解碼 。 所以這個庫也是該格式的事實標准。
WebP 編碼主要有幾個參數:
lossless: YES:有損編碼 NO:無損編碼。WebP 主要優勢在於有損編碼,其無損編碼的性能和壓縮比表現一般。
quality: [0~100] 圖像質量,0表示最差質量,文件體積最小,細節損失嚴重,100表示最高圖像質量,文件體積較大。該參數只針對有損壓縮有明顯效果。Google 官方的建議是 75,騰訊在對 WebP 評測時給出的建議也是 75。在這個值附近,WebP 能在壓縮比、圖像質量上取得較好的平衡。
method: [0~6] 壓縮比,0表示快速壓縮,耗時短,壓縮質量一般,6表示極限壓縮,耗時長,壓縮質量好。該參數也只針對有損壓縮有明顯效果。調節該參數最高能帶來 20% ~ 40% 的更高壓縮比,但相應的編碼時間會增加 5~20 倍。Google 推薦的值是 4。
對於編碼無損圖片來說,quality=0, method=0~3 是相對來說比較合適的參數,能夠節省編碼時間,同時也有不錯的壓縮比。無損編碼圖片,quality=75, method=2~4 是比較合適的參數,能在編碼時間、圖片質量、文件體積之間有著不錯的平衡。
WebP 解碼有三個參數:
use_threads: 是否啟用 pthread 多線程解碼。該參數只對寬度大於 512 的有損圖片起作用。開啟後內部會用多線程解碼,CPU 占用會更高,解碼時間平均能縮短 10%~20%。
bypass_filtering: 是否禁用濾波。該參數只對有損圖片起作用,開啟後大約能縮短 5%~10% 的解碼時間,但會造成一些顏色過渡平滑的區域產生色帶(banding)。
no_fancy_upsampling: 是否禁用上采樣。該參數只對有損圖片起作用。在我的測試中,開啟該參數後,解碼時間反而會增加 5~25%,同時會造成一些圖像細節的丟失,線條邊緣會增加雜色,顯得不自然。
通常情況下,這三個參數都設為 NO 即可,如果要追求更高的解碼速度,則可以嘗試開啟 use_threads 和 bypass_filtering 這兩個參數。而 no_fancy_upsampling 在任何情況下都沒必要開啟。
由於 WebP 測試數據較多,這裡只貼一下 512x512 大小的一部分測試結果,感興趣的可以看文章結尾處的 Excel 附件。
對於簡單的圖形類型的圖像(比如 App 內的各種 UI 素材),WebP 無損壓縮的文件體積和解碼速度某些情況下已經比 PNG 還要理想了,如果你想要對 App 安裝包體積進行優化,可以嘗試一下 WebP。
對於復雜的圖像(比如照片)來說,WebP 無損編碼表現並不好,但有損編碼表現卻非常棒。相近質量的圖片解碼速度 WebP 相距 JPEG 也已經相差不大了,而文件壓縮比卻能提升不少。
BPG 是目前已知最優秀的有損壓縮格式了,它能在相同質量下比 JPEG 減少 50% 的體積。下面是經典的 Lena 圖的對比,你也可以在這裡看到大量其他圖片的 BPG、JPEG、JPEG2000、JPEG-XR、WebP 壓縮效果的在線對比,效果非常明顯。
BPG 目前只有作者發布的 libbpg 可用。但作者基於 libbpg 編譯出了一個 Javascript 解碼器,很大的擴展了可用范圍。bpg 可以以無損和有損壓縮兩種方式進行編碼,有損壓縮時可以用 quality 參數控制壓縮比,可選范圍為 0~51,數值越大壓縮比越高。通常來說,25 附近是一個不錯的選擇,BPG 官方工具默認值是 28。
libbpg 目前並沒有針對 ARM NEON 做優化,所以其在移動端的性能表現一般。下面是 iPhone 6 上的性能測試:
由於 bpg 編碼時間太長,我並沒有將數據放到表格裡。可以看到相同質量下,BPG 的解碼速度還是差 JPEG 太多,大約慢了 3~5 倍。目前來說,BPG 適用於那些對流量非常敏感,但對解碼時間不敏感的地方。從網上的新聞來看,手機淘寶和手機QQ都已經有所嘗試,但不清楚他們是否對 BPG 解碼進行了優化。
動圖在網絡上非常受歡迎,它近似視頻,但通常實現簡單、文件體積小,應用范圍非常廣泛。動圖的始祖是 GIF,它自 Windows 1.0 時代就在互聯網上流行開來,直到今天仍然難以被其他格式取代。盡管它非常古老,但其所用的原理和今天幾種新興格式幾乎一樣。
下面是一張 GIF 格式的 QQ 大表情:
這張表情由 6 幅靜態圖構成,每幅圖片有一定的存活時間,連貫播放就形成了動畫:
這幾張圖中,大部分內容是相近的,為了壓縮文件體積,通常動圖格式都支持一些特殊的方式對相似圖片進行裁剪,只保留前後幀不同的部分:
在解碼動圖時,解碼器通常采用所謂"畫布模式"進行渲染。想象一下:播放的區域是一張畫布,第一幀播放前先把畫布清空,然後完整的繪制上第一幀圖;播放第二幀時,不再清空畫布,而是只把和第一幀不同的區域覆蓋到畫布上,就像油畫的創作那樣。
像這樣的第一幀就被稱為關鍵幀(即 I 幀,幀內編碼幀),而後續的那些通過補償計算得到的幀被稱為預測編碼幀(P幀)。一個壓縮的比較好的動圖內,通常只有少量的關鍵幀,而其余都是預測編碼幀;一個較差的壓縮工具制作的動圖內,則基本都是關鍵幀。不同的動圖壓縮工具通常能得到不同的結果。
除此之外,動圖格式通常有更為詳細的參數控制每一幀的繪制過程,下面是 GIF/APNG/WebP 通用的幾個參數:
Disposal Method (清除方式)
Do Not Dispose:把當前幀增量繪制到畫布上,不清空畫布。
Restore to Background:繪制當前幀之前,先把畫布清空為默認背景色。
Restore to Previous:繪制下一幀前,把先把畫布恢復為當前幀的前一幀
Blend Mode (混合模式)
Blend None: 繪制時,全部通道(包含Alpha通道)都會覆蓋到畫布,相當於繪制前先清空畫布的指定區域。
Blend over:繪制時,Alpha 通道會被合成到畫布,即通常情況下兩張圖片重疊的效果。
上面這些技術,就是常見動圖格式的基礎了,下面分別介紹一下不同動圖格式的特點。
GIF 缺陷非常明顯:它通常只支持 256 色索引顏色,這導致它只能通過抖動、差值等方式模擬較多豐富的顏色;它的 Alpha 通道只有 1 bit,這意味著一個像素只能是完全透明或者完全不透明。
上面這是騰訊博客裡的一張演示圖,可以看到 GIF 由於 Alpha 通道的問題,產生了嚴重的"毛邊"現象。目前通常的解決方案是在圖片的邊緣加一圈白邊,以減輕這種視覺效果:
可以仔細觀察一下 QQ、微信等 App 裡面的動畫表情,幾乎每個表情都被一圈白邊所環繞,不得不說是一種很無奈的解決方案。
GIF 的制作工具有很多,但效果好、壓縮比高的工具非常少。對於已經制作好的 GIF 來說,用imagemagick 處理一下可以把文件體積壓縮不少。如果需要將視頻轉為 GIF,Cinemagraph Pro 是個不錯的傻瓜化工具。這裡有一篇文章介紹如何用 ffmpeg 壓縮 GIF,雖然參數調節有點麻煩,但效果非常理想。
下面是沒有經過優化的 GIF 和經過 ffmpeg 優化編碼的 GIF,可以看到差距非常大。
APNG 目前並沒有被 PNG 官方所接受,所以 libpng 並不能直接解碼 APNG。但由於 APNG 只是基於 PNG 的一個簡單擴展,所以在已經支持 PNG 的平台上,可以很輕松的用少量代碼實現 APNG 的編解碼。Chromium 為了支持 APNG 播放,只增加了不到 600 行代碼 ,我自己也用大概 500 行 C 代碼實現了一個簡單的 APNG 編解碼工具。另外,在支持 canvas 的浏覽器上,可以用 apng-canvas 直接顯示 APNG 動畫。APNG 壓縮最好的工具目前是 apngasm,大部分圖形化工具比如騰訊的 iSparta 都是基於這個工具開發的。
就目前而言, APNG 是 GIF 最好的替代了:實現簡單,可用范圍廣,壓縮比不錯,顯示效果好。
WebP 在 2010 年 發布時並沒有支持動圖。2012 年 libwebp v0.2 的時候,Google 才開始嘗試支持動畫,但其實現有很多問題,性能也非常差,以至於 Chrome 團隊一直都沒有接受。直到 2013 年,libwebp v0.4 時,動畫格式才穩定下來才被 Chrome 所接受。
WebP 動圖實際上是把多個單幀 WebP 數據簡單打包到一個文件內,而並不是由單幀 WebP 擴展而來,以至於動圖格式並不能向上兼容靜態圖。如果要支持動圖,首先在編譯 libwebp 時需要加上 demux 模塊,解碼 WebP 時需要先用 WebPDemuxer 嘗試拆包,之後再把拆出來的單幀用 WebPDecode 解碼。為了方便編譯,我寫了個腳本用於打包 iOS 的靜態庫,加入了 mux 和 demux 模塊。
Google 提供了兩個簡單的命令行工具用於制作動圖:gif2webp 能把 GIF 轉換為 WebP, webpmux 能把多個 WebP 圖片打包為動態圖,並且有著很多參數可以調節。這兩個工具對相近幀的壓縮並不太理想,以至於有的情況下壓縮比還不如 APNG,但除此以外也沒有其他什麼更好的工具可以用了。
BPG 本身是基於 HEVC (H.265) 視頻編碼的,其最開始設計時就考慮到了動圖的實現。由於它充分利用了 HEVC 的高壓縮比和視頻編碼的特性,其動圖壓縮比遠超其他格式。這裡和這裡有幾張 BPG 動圖示例,可以看到相同質量下 BPG 動圖只有 APNG/WebP/GIF 幾十分之一的大小。
我在這裡寫了個簡單的利用 libbpg 解碼動圖的方法,如有需要可以參考下。
我把下面這張 GIF 分別轉為 WebP、APNG、BPG 動圖,並在 iPhone 6 上對其所有幀進行解碼。
評測結果如下:
APNG 在文件體積上比 GIF 略有優勢,解碼時間相差不多。WebP 在體積和解碼時間上都具有較大的優勢。BPG 在體積上優勢最大,但解碼時間也最長。這麼看來,APNG 和 WebP 都是不錯的選擇,而 BPG 還有待性能優化。
最後做一個小廣告:如果你是 iOS 平台的開發者,可以試試我開發的 YYWebImage,它支持 APNG、WebP、GIF 動圖的異步加載與播放、編碼與解碼,支持漸進式圖像加載,可以替代 SDWebImage、PINRemoteImage、FLAnimatedImage 等開源庫。
上面提到的所有評測數據表格:image_benchmark.xlsx 推薦用 Excel 打開查看。
原文地址http://blog.ibireme.com/2015/11/02/mobile_image_benchmark/