質量監控-圖片減包
經過多個版本迭代,專案在release
配置下的打包體積依舊輕鬆破百,應用體積過大導致的問題包括:
- 更長的構建時間,換個詞就是
加班
TEXT
段體積過大會導致稽核失敗- 使用者不願意下載應用
通常來說,資原始檔能在應用體積包中佔據1/3
或者更多的體積,相比起程式碼(5kb/千行)
的平均佔用來說,對圖片進行減包是最直接高效的手段,對圖片資源的處理方式包括四種:
- 通過請求下載大圖
- 使用工具壓縮圖片
- 查詢刪除重複圖片
- 查詢複用相似圖片
考慮到由於專案開發分工的問題,方式1
需要推動落地,所以本文不討論這種處理方式。其他三種都能通過編寫指令碼實現自動化處理
圖片壓縮
圖片壓縮分為有失真壓縮
和無失真壓縮
兩類,有失真壓縮
放棄了一部分圖片的質量換取更高的壓縮比。網上主流的壓縮工具有tinypng
、pngquant
、ImageAlpha
和ImageOptim
等,分別採用了一種或者多種壓縮技術完成圖片壓縮
為什麼png能夠無失真壓縮
由於png
格式的靈活性,同一張圖片可以使用多種方式進行表示,不同方式佔用的大小不一樣。一般的軟體會採用效率更高的方式來表示圖片,所以這種情況下png
圖片存在巨大的優化空間。通常來說,從png
檔案中能去除的資料包括:
iTXt
、tEXt
和zTXt
這些可以儲存任意文字的資料區段iCCP
資料區段儲存的profile
photoshop
匯出的png
圖片存在大量的額外資訊
png
圖片有兩種型別的資料塊,一種是必不可缺的資料塊稱為關鍵資料塊
。另一種叫做輔助資料塊
,png
檔案格式規範指定的輔助資料塊包括:
- 背景顏色資料塊
bKGD
- 基色和白色資料塊
cHRM
- 影象
γ
資料塊gAMA
- 影象直方圖資料塊
hIST
- 物理畫素尺寸資料塊
pHYs
- 樣本有效位資料塊
sBIT
- 文字資訊資料塊
tEXt
- 影象最後修改時間資料塊
tIME
- 影象透明資料塊
tRNS
- 壓縮文字資料塊
zTXt
其中tEXt
和zTXt
資料段中存在的資料包括:
關鍵字 | |
---|---|
Title | 影象名稱 |
Author | 影象作者 |
Description | 影象說明 |
Copyright | 版權宣告 |
CreationTime | 原圖創作時間 |
Software | 創作影象使用的軟體 |
Disclaimer | 棄權 |
Warning | 影象內容警告 |
Source | 創作影象使用的裝置 |
Comment | 註釋資訊 |
由上可見,輔助資料塊在png
檔案中可能佔據了極大的篇幅,正是這些資料塊構成了png
的無失真壓縮條件
tinypng
tinypng
採用了一種稱作Quantization
的壓縮技術,通過合併圖片中相似的顏色,將24bit
的圖片檔案壓縮成8bit
圖片,同時去除圖片中不必要的元資料,圖片最高能達到70%
以上的壓縮率。截止文章完成之前,tinypng
僅提供了線上壓縮功能,暫未提供工具下載
pngquant
根據官方介紹,pngquant
將24bit
以上的圖片轉換成8bit
的保留透明度通道的壓縮圖片,壓縮演算法的壓縮比非常顯著,通常都能減少70%
的大小。pngquant
提供了命令列工具來完成解壓任務:
pngquant --quality=0-100 imagepath
複製程式碼
命令列更多除錯引數可以在官網參閱
ImageAlpha
ImageAlpha
是一個macOS
系統下的有損圖片壓縮工具,內建了pngquant
、pngnq-s9
等多個壓縮工具,多數情況下通過將圖片降至8bit
來獲取高壓縮比。由於ImageAlpha
的視覺化介面無法批量處理圖片,直接使用提供的命令工具可以實現批量壓縮圖片:
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
命令實現替換 - 有失真壓縮會修改
關鍵資料塊
,可能導致壓縮圖片尺寸增大,需要過濾
在使用有失真壓縮
時需要注意單張png
圖片是可以被多次壓縮的,但這會導致圖片的清晰度和色彩都受到影響,不建議對圖片超過一次以上的有失真壓縮
ImageOptim
ImageOptim
是介紹的四種工具中唯一的無失真壓縮
,它採用了包括去除exif資訊
、重新排列畫素儲存方式
等手段實現圖片的壓縮。無損
代表著一張圖片被ImageOptim
壓縮後,後續無法再次進行壓縮,同時它的壓縮比往往比不上其他的有失真壓縮
方案,但最大程度上保證了圖片的原始清晰度和色彩
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
的表現不盡人意
刪除重複圖片
通常來說,出現重複圖片的原因包括模組間需求開發沒有打通
或是缺少統一的圖片命名規範
。通過圖片MD5
摘要是識別重複圖片的最快方法,以python
為例,匹配重複圖片的程式碼如下:
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
查詢存在一個以上路徑的陣列並且輸出
尋找相似圖片
相似圖片在圖片內容、色彩上都十分的接近,多數時間可以考慮複用這些圖片,但相似圖片的問題在於無法通過MD5
直接匹配。為了確認兩個圖片是否相似,要使用簡單的一個數學公式來幫忙查詢:
方差。在概率論和統計學中,一個隨機變數的方差描述的是它的離散程度,也就是該變數離其期望值的距離
舉個例子,甲同學五次成績分別是65, 69, 81, 89, 96
,乙同學五次成績是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
複製程式碼
平均值相同的情況下,方差越大,說明資料偏離期望值的情況越嚴重。方差越接近的兩個隨機變數,他們的變化就越加趨同,獲取方差程式碼如下:
def getVariance(nums):
variance = 0
average = sum(nums) / len(nums)
for num in nums:
variance += (num - average) * (num - average) / len(nums)
return variance
複製程式碼
因此將圖片劃分成連串的一維資料,以此計算出圖片的方差,通過方差匹配可以實現一個簡單的圖片相似度判斷工具,實現前還要注意兩點:
- 圖片
RGB
色彩值會導致方差的計算變得複雜,所以轉成灰度圖可以降低難度 - 不同尺寸需要縮放到相同尺寸進行計算
最終將圖片轉換成一維資料列表的程式碼如下:
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)
複製程式碼
將圖片轉成灰度圖後,仍然可能存在RGB
色值不同但灰度值相同的情況導致判斷失準,可以考慮兩種方案提高演算法的檢測準確率:
- 在不修改以灰度值計算方差的方案下,構建以
列平均畫素值
為單位的一維列表計算另一個方差,兩個方差值一併做判斷 - 摒棄灰度值方差方案,每一行分別生成
R
、G
、B
三種色彩平均值的一維列表,計算出三個方差進行匹配檢測
效果
經過兩輪圖片減包處理後,整個專案資源產生的減包量約有20M
,其中通過文中的三種手段產生的減包量在6.5M
左右,整體上來看產出還是比較可觀的