我們是如何記錄圖片的?

語言: CN / TW / HK

作為 Web 開發者,我們日常需要與各式各樣的圖片格式打交道,以至於有些知識幾乎已經成為常識,比如我們應該都知道 PNG 可以支援透明度,jpg 可以壓縮到較低的質量,而 gif 則可以顯示動圖……但是,你知道這些不同的圖片格式是如何產生的、並且演進至今的嗎?

起源

最早的圖片格式如今已經不可考證,但可以肯定的是,從計算機誕生之初,數字圖片就已經存在了,正如早在沒有計算機時我們就創造了“楊輝三角”這樣的圖形。不過在網際網路誕生之後,由於資訊(biao qing bao)傳遞的效率飛速提升,我們才有了更多機會看到數字圖片格式的發展。

1982 年,隨著網路社交的開始,網際網路上開始出現了一種被稱為 ASCII Art 的藝術形式。人們開始使用一些字元來構成一些複雜的圖形:

██████╗ ██████╗ ███╗   ███╗███╗   ███╗ █████╗ ███████╗
██╔════╝██╔═══██╗████╗ ████║████╗ ████║██╔══██╗██╔════╝
██║     ██║   ██║██╔████╔██║██╔████╔██║███████║███████╗
██║     ██║   ██║██║╚██╔╝██║██║╚██╔╝██║██╔══██║╚════██║
╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚═╝ ██║██║  ██║███████║
 ╚═════╝ ╚═════╝ ╚═╝     ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚══════╝

這實際上就是一種非常直觀的計算機圖片儲存方式:在上面這張圖裡,我們可以把每一個單元格看成是顯示螢幕的一個光學元件。當這些光學元件展示一張圖片時,有些可能不發光(對應上面的空白部分),有些可能具有不同的顏色(對應上面的 █ 或者 ║)。通過這種方式,我們用一種非常符合計算機直覺的“編碼”儲存了這張圖片——這裡我不妨將其命名為 「Commas 編碼」 吧。

通過 Commas 編碼,「我們只需要儲存一個二維陣列,對應圖中的不同位置,在陣列的每一項記錄對應的字元(顏色)」。這就是我們現在看到的絕大多數圖片的儲存方式:「點陣圖」,或者用術語來描述:「點陣圖」。

如果使用我們上面描述的方式儲存這張圖片,它的儲存效率是怎樣的呢?

沒錯,實際上上面的程式碼塊就是圖片檔案本身!因為我們用了 Unicode 而非二進位制的方式儲存,它的畫素數是 55*6,每一個畫素需要用一個 Unicode 字元儲存。假設我們使用 UCS-2 編碼,則這張圖片使用我們的編碼方式需要 「660B」 的儲存空間。

PNM: 色彩與二進位制

在真實的計算機世界裡,真的有像 Commas 編碼這種圖片格式嗎?事實上還真有。在全球資訊網還沒有誕生的年代,就已經有了一種用於電子郵件傳輸的圖片格式,它叫做 PBM(Portable BitMap)。在現在,它使用 .pbm​ 字尾和 image/x‑portable‑bitmap MIME 型別。

PBM 是一種單色圖片,這意味著它只有黑色和白色兩種顏色,在遊戲美術中這通常被稱為 1-bit。在儲存時,我們可以使用 0 和 1 代表黑白兩種顏色。比如上面的 COMMAS 圖片,如果用 PBM 它的編碼方式如下:

P1
# This is comment
55 6
0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 0 0 0 1 1 1 0 0 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0
1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 1 1 0 0 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 0 1 1 0 0 0 1 1 0 1 1 0 0 0 0 0 0
1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0
1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 0 1 1 0 0 0 1 1 0 0 0 0 0 0 1 1 0
0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 1 1 1 1 1 0

你可以將它儲存為一個 `.pbm` 檔案,並利用 MacOS 的圖片檢視器檢視它。

比起 Commas 編碼,PBM 失去了表達顏色的能力,但多了註釋結構。不過單色顯然是無法滿足我們傳送表情包的需要的,至少連黑白電視也是支援不同亮度的黑色的。為了讓 PBM 支援灰度,我們可以將 0 和 1 擴充套件為 0-255,這就是 PGM;更進一步地,我們還可以用 255 255 255 來將每個畫素擴充套件為真彩色,也就是我們熟悉的 RGB 編碼,這就是 PPM。在 PBM 開頭的 P1 就起到了描述編碼的作用,P1 P2 和 P3 分別代表 PBM、PGM 和 PPM 三種格式,他們被統稱為 PNM。

現在,我們來考慮一下儲存空間吧。PBM 使用 ASCII 明文儲存,上面的圖片文字長度是 575,因此它佔用 575B。當然我們還可以進一步壓縮,在去除註釋後,它的長度是 557B,但也僅限於此了;如果使用支援色彩的 PPM,每個畫素可能要佔 11 個文字長度,於是一張簡單的 55*6 的 COMMAS Logo 圖需要使用 「3,857B」 來儲存!

有什麼方法可以進一步壓縮圖片呢?聰明的你一定可以想到,我們可以使用二進位制。PBM 的編碼可以使用 P4-P6 表示它使用二進位制儲存。於是我們可以用兩個位元組表示編碼,兩個 Int32 也就是 8 個位元組表示尺寸,後面的 330 個畫素可以將每一個真彩色轉換成 3 個位元組的色彩空間,這樣我們一共只需要 1,000B 就能夠儲存上面的 COMMAS Logo 圖了,而且還是支援真彩色的!

GIF: 空間與編碼壓縮

在 20 世紀 80 年代,1K 的尺寸依然是無法想象的 —— 只是一張小小的 Logo,竟然比 Commas 的使用文件還要大!有什麼辦法能夠繼續優化尺寸呢?

在那個年代,在我們甚至很難購買到支援真彩色的顯示屏的時候,使用真彩色編碼圖片無疑是一種浪費。我們也可以縮小色彩空間來針對每個畫素減少其佔用的空間。在 1-bit 之外,同樣有一種常見於畫素遊戲的畫風叫做 8-bit,意思就是用 8bit 也就是 1 個位元組來儲存顏色,我們只需要將顏色限制為 256 種,就能將用於色彩的儲存進一步壓縮。假設我們將 PPM 的 Commas Logo 縮小到 256 種顏色的範圍,每一個畫素就能用 1 個位元組來儲存了,這樣上面的 1,000B 的 COMMAS Logo 圖就能壓縮到 340B,甚至比文字格式的 PBM 圖還要小!

玻璃的光影只需要使用四種顏色即可完成。

另一方面,學過資料結構的同學能夠想到一種常見的壓縮方式:「霍夫曼編碼」。簡單地來說就是我們可以記錄一份字典,用更小的位元序列來記錄更常出現的字元。比如 Commas,如果用 ASCII 的話,每一個字母都要 1 個位元組也就是 8bit,但如果用 1 表示 m,10 表示 c,11 表示 o……我們只需要 12 bit 就能表達這個字串了!只要字典佔用的空間小於壓縮減少的空間,霍夫曼編碼就能有效減少檔案尺寸。

沿著這樣的思路,一種主流的圖片格式終於誕生了,它就是 GIF。儘管我們現在提起 GIF 就會想到動圖,但實際上,GIF 是最早流行起來的圖片的編碼方式。GIF 的全稱是 「Graphics Interchange Format」,也就是“影象交換格式”的意思,顯然它就是為了高效傳輸而誕生的。

GIF 使用了一種叫做 LZW 的壓縮技術,它與幾乎所有現代壓縮軟體師出同源。比起標準的霍夫曼編碼,它的字典是動態生成的,因此需要傳輸的尺寸將會進一步減小。所以 GIF 就相當於用壓縮檔案來編碼圖片,這能不厲害嘛!

另外,在推出後不久,Netscape Navigator 支援了 GIF 的多幀動畫,以及定義動畫重複能力的功能——這下表情包王者們發揮的空間來了!GIF 很快風靡網際網路,包含 Netscape 定義的動畫格式變成了事實標準。

那麼,為什麼現在除了動圖之外,我們都不再使用 GIF 了呢?一方面是由於 GIF 僅支援 256 種顏色,對於攝影圖片來說這幾乎是無法忍受的;另一方面則是因為一家公司的騷操作:

GIF 是歷史上首個提供電子郵件服務的公司 CompuServe 開發並開放給大家使用的,但是另一家公司優利系統卻聲稱自己申請了 「LZW 中 W 的專利」 ,需要大家交納高額的專利費……於是很快,開源社群開始抵制 GIF 並開始開發新的圖片格式。因此儘管 2006 年開始 GIF 不再被專利限制,一個巨人已經悄然站在了 GIF 的前面,它就是—— PNG。

PNG: 開放

PNG 的全稱是 「Portable Network Graphics」,即“行動式網路圖片”。這意味著從設計之初它就面向:

  • 面向 Web 展示場景。
  • 更小的體積。
  • 取代 GIF。實際上,確實如此!

PNG 的誕生離不開 GIF 的作死,正如 PNG 也被解釋為 「PNG is Not GIF」……PNG 從誕生之初就是完全無需許可的,由 IETF 作為 RFC 2083 釋出,正如我們所知道的很多網際網路基礎協議一樣(例如 RFC 791 IP、RFC 1034 DNS、RFC 2616 HTTP/1.1~~ 等等),並在釋出後不久就成為了 W3C 的推薦標準。PNG 能夠取代 GIF 是一件很容易理解的事情:畢竟,誰不喜歡白嫖呢?更何況 PNG 完美解決了 GIF 的另一個痛點:PNG 支援真彩色!!!

其次,PNG 還有一個很大的特點:「向前相容」。

PNG 格式與 PNM 和 GIF 類似,都由一個協議頭+多個內容塊構成,但 PNG 在標準制訂時就規定了塊可以分為兩類:「關鍵資料塊」 和 「輔助資料塊」。PNG 解析器必須支援關鍵資料塊的解析,而對於輔助資料塊則是能識別就識別,不能識別可以忽略。

這個特性有什麼用呢?我們可以重新回顧一下,我們會在什麼場景下使用 GIF 而不是 PNG 呢?顯然,最常見的場景就是上面所說的動圖。在 2004 年,Mozilla 推出了 APNG 格式。APNG 將動畫的第一幀作為 PNG 的關鍵資料塊,而後續幀和其他動畫資訊則作為輔助資料塊,這樣即使是在不支援 APNG 的場景下,這些圖片也能展示為靜態的圖片了。

注意哦,這是一張 PNG 格式的圖!

另一個你可能感興趣的問題或許是,我們經常使用 TinyPNG 這樣的工具來對 PNG 做壓縮,那麼一張 PNG 圖片是如何被壓縮的呢?尤其是在我們已經知道,GIF 為其壓縮演算法保留了專利的情況下?

實際上 PNG 和 GIF 的演算法非常相似。前面我們提到,GIF 為 LZW 中的 W 部分聲明瞭專利,而剩下的 LZ 部分實際上就是 LZW 的原始演算法——LZ77,它來自於名字首字母 L 和 Z 的兩位大佬在 1977 年提出的壓縮演算法。PNG 的壓縮正是基於 LZ77 的另一種演算法:DEFLATE,這也正是 Web 領域常見的 GZIP 的壓縮演算法。儘管細節不同,但基於動態字典的思路與 GIF 是類似的,這也保證了 PNG 具有高效的壓縮效率。

JPEG: 有失真壓縮

事情已經發展到了這一階段:PNG 幾乎就要一統江湖了,但是為什麼沒有呢?最主要的原因依然是 LZ77——它的壓縮效率相比原始資料已經很高了,但是還遠遠不夠。像 LZ77 這樣的壓縮演算法與霍夫曼編碼類似:資料多樣性越差,壓縮效率就越高。這意味著對於早期網際網路上的那種剪貼畫,PNG 是很有優勢的;但隨著影象複雜程度升高,PNG 能夠壓縮的空間也越來越有限了。有什麼辦法能夠壓縮那些複雜的圖片,例如攝影作品呢?

JPEG 就是這樣誕生的。JPEG 全稱 「Joint Photographic Experts Group」,也就是“聯合影象專家組”的意思,我們通常所說的 JPEG 就是這個專家組提出的第一種編碼標準,在 JPEG 之後實際上還有 JPEG 2000 這樣更高階的標準,但並沒有能夠撼動 JPEG 的地位(後文會提到)。事實上,JPEG 比 PNG 的出現還要更早,但比起 GIF 粗暴的 256 色化和 PNG 偏執的無失真壓縮,JPEG 採取了一種更實用主義的策略:「面向人眼識別的有失真壓縮」。

不同於我們在 PNM 中使用的 RGB 色彩空間,JPEG 使用的是一種叫做 YUV 的色彩空間。YUV 三個字母分別代表亮度、色調和飽和度,這和現代更流行的 HSL 很接近。正如設計師們推崇 HSL 的主要原因是 HSL 更接近於人眼對色彩的感受方式,因此更適合做漸變處理,YUV 也被廣泛應用電視色彩調頻領域,原因就在於:「人眼對於亮度的感受要高於色調和飽和度」。你可以試試開啟或關閉手機的夜覽模式和原彩顯示,再試試調整亮度,就會發現,我們對亮度的敏感程度是非常高的。

不僅如此,「人眼對於在一定範圍內的亮度差異較為敏感」,例如我們會明顯感覺到 10% 和 20% 比 20% 和 25% 更接近,但卻難以區分 10% 和 90% 與 20% 和 80% 兩種對比的差異。通過一些矩陣變換的技巧,我們可以將頻域上的一些細節捨棄,這個過程被稱為「量化」。這是 JPEG 有失真壓縮的最主要來源,通常 JPEG 壓縮時可以選擇壓縮質量,影響的就是量化過程的係數。

從上面這張圖我們就可以看出來,一些亮度變化明顯的部分是 JPEG 損失最多的地方,例如鳥喙周圍會有明顯的噪點。儘管如此,對於我們通常看到的圖片,JPEG 都能在合理地保證質量的前提下大幅壓縮影象的尺寸,這尤其體現在一些「繪畫作品」中,因為這些作品通常都有相對均勻的亮度。

然而,專利問題在 JPEG 身上依然未能倖免,2002 年一家公司宣稱自己擁有 JPEG 的專利,這在 PC 領域引發軒然大波,這與 JPEG 專家組創立之初期望的免版稅標準背道而馳。此案經過 4 年的扯皮,最終以庭外和解的方式解決了。此事件同時暴露了 JPEG 2000 存在的專利風險,導致儘管 JPEG 2000 在技術上更加先進,卻最終沒有流行起來。

WebP: 金聲玉振

資料壓縮是一個非常經典的資訊學問題。我們之所以能夠壓縮資料,往往是因為這些資料「本身存在冗餘資訊」。例如霍夫曼編碼就利用了不同的資料出現的頻率不一致,這就是一種統計學冗餘。但不論如何,我們都沒有辦法在 1bit 裡面傳遞 2bit 的資訊,因為資訊也需要受到熵的限制,

截至此時,也許我們已經把所有能夠使用的方式都窮盡了:我們嘗試了二進位制編碼、壓縮演算法,甚至針對人眼的觀感放棄了影象的一部分資訊……但是,真的結束了嗎?

開動我們的腦筋仔細想想,會不會有一種可能,我們可以將圖片資訊中更多的部分「變成冗餘」呢?

讓我們把視線從圖片轉向其他領域。有一個看起來和 JPEG 很有關係的格式是 MPEG,但是實際上二者的關係就和張飛與王菲差不多。但正如 JPEG 是專家組的名稱,MPEG 實際上也是如此,它是一個提出了音視訊領域多種編碼標準的組織。我們通常所說的 MPEG 實際上是 MPEG-1,是一種音訊標準,它最經典的應用是 MPEG-1 第 3 音訊層,俗稱 MP3。而 MPEG 釋出的最具影響力的標準當屬 MPEG-4,其中包含了 27 個子部分。每一個部分分別規定的視訊的編碼、控制、示例、優化方式等等。我們最熟悉的應當是第 14 部分,俗稱 MP4。

在 MPEG-4 中,視訊編碼的部分是由第二部分(XVID)和第十部分(AVC)組成的,其中 AVC 更加高階;同時,由於 AVC 是 MPEG 和國際電信聯盟共同制定的,因此這個規範在國際電信聯盟也有一個別名,它就是 「H.264」。

H.264 通過只記錄幀與幀之間的變化,得以將視訊大幅壓縮,也就是我們通常所說的 「運動補償」。顯然比起 GIF 對圖片的每幀都進行儲存,這種方式的儲存效率要高出幾個數量級!除了運動補償外,還有一些高階的壓縮手段,例如“「幀內預測」”。這意味著,在解碼器對某一幀進行解碼時,「可以根據某個單元相鄰的單元來預測該單元的值,從而使視訊檔案只需要記錄實際值和預測值的差值就足夠了」。由於其極高的壓縮效率,H.264 很快統治了視訊編碼領域。

但是,正如 GIF 和 JPEG 面對的一樣,MPEG 的標準實際上是包含專利的。因此,很多公司試圖開發能夠代替 H.264 的視訊編碼。Google 在 2010 年收購的一家叫做 On2 的公司就是做這件事情的。收購完成不久後,Google 就在當年的 Google I/O 上宣佈開源 On2 的最新視訊編碼 VP8,為什麼呢?因為與此同時,Google 推出了一個新的開源視訊格式,也就是本章節的主角的兄弟——WebM。

我們現在知道,WebM 實際上並未掀起多大波瀾,但在當時仍然是劃時代般的進步,尤其是在半年後,Google 基於 VP8 技術再次給出了新的圖片格式 WebP 的時候。在經歷了那麼多版權和專利糾紛後,Web 領域終於又有了「開源+免版稅」的新格式,並且還是圖片+視訊打包的!

正是因為 WebP 技術是從 VP8 衍生而來的,因此它繼承了視訊領域的有失真壓縮手段,這就是前面提到的 「幀內預測」。WebP 會使用每個塊上方的三個塊和左側的三個塊進行預測,並且包含了 H.264 的四種幀內預測模式,這也就是 WebP 能夠做到比 PNG 更小的原因——通過幀內預測實現了更多原始資訊的冗餘化。在此基礎之上,WebP 同樣使用了字典編碼等等無失真壓縮技術,從而使圖片的尺寸降到了儘可能低的程度。

展望

WebP 已經是圖片壓縮的終結了嗎?當然不!

在 WebP 誕生之初實際上不支援無失真壓縮和透明通道,並且它的有失真壓縮甚至不如 JPEG;但正是因為開源的力量,讓 WebP 逐漸成為了目前最具優勢的主流 Web 圖片格式。那麼現在,除了 WebP 我們還有哪些選擇呢?

如果說有哪種格式表現超越 WebP,BPG 一定是其中的一個。前面我們提到,WebP 的實現來自於 VP8,而 VP8 又來自於 H.264。現在你也許知道,H.264 已經不足以滿足如今大量的 4k、8k 甚至更高的顯示需要,因此 2013 年的時候國際電信聯盟釋出了新的視訊標準 H.265,學名叫做 HEVC —— 「High Efficiency Video Coding」,高效視訊編碼。HEVC 在 H.264 之上做了諸多改進,例如幀內預測就從 4 種模式上升到了 33 種!

BPG 使用了基於 HEVC 幀內預測演算法的有失真壓縮,這意味著它的效能要顯著領先於 JPEG 甚至 WebP。然而,由於 HEVC 與 H.264 一樣保留版權,這也成為了 BPG 並沒有大規模流行的主要原因。

在無失真壓縮領域有沒有呢?顯然也是有的,FLIF(https://github.com/FLIF-hub/FLIF) 就是其中的典型。FLIF 是 2015 年釋出的,並且也是完全開源的。FLIF 採用和 H.264 類似的一種動態學習的壓縮演算法,在無失真壓縮方面將壓縮效能優化到了新高度。但作為一個純社群專案,FLIF 在釋出後不久就逐漸式微,但其核心思路被 JPEG 的新標準 JPEG XL 繼承,並且 JPEG XL 最終也在 2020 年作為免版稅標準釋出;BPG 和 FLIF 也最終啟發了 MPEG,釋出了基於 HEVC 的圖片格式 HEIF。

WebP 的後現代藝術

最後

故事的最後則重新回到我們的卷王 Google 身上。在 VP8 對標 H.264 而國際電信聯盟釋出了 H.265 之後,Google 推出了 VP9 來對標 H.265;儘管不及預期,但最終促成了 AOMedia(開放標準聯盟,由 Google、蘋果、微軟、Facebook、Mozilla 等建立)的成立。基於 VP10 的 AV1 正是 AOMedia 開發的用以代替 H.265 的最新免版稅標準。

2021 年起 Google 開始開發下一代 WebP,被稱為 WebP 2,便是基於 AV1 視訊編碼。或許在不久之後,我們就能看到新的圖片格式橫空出世,並建立它的天下。