JPEG 檔案格式
有損壓縮的點陣圖格式,照片的通用格式。
- MIME 類型
- image/jpeg
- 副檔名
- .jpg, .jpeg
- 分類
- 圖片
Magic number(檔案簽章)
| 0x00000000 | FFD8FF | ··· | Start-Of-Image marker |
關於 JPEG 格式
JPEG 使用針對照片調校的離散餘弦轉換來壓縮,以幾乎看不出來的少量畫質換取檔案大小的大幅下降。檔案以 Start-Of-Image 標記 FF D8 開頭,並以 FF D9 結尾。這兩個標記之間全部是一連串的 segment,每一段都由 FF 加一個標記位元組帶頭,而且幾乎每一段緊接著都有兩個位元組的大端序長度 — 剖析器不必看懂任何一段,也能靠這個長度一路走完整個 JPEG。像素本身住在 Start-Of-Scan 標記之後的熵編碼串流裡,而那段串流是全檔唯一沒有長度欄位的部分:解碼器只能往前掃到下一個真正的標記,才知道它在哪裡結束。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 2 | Start of Image marker | FF D8。少數幾個沒有長度也沒有酬載的標記之一。這份目錄的簽章讀三個位元組而不是兩個,是因為下一個標記開頭的 FF 緊跟在後面,多要求這一個位元組就能排除掉剛好以 FF D8 開頭的檔案。 |
| 2 | 2 | First application marker | FF E0 帶出 APP0,代表檔案遵循 JFIF;FF E1 帶出 APP1,通常代表 Exif。相機和手機寫 APP1,影像編輯器習慣先寫 APP0 再寫 APP1,兩個都有的檔案完全正常。 |
| 4 | 2 | Segment length | 這一段 segment 的位元組數,大端序,含這兩個位元組本身,但不含前面那兩個標記位元組。沒有縮圖的 APP0 會讀到 00 10 — 十六 — 所以下一個標記從 4 加 16、也就是位移 20 開始。 |
| 6 | 5 | JFIF identifier | 4A 46 49 46 00,ASCII 的 JFIF 加一個 NUL。如果排在最前面的是 APP1,位移 6 放的會是 45 78 69 66 00 00 這六個位元組,整段就是 Exif 而不是 JFIF。 |
| 11 | 2 | JFIF version | 主版本號和次版本號各一個位元組:01 01 是 1.01,01 02 是 1.02。只有在 APP0 是第一段時才存在;Exif 排前面的檔案裡,這兩個位元組已經是 TIFF 結構的一部分了。 |
| 13 | 1 | Density units | 0 代表後面那組密度數字只表示長寬比,1 代表每英吋點數,2 代表每公分點數。很多編碼器這裡寫 0,卻還是照樣填上 72 和 72 — 這就是為什麼從 JPEG 推算出來的列印尺寸常常毫無意義。 |
| 14 | 4 | Horizontal and vertical density | 兩個大端序 16 位元數字,先 X 後 Y,怎麼解讀要看前面那個單位位元組。這是 JFIF 版本的 pHYs,而且沒有任何東西會讓它跟 PNG 那邊保持一致。 |
| 18 | 2 | Thumbnail width and height | 寬和高各一個位元組,幾乎一律是 00 00。如果不是零,位移 20 之後會跟著寬乘高乘三個位元組的未壓縮 RGB,而位移 4 的段長度也會跟著變大 — 用手算就能對得起來。 |
| -2 | 2 | End of Image marker | FF D9,跟 SOI 一樣沒有長度。它後面的多餘位元組合法到解碼器會直接忽略,ZIP 壓縮檔和 shell script 就是這樣被夾帶進影像檔的。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| JFIF/Exif | 兩種都是 JPEG,差別完全由位移 2 的標記決定。FF E0 加上位移 6 的 4A 46 49 46 00 是 JFIF,而它能告訴你的就只有那組密度。FF E1 加上位移 6 的 45 78 69 66 00 00 是 Exif,位移 12 開始是一整份 TIFF 標頭 — 49 49 2A 00 或 4D 4D 00 2A,接著四個位元組指向第一個目錄。相機型號、曝光、方向和 GPS 都在那裡。一個檔案可以同時帶兩段,這時值得讀的是 Exif 那段。 |
| HEIC | 這兩種格式在同一批手機上搶同一個工作,切換它們的設定藏在相機 app 裡的某個開關。JPEG 在位移 0 是 FF D8 FF。HEIC 的位移 0 只是一個 box 長度,位移 4 才讀得到 66 74 79 70 68 65 69 63。iPhone 用「最相容」模式匯出的照片是 JPEG,其他都是 HEIC。 |
| PNG | FF D8 FF 對上 89 50 4E 47。與其相信副檔名不如查一下,因為存成 .png 的 JPEG 還是 JPEG,每個瀏覽器照樣畫得出來;也因為把截圖重新編成 JPEG,是圖裡文字糊掉最常見的單一原因。 |
| JPEG 2000 | 只是名字撞在一起的另一種編碼。純 JPEG 2000 codestream 開頭是 FF 4F FF 51;裝成 box 的 JP2 檔案形式開頭是 00 00 00 0C 6A 50 20 20 0D 0A 87 0A。兩種瀏覽器都解不開,所以從科學或典藏來源下載的 .jp2 看起來才會像壞掉。 |
常見的壞法
- 截斷。標頭還在,所以檢視器知道尺寸、也畫得出照片上半部,接著熵編碼串流用完,畫面剩下的部分不是留在灰色就是重複最後一個區塊。判斷方式是看最後兩個位元組:完好的 JPEG 以 FF D9 結尾。
- 掃描資料裡多了或少了一個位元組。霍夫曼編碼沒有位元組對齊可以重新同步,所以損壞之後的每一個區塊都用錯誤的 DC 預測值解出來,影像從那一列開始化成一條條偏移的色帶。除非編碼器有寫 restart 標記,否則救不回來。
- 文字模式傳輸。那套毀掉 PNG 的換行轉換,在這裡一樣會改寫壓縮資料裡的 0D 0A,而 JPEG 沒有逐段的檢查碼可以察覺。檔案打得開、標頭完美無缺,下面三分之二糊成一片。
- Exif 目錄壞掉。APP1 的段長度是對的,但 TIFF 結構裡某個位移指到段尾之外,照片的方向標籤就此遺失,在有些 app 裡轉了九十度、在另一些裡卻是正的。把整段拿掉可以修好顯示,代價是中介資料全滅。
- 伺服器錯誤頁或多餘的 BOM 塞在前面。FF D8 FF 不在位移 0 了,嚴格的解碼器直接拒收,寬鬆的則往前掃過去照樣開起來,於是就出現那個熟悉的場面:圖在瀏覽器裡好好的,在 build 裡壞掉。
這個格式現在還在用嗎
JPEG 已經三十歲,網路上幾乎每一張照片還是用它送出去的。沒有第二種格式是被有史以來每一台相機、手機、瀏覽器、印表機、掃描器和數位相框都解得開的,而這種通用性比 WebP、AVIF、HEIC 能省下的那兩成到五成更值錢。它真正吃虧的是所有邊緣銳利的東西 — 截圖、示意圖、文字、線稿 — 因為區塊轉換抹掉的正好就是這類影像賴以成形的高頻細節;另一個是會被反覆編輯再存檔的東西,畢竟每存一次就是一次全新的有損編碼,傷害會累積。它完全沒有 alpha 通道,所以需要透明背景的 logo 不可能是 JPEG。原本的接班人 JPEG XL 能對既有 JPEG 做無損重壓縮,每個位元組換到的畫質也更好,但瀏覽器支援停在半路,今天還不是能拿來上線的選項。實務上現在的答案是:把 JPEG 留在 picture 元素裡當 fallback,吃得下 WebP 或 AVIF 的就送新格式。
在這個站上打開 JPEG 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 JPEG 搞混的格式,可以比對它們的開頭位元組。