經(jīng)過多個版本迭代,項目在release
配置下的打包體積依舊輕松破百,應(yīng)用體積過大導(dǎo)致的問題包括:
- 更長的構(gòu)建時間,換個詞就是
加班
-
TEXT
段體積過大會導(dǎo)致審核失敗 - 用戶不愿意下載應(yīng)用
通常來說,資源文件能在應(yīng)用體積包中占據(jù)1/3
或者更多的體積,相比起代碼(5kb/千行)
的平均占用來說,對圖片進行減包是最直接高效的手段,對圖片資源的處理方式包括四種:
- 通過請求下載大圖
- 使用工具壓縮圖片
- 查找刪除重復(fù)圖片
- 查找復(fù)用相似圖片
考慮到由于項目開發(fā)分工的問題,方式1
需要推動落地,所以本文不討論這種處理方式。其他三種都能通過編寫腳本實現(xiàn)自動化處理
圖片壓縮
圖片壓縮分為有損壓縮
和無損壓縮
兩類,有損壓縮
放棄了一部分圖片的質(zhì)量換取更高的壓縮比。網(wǎng)上主流的壓縮工具有tinypng
、pngquant
、ImageAlpha
和ImageOptim
等,分別采用了一種或者多種壓縮技術(shù)完成圖片壓縮
為什么png能夠無損壓縮
由于png
格式的靈活性,同一張圖片可以使用多種方式進行表示,不同方式占用的大小不一樣。一般的軟件會采用效率更高的方式來表示圖片,所以這種情況下png
圖片存在巨大的優(yōu)化空間。通常來說,從png
文件中能去除的數(shù)據(jù)包括:
-
iTXt
、tEXt
和zTXt
這些可以存儲任意文本的數(shù)據(jù)區(qū)段 -
iCCP
數(shù)據(jù)區(qū)段存儲的profile
等等 -
photoshop
導(dǎo)出的png
圖片存在大量的額外信息
png
圖片有兩種類型的數(shù)據(jù)塊,一種是必不可缺的數(shù)據(jù)塊稱為關(guān)鍵數(shù)據(jù)塊
。另一種叫做輔助數(shù)據(jù)塊
,png
文件格式規(guī)范指定的輔助數(shù)據(jù)塊包括:
- 背景顏色數(shù)據(jù)塊
bKGD
- 基色和白色數(shù)據(jù)塊
cHRM
- 圖像
γ
數(shù)據(jù)塊gAMA
- 圖像直方圖數(shù)據(jù)塊
hIST
- 物理像素尺寸數(shù)據(jù)塊
pHYs
- 樣本有效位數(shù)據(jù)塊
sBIT
- 文本信息數(shù)據(jù)塊
tEXt
- 圖像最后修改時間數(shù)據(jù)塊
tIME
- 圖像透明數(shù)據(jù)塊
tRNS
- 壓縮文本數(shù)據(jù)塊
zTXt
其中tEXt
和zTXt
數(shù)據(jù)段中存在的數(shù)據(jù)包括:
關(guān)鍵字 | |
---|---|
Title | 圖像名稱 |
Author | 圖像作者 |
Description | 圖像說明 |
Copyright | 版權(quán)聲明 |
CreationTime | 原圖創(chuàng)作時間 |
Software | 創(chuàng)作圖像使用的軟件 |
Disclaimer | 棄權(quán) |
Warning | 圖像內(nèi)容警告 |
Source | 創(chuàng)作圖像使用的設(shè)備 |
Comment | 注釋信息 |
由上可見,輔助數(shù)據(jù)塊在png
文件中可能占據(jù)了極大的篇幅,正是這些數(shù)據(jù)塊構(gòu)成了png
的無損壓縮條件
tinypng
tinypng
采用了一種稱作Quantization
的壓縮技術(shù),通過合并圖片中相似的顏色,將24bit
的圖片文件壓縮成8bit
圖片,同時去除圖片中不必要的元數(shù)據(jù),圖片最高能達(dá)到70%
以上的壓縮率。截止文章完成之前,tinypng
僅提供了線上壓縮功能,暫未提供工具下載
pngquant
根據(jù)官方介紹,pngquant
將24bit
以上的圖片轉(zhuǎn)換成8bit
的保留透明度通道的壓縮圖片,壓縮算法的壓縮比非常顯著,通常都能減少70%
的大小。pngquant
提供了命令行工具來完成解壓任務(wù):
pngquant --quality=0-100 imagepath
命令行更多調(diào)試參數(shù)可以在官網(wǎng)參閱
ImageAlpha
ImageAlpha
是一個macOS
系統(tǒng)下的有損圖片壓縮工具,內(nèi)置了pngquant
、pngnq-s9
等多個壓縮工具,多數(shù)情況下通過將圖片降至8bit
來獲取高壓縮比。由于ImageAlpha
的可視化界面無法批量處理圖片,直接使用提供的命令工具可以實現(xiàn)批量壓縮圖片:
for file in $(ls $1); do
imagepath=$1"/"$file
if [ -d imagepath ]
then
/// 路徑為文件夾
else
if [[ $file == *.png ]]
then
beforeSize=`ls -l $imagepath | awk '{print $5}'`
/Applications/ImageAlpha.app/Contents/MacOS/pngquant $imagepath
afterSize=`ls -l ${imagepath/.png/-fs8.png} | awk '{print $5}'`
if [[ $afterSize -lt $beforeSize]]
then
mv ${imagepath/.png/-fs8.png} $imagepath
fi
fi
fi
done
使用ImageAlpha
需要注意兩點:
- 壓縮后的圖片命名會自動添加
-fs8
后綴,需要使用mv
命令實現(xiàn)替換 - 有損壓縮會修改
關(guān)鍵數(shù)據(jù)塊
,可能導(dǎo)致壓縮圖片尺寸增大,需要過濾
在使用有損壓縮
時需要注意單張png
圖片是可以被多次壓縮的,但這會導(dǎo)致圖片的清晰度和色彩都受到影響,不建議對圖片超過一次以上的有損壓縮
ImageOptim
ImageOptim
是介紹的四種工具中唯一的無損壓縮
,它采用了包括去除exif信息
、重新排列像素存儲方式
等手段實現(xiàn)圖片的壓縮。無損
代表著一張圖片被ImageOptim
壓縮后,后續(xù)無法再次進行壓縮,同時它的壓縮比往往比不上其他的有損壓縮
方案,但最大程度上保證了圖片的原始清晰度和色彩
for file in $(ls $1); do
imagepath=$1"/"$file
if [ -d imagepath ]
then
/// 路徑為文件夾
else
if [[ $file == *.png ]]
then
/Applications/ImageOptim.app/Contents/MacOS/ImageOptim $imagepath
fi
fi
done
ImageOptim
同樣存在可視化的工具并且支持批量壓縮圖片
多方案對比
考慮到ImageAlpha
幾乎都是使用pngquant
作為壓縮工具,因此只列出三種壓縮工具的對比:
原始尺寸 | 壓縮工具 | 壓縮后尺寸 | 壓縮比 |
---|---|---|---|
319.5KB | tinypng | 120.5KB | 62% |
319.5KB | ImageAlpha-pngquant | 395KB | -24% |
319.5KB | ImageOptim | 252KB | 21% |
測試圖片采用qq
聊天截圖生成的png
,tinypng
壓縮率非常高,而pngquant
的表現(xiàn)不盡人意
刪除重復(fù)圖片
通常來說,出現(xiàn)重復(fù)圖片的原因包括模塊間需求開發(fā)沒有打通
或是缺少統(tǒng)一的圖片命名規(guī)范
。通過圖片MD5
摘要是識別重復(fù)圖片的最快方法,以python
為例,匹配重復(fù)圖片的代碼如下:
md5list = {}
for file in files:
if os.path.isdir(file.path):
continue
md5obj = hashlib.md5()
fd = open(file.path, 'rb')
while True:
buff = fd.read(2048)
if not buff:
break
md5obj.update(buff)
fd.close()
filemd5 = str(md5obj.hexdigest()).lower()
if filemd5 in md5list:
md5list[filemd5].add(file.path)
else:
md5list[filemd5] = set([file.path])
for key in md5list:
list = md5list[key]
if len(list) > 1:
print (list)
在遍歷中以文件MD5
字符串作為key
,維護具備相同MD5
的圖片路徑,最后遍歷這個map
查找存在一個以上路徑的數(shù)組并且輸出
尋找相似圖片
相似圖片在圖片內(nèi)容、色彩上都十分的接近,多數(shù)時間可以考慮復(fù)用這些圖片,但相似圖片的問題在于無法通過MD5
直接匹配。為了確認(rèn)兩個圖片是否相似,要使用簡單的一個數(shù)學(xué)公式來幫忙查找:
方差。在概率論和統(tǒng)計學(xué)中,一個隨機變量的方差描述的是它的離散程度,也就是該變量離其期望值的距離
舉個例子,甲同學(xué)五次成績分別是65, 69, 81, 89, 96
,乙同學(xué)五次成績是82, 80, 77, 81, 80
,兩個人平均成績都是80
,但是引入方差公式計算:
甲: ((65-80)^2 + (69-80)^2 + (81-80)^2 + (89-80)^2 + (96-80)^2) / 5 = 136.8
乙: ((82-80)^2 + (80-80)^2 + (77-80)^2 + (81-80)^2 + (80-80)^2) / 5 = 2.8
平均值相同的情況下,方差越大,說明數(shù)據(jù)偏離期望值的情況越嚴(yán)重。方差越接近的兩個隨機變量,他們的變化就越加趨同,獲取方差代碼如下:
def getVariance(nums):
variance = 0
average = sum(nums) / len(nums)
for num in nums:
variance += (num - average) * (num - average) / len(nums)
return variance
因此將圖片劃分成連串的一維數(shù)據(jù),以此計算出圖片的方差,通過方差匹配可以實現(xiàn)一個簡單的圖片相似度判斷工具,實現(xiàn)前還要注意兩點:
- 圖片
RGB
色彩值會導(dǎo)致方差的計算變得復(fù)雜,所以轉(zhuǎn)成灰度圖可以降低難度 - 不同尺寸需要縮放到相同尺寸進行計算
最終將圖片轉(zhuǎn)換成一維數(shù)據(jù)列表的代碼如下:
def getAverageList(img):
commonlength = 30
img = cv2.resize(img, (commonlength, commonlength), interpolation=cv2.INTER_CUBIC)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
res = []
for idx in range(commonlength):
average = sum(gray[idx]) / len(gray[idx])
res.append(average)
將圖片轉(zhuǎn)成灰度圖后,仍然可能存在RGB
色值不同但灰度值相同的情況導(dǎo)致判斷失準(zhǔn),可以考慮兩種方案提高算法的檢測準(zhǔn)確率:
- 在不修改以灰度值計算方差的方案下,構(gòu)建以
列平均像素值
為單位的一維列表計算另一個方差,兩個方差值一并做判斷 - 摒棄灰度值方差方案,每一行分別生成
R
、G
、B
三種色彩平均值的一維列表,計算出三個方差進行匹配檢測
效果
經(jīng)過兩輪圖片減包處理后,整個項目資源產(chǎn)生的減包量約有20M
,其中通過文中的三種手段產(chǎn)生的減包量在6.5M
左右,整體上來看產(chǎn)出還是比較可觀的