目錄

廣告 AD

Webp 圖片格式:比 JPG 還要小?

最近在想如何加快網頁圖片讀取速度

最簡單的方式應該就是降低圖片檔案大小了

但是總不可能縮小圖片尺寸來降低檔案大小

於是我把目標轉向了圖片格式…

廣告 AD

Google 在 2010 年的時候公布了 WebP 格式,到目前為止,已經有 94% 的瀏覽器支援這個圖片格式了。

這個格式的主要目標是在減少網路上圖片的大小,來藉此加快網頁的載入速度。

根據 Google 開發人員的說明1,跟 PNG 檔案相比,不失真的圖片可以減少 28% 以上的檔案大小。

WebP 格式同時支援不失真的壓縮以及失真壓縮,另外也支援 Alpha 通道,所以可以有透明的背景,而且,也有支援動畫,可以取代 GIF,降低檔案大小。

這邊我找了張風景照,原始圖片檔案是 JPG,檔案大小 668 KB,使用 80% 失真壓縮成 WebP 之後,檔案大小縮小成 378 KB,只有原本的 57% 左右。

以下圖片上方為原始圖片,下方為轉換後的圖片,不仔細看很難發現出差異,雖然我仔細看也看不出來

JPG 格式

WebP 格式

由於圖片檔案分在資料夾各個不同的地方,因此我打算寫一個 Python 檔案,來找到這些圖片檔案,並將他們換成 WebP 格式。

要用 Python 轉換成 WebP 的話可以直接使用 Pillow 套件,沒有安裝過的可以執行以下指令:

bash

pip install pillow

首先我們先找到所有圖片的路徑,這裡使用 glob 來找所有的 jpg 圖片。

由於我們要找尋資料夾內所有的子目錄,所以要加上 ** 和 recursive = True。

python

import glob
import os

directory = "content"

for img_path in glob.glob(os.path.join(directory, "**", "*.jpg"), recursive=True):
  print(img_path)

由於 glob.glob 是回傳 list,如果檔案數量很多的話,會占用一部份的記憶體,因此可以改成用 glob.iglob 來得到 iterator,這樣就不會占用記憶體了。

另外,如果圖片檔案格式有很多個的話,我們可以把每個格式都搜尋一遍,並且使用 chain.from_iterable 將這些 iterator 都串起來。

python

import glob
import os
from itertools import chain

directory = "content"
extensions = ["png", "jpg"]

def multiple_file_types(path, patterns):
  return chain.from_iterable( \
    glob.iglob(os.path.join(path, f"*.{pattern}"), recursive=True) for pattern in patterns)

for img_path in multiple_file_types(os.path.join(directory, "**"), extensions):
    print(img_path)

轉換的部分就比較簡單了,用 pillow 讀取圖片,儲存的時候以 WebP 的格式儲存即可。

python

fileName = "test.png"
newFileName = "test.webp"
im = Image.open(fileName)
im.save(newFileName)

最後 code 組合起來是這樣:

python

import glob
import os
from PIL import Image
from itertools import chain

def multiple_file_types(path, patterns):
  return chain.from_iterable(glob.iglob(os.path.join(path, f"*.{pattern}"), recursive=True) for pattern in patterns)

def convert(directory, extensions):
  for img_path in multiple_file_types(os.path.join(directory, "**"), extensions):
    print(img_path, end="  ")

    # 取得圖片名稱
    filename = os.path.splitext(img_path)[0]

    # 取得圖片原始檔案大小
    orig_file_size = os.path.getsize(img_path)

    # 轉成 webp
    im = Image.open(img_path)
    file_path_webp = f"{filename}.webp"
    im.save(file_path_webp)

    # 取得轉換後的檔案大小
    webp_file_size = os.path.getsize(file_path_webp)

    # 印出減少比例
    print(f"{(webp_file_size/orig_file_size)*100:.0f}%")

if __name__ == '__main__':
  convert("content", ["png", "jpg"])
  convert("static", ["png", "jpg"])

廣告 AD