我們是如何記錄圖片的?

來自內部 「孫翛然」 分享,原文鏈接:https://scrapbook-cyansalt.vercel.app/how-we-record-pictures,這文章寫的很細很有邏輯性,真把我看跪了。

作爲 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」,即 “便攜式網絡圖片”。這意味着從設計之初它就面向:

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 視頻編碼。或許在不久之後,我們就能看到新的圖片格式橫空出世,並建立它的天下。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ADPfJ4SnQk0UF4PIRBZjsw