編輯:關於Android編程
目前網絡中圖片仍然是占用流量較大的一部分,對於移動端更是如此,因此,如何在保證圖片視覺不失真前提下縮小體積,對於節省帶寬和電池電量十分重要。
然而目前對於JPEG、PNG、GIF等常用圖片格式的優化已幾乎達到極致,因此Google於2010年提出了一種新的圖片壓縮格式 – WebP,給圖片的優化提供了新的可能。
WebP為網絡圖片提供了無損和有損壓縮能力,同時在有損條件下支持透明通道。據官方實驗顯示:無損WebP相比PNG減少26%大小;有損WebP在相同的SSIM(Structural Similarity Index,結構相似性)下相比JPEG減少25%~34%的大小;有損WebP也支持透明通道,大小通常約為對應PNG的1/3。
同時,谷歌於2014年提出了動態WebP,拓展WebP使其支持動圖能力。動態WebP相比GIF支持更豐富的色彩,並且也占用更小空間,更適應移動網絡的動圖播放。
目前國內外各大互聯網公司已逐步使用WebP,科技博客GigaOM曾報道,YouTube的視頻縮略圖采用WebP後,網頁加載速度提升了10%;谷歌網上應用商店采用WebP後,每天可節省幾TB的帶寬,頁面平均加載時間大約減少1/3;谷歌移動應用市場采用WebP圖片格式後,每天節省了50TB的存儲空間;2014年騰訊新聞客戶端應用了WebP後,流量峰值帶寬降低9GB,網絡連接延時不變的前提下,平均圖片延時和數據下載延時降低了100ms;2014年空間裝扮也全量轉換成WebP,帶寬上也有顯著降低。(雖然聽說目前已轉成SharpP格式…)
WebP的優勢在於它具有更優的圖像數據壓縮算法,在擁有肉眼無法識別差異的圖像質量前提下,帶來更小的圖片體積,同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG 和 PNG 上的轉化效果都非常優秀、穩定和統一。
本文主要對WebP現狀與原理進行整理,並尤其研究其動圖格式在Android上的支持情況。
WebP的壓縮主要分為有損壓縮、無損壓縮以及有損帶透明通道壓縮。
有損WebP基於VP8視頻編碼中的預測編碼方法來壓縮圖像數據,其基本步驟類似於JPEG壓縮,主要包含格式轉換、分割子塊、預測編碼、FDCT、量化、Z排列、熵編碼,流程如下圖所示,紅色代表與JPEG不同的部分。
若壓縮前圖像數據為RGB格式,則需先進行格式轉換成YUV格式,Y表示亮度分量,UV表示色度分量。之所以轉換成YUV格式是因為人類視覺對亮度遠比色度敏感,所以可通過適當減少色度數據的存儲來節省數據占用的空間,但卻不會對視覺效果造成太大影響,如可每兩個或四個相鄰的像素點才保存一對UV值。
接下來將數據分割成一個個8x8或16x16的宏塊。
預測編碼的原理是基於前面編碼好的宏塊,預測多余的動作顏色等信息,屬於幀內預測。對各宏塊可使用以下幾種幀內預測模式:
H_PRED(horizontal prediction).使用block左邊的一列L來填充block中的每一列
V_PRED(vertical prediction):使用block上邊的一行A來填充block中的每一行
DC_PRED(DC prediction):使用L和A中所有像素的平均值作為唯一的值填充block
TM_PRED(TrueMotion prediction):使用漸進的方式,記錄上面一行的漸進差,以同樣的差值,以L為基准拓展每一行。
FDCT(Forward Discrete Cosine Transform,正向離散余弦變換)是將一組空間域的像素點轉變成頻域中的系數,對每個宏塊執行FDCT,使得變換後數據的低頻部分分布在數據塊的左上方,高頻部分集中在右下方,其中左上角第一個系數稱為直流系數,其他均為交流系數。
量化是壓縮中損失數據的主要步驟,它主要原理是把經過DCT變換後的宏塊中每個數值除以量化表中對應的系數並取整。其中量化表中高頻部分對應的系數比低頻部分系數要大得多,則在經過量化後,高頻部分的頻率系數被大大衰減甚至許多被清零,而低頻部分的頻率系數則較好地被保留。由於人眼對低頻部分更敏感,所以經過量化後再還原成圖像對視覺效果影響較小,但數據得到有效的壓縮。量化的最終目的是減少低頻部分非零系數的幅值並增加高頻部分零值系數的數量。
為更便於後續的編碼,需在編碼前對數據塊進行重新的排列,使得低頻部分的數據排在前面,高頻部分的數據排在後面,以增加數組中連續零值的數量,所以采用一種Z字型的排列方式。
可用DPCM(Differential Pulse Code Modulation,差分脈沖編碼調制)對直流系數進行編碼。由於直流系數的數值較大,且相鄰數據塊的直流系數相差不大,所以可使用DPCM對相鄰數據塊間量化後的直流系數差值進行編碼,從而提高壓縮比。
行程編碼是一種根據相同數據重復多次的情況簡化表示的算法,例如1111222222333按照行程編碼表示為(1,4)(2,6)(3,3)。由於量化後的交流系數中包含較多連續零值系數,因此可用行程編碼對它們進行編碼來有效壓縮數據長度。
熵編碼是一種無損數據壓縮編碼方式,WebP中采用布爾算術編碼作為熵編碼方式。和其它熵編碼方法不同的地方在於,其他的熵編碼方法通常是把輸入的消息分割為符號,然後對每個符號進行編碼,而算術編碼是直接把整個輸入的消息編碼為一個數,一個滿足(0.0 ≤ n < 1.0)的小數n。消息越長,編碼表示它的間隔就越小,表示這一間隔所需的二進制位就越多。
WebP還有一些細節上的步驟,比如自適應分塊(對不同區域的宏塊分配不同的壓縮參數)、環路濾波等。
為什麼有損WebP會比JPEG好?
主要原因是預測編碼。
自適應分塊也提供了較好表現。
環路濾波在中、低比特率的情況下有較大幫助。
算數編碼相比霍夫曼增強了5%~10%的壓縮能力。
無損WebP基於使用不同的技術對圖像數據進行轉換,包括:預測空間變換、色彩空間轉換、使用調色板、多像素打包成一個像素、alpha值替換等技術。對於熵編碼,則采用改進的LZ77-Huffman編碼來緊湊稀疏值,它是一種對距離值的2D編碼技術。
區別於有損WebP和無損WebP,這種編碼允許對RGB頻道的有損編碼同時可對透明度頻道進行無損編碼。由於這種形式目前其他的格式還未能提供,所以目前需要使用透明度的話都會使用無損的PNG,導致大小膨脹。對於這類圖片,WebP提供了較好的壓縮效果。相比有損的WebP,添加透明通道只增加22%的大小。
因此,將支持透明的PNG換成無損+支持透明的WEBP可以平均節省60%-70%大小,這個已經被一些含較多Icon的移動網站證明。
(如:https://github.com/EverythingMe/webp-test)
動態WebP的原理與GIF和APNG原理類似,每一幀記錄變化區域的坐標、長寬、播放延時等用於還原並播放。
一個WebP文件表示一個靜態圖片或動畫,並可選的包含透明度、色彩
配置文件和元數據等。
RIFF:ASCII字符RIFF。
File Size:文件大小,以字節為單位。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPldFQlCjukFTQ0lJ19a3+1dFQlCjrLHqyrbV4srHV2ViUM7EvP48L3A+DQo8aDQgaWQ9"32-有損webp簡要文件格式">3.2 有損WebP簡要文件格式
這種文件格式支持有損編碼,但不包含透明度以及其他拓展特性,可以被許多舊版本軟件支持。
VP8 chunk:
Chunk Header:VP8塊首部,定義了VP8比特流數據的大小,以及該幀VP8數據的長寬等信息。
VP8 data:VP8比特流數據。
VP8比特流格式的定義可參考rfc6386,主要定義了如何將圖像數據轉換成YUV格式。https://tools.ietf.org/html/rfc6386
這種格式用在無損WebP編碼(可選透明)並且不要求拓展特性時。需要注意的是,較舊的WebP軟件可能不支持該格式。
VP8L chunk:
屬性意義類似於有損WebP的簡要格式,其中VP8L的定義可參考文檔:
https://chromium.googlesource.com/webm/libwebp/+/master/doc/webp-lossless-bitstream-spec.txt
3.4 拓展格式
拓展格式包括:
VP8X用於指明使用的特性 可選的‘ICCP’用於色彩配置 可選的‘ANIM’用於動畫控制 可選的‘EXIF’用於EXIF元數據 可選的‘XMP’用於XMP元數據對於靜態圖像,圖像數據包含一幀數據,由以下組成:
一個可選的透明度子chunk 1個比特流子chunk對於動態圖像,圖像數據則包含多幀數據。
拓展頭部格式:
ICC profile(I):置位時表示包含ICC配置文件。
Alpha(L):當圖像包含透明數據時置位。
EXIF metadata(E)當包含EXIF元數據時置位。
XMP metadata(X):當包含XMP元數據時置位。
Animation(A):動態WebP置位,此時ANIM和ANMF數據塊中的數據將會被使用來控制動畫。
Canvas Width Minus One:畫布的真實寬度是該數值+1。
Canvas Height Minus One:畫布的真實高度是該數值+1。
動畫
WebP動畫被ANIM和ANMF塊所控制。
ANIM Chunk:
對於一個動圖,該塊數據定義了動畫的全局參數。
Background Color: 定義畫布的背景顏色,以BGRA的順序存儲。這個顏色會被填充到每幀數據沒用到的區域。
LoopCount:循環次數,0表示無限循環。
當動畫標識被置位時,該數據塊必須出現。當動畫標識位沒出現時,該數據塊會被忽略。
ANMF chunk:
對於動圖,該數據塊包含了一幀圖像的數據。
Frame X:該幀數據左上角X坐標為該值*2。
Frame Y:該幀數據左上角Y坐標為該值*2。
Frame Duration: 播放該幀後的延時時間,以ms為單位。
Blending method (B):標識如何混用前面畫布的相應透明像素點。置0時,處理完前面一幀圖像後,使用透明混合。置1時,不混合,渲染時直接覆蓋矩形區域。
Disposal method (D):標識該幀數據在被顯示後如何處理畫布。置0時不處理;置1時將畫布矩形區域轉換成ANIM定義的背景顏色。
Frame Data:以2字節為單位,包含圖像比特流數據以及可選的透明度數據。
Alpha
Pre-processing (P): 標識壓縮中使用了預處理。
Filtering method (F): 濾波方法。0-無過濾;1-橫向過濾;2-垂直過濾;3-梯度過濾。
Compression method (C): 0-無壓縮;1-使用WebP無損格式壓縮。
Alpha bitstream:編碼的透明度比特流數據
顏色配置文件、元數據數據的文件格式類似,主要是頭部ASCII碼不同。
帶透明有損WebP文件形如:
無損WebP形如:
無損WebP包含ICC配置文件和XMP元數據形如:
動態WebP包含EXIF元數據形如:
有損WebP vs JPEG:
谷歌使用Lenna、Kodak、Tecnick還有Image_crawl四個圖像來源來做實驗,在相同或稍高的SSIM基礎上,WebP相比JPEG體積降低25% ~ 34%。詳見:
https://developers.google.com/speed/webp/docs/webp_study
無損WebP 、有損帶透明WebP vs PNG
下圖是選取了1000張網絡中的PNG,對其進行壓縮後,與對應的無損WebP以及有損帶透明WebP比較。可見絕大部分圖片經過兩種WebP壓縮後均比PNG要小。
WebP的編碼時間較長,是PNG的5倍以上,但解碼速度與PNG差不多,甚至很多時候比PNG快。而WebP在編碼時占用內存比PNG高25%,解碼時比PNG低30%。
詳見:https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study
動態WebP vs GIF
優勢:
WebP支持24位RGB和8位透明通道,GIF僅支持8位色彩及1位透明度。
WebP支持無損和有損兩種模式,而且對於動態圖,能同時結合有損和無損的圖片。而GIF僅支持無損的壓縮。WebP的有損壓縮技術也更好地適應從現實世界視頻中創建的動圖。
WebP相比GIF占用更小的空間。Animated GIFs轉換為有損WebP減少64%,轉換成無損WebP減少19%,這對移動網絡十分重要。
WebP使用更短的解碼時間,WebP所用解碼時間是GIF的57%。
劣勢:
支持不夠普遍。
添加WebP支持到浏覽器需要添加較多代碼,但這在將來當WebP和WebM共享更多解碼代碼或者WebP被WebM合並後會有改善。
動態WebP vs APNG
APNG是一種基於PNG的編碼,對動圖的編碼方式類似於WebP,都是對變化的區域進行編碼。雖然理論上單張WebP要比PNG小,但有些整合成動圖形式後WebP會比APNG更大:
WEBP、APNG、GIF簡要比較表格
雖官方給出實驗示例中無論靜態或動態WebP都有比較好的壓縮表現,但實際上,自己嘗試用谷歌提供轉換工具轉換以及查詢某些網站上的示例,都顯示靜態和動態WebP的壓縮率均不太穩定,基於不同的圖片壓縮後的大小反而比JPEG或PNG更大。
在浏覽器上的支持:
在終端上的支持:
對於在App中使用WebP,除了Android 4.0以上提供了靜態WebP原生支持外,其他版本和IOS都可以直接使用官方提供的解析庫來支持靜態WebP
Android:https://github.com/alexey-pelykh/webp-android-backport
IOS:https://github.com/carsonmcdonald/WebP-iOS-example
然而,對於動態WebP,目前只能找到facebook的開源庫Fresco對其支持,不過Fresco最低僅支持API 9,且引用的相關庫較多。
https://www.fresco-cn.org/
WebP作為一種較新的圖片格式,在一定程度上提高了圖片的壓縮率,但目前壓縮表現尤其對於動圖,還較不穩定。
而Android對WebP動圖支持較差,目前僅有Fresco一個開源庫支持,要引入項目中,需進一步分離出無關的功能,並考慮最低僅支持API 9的問題。
關於ListView側滑刪除這是個老話題,大多數APP都具有這樣類似的功能,對於一位Android初涉者來說,實現這樣的功能確實有一點難度,網上的實現方法也層出不窮,我仔
Dalvik字節碼的指令格式指令格式分類Dalvik指令,根據需要的寄存器數目的不同,長度也有所不同。如下面的結構所示,有下面的這些情況: enum Format {
先看看效果圖:開源項地址:https://github.com/chrisbanes/Android-PullToRefresh 下拉刷新這個功能我們都比較常見
先看一下standard啟動模式的說明:默認啟動模式,每次激活Activity都會創建Activity實例,並放入Activity棧中下面我們通過一個實例來了解布局文件: