鏈接:https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression
(看代碼設計的時候,真的大為震撼,偉大的algorithm T.T)
docs:QOI圖像格式
qoi
項目提出了Quite OK Image(QOI)格式,這是一種快速且無損的圖像壓縮方案。
它為開發者提供了核心庫API接口,能夠便捷地將原始像素數據編碼為QOI格式,或將QOI文件解碼還原為圖像。
項目還包含qoiconv
命令行工具用于圖像格式轉換,以及qoibench
工具用于性能評估(對比PNG等格式)。
架構
章節導航
- 圖像描述(
qoi_desc
)
- QOI文件格式
- QOI庫API接口
- 命令行轉換器(
qoiconv
)
- 基準測試工具(
qoibench
)
- 像素哈希與索引
- 行程長度編碼(QOI_OP_RUN)
- 差值編碼(QOI_OP_DIFF, QOI_OP_LUMA)
第一章:圖像描述結構體(qoi_desc
)
歡迎來到QOI(“Quite OK Image”)圖像格式的世界!
在這個開篇章節中,我們將深入探討QOI庫理解與處理圖像的基礎概念——qoi_desc
結構體。
圖像描述的定義
假設我們持有一張數字照片。在打印、屏幕顯示或發送給他人之前,計算機需要掌握其基本屬性:
- 圖片寬度是多少?
- 圖片高度是多少?
- 使用標準RGB色彩模式還是包含透明通道的RGBA模式?
- 色彩空間如何解析(例如明亮或暗淡)?
這正是
qoi_desc
結構體所提供的"身份證"功能。
這個精煉的結構體包含了QOI庫處理圖像所需的所有基礎維度
與色彩屬性
信息,而無需接觸實際像素數據。
qoi_desc
的核心構成
該結構體包含四個關鍵要素:
組件 | 描述 |
---|---|
width | 圖像寬度,以像素為單位 |
height | 圖像高度,以像素為單位 |
channels | 每像素的色彩分量數。通常為3 (RGB三通道)或4 (RGBA四通道含透明) |
colorspace | 色彩解析方式。常規圖像使用QOI_SRGB (標準sRGB),線性未校正數據使用QOI_LINEAR 。初學者只需理解這是色彩的"風味"定義 |
在qoi.h
頭文件中可見其具體定義:
// 摘自:qoi.htypedef struct {unsigned int width; // 圖像寬度(像素單位)unsigned int height; // 圖像高度(像素單位)unsigned char channels; // 3代表RGB,4代表RGBAunsigned char colorspace; // 0代表sRGB,1代表線性色彩空間
} qoi_desc;// 色彩空間常量定義
#define QOI_SRGB 0
#define QOI_LINEAR 1
這就是我們圖像身份證的藍圖。
讀取圖像時的qoi_desc
應用
圖像處理中最常見的操作之一是從文件載入圖像。
當通過QOI庫加載QOI圖像時,首先需要從文件頭讀取qoi_desc
信息,該操作由qoi_read
函數完成。
以qoiconv.c
格式轉換工具中的簡化示例說明:
// 摘自:qoiconv.c// 1. 聲明qoi_desc類型變量(當前為空身份證)
qoi_desc desc;// 2. 調用qoi_read加載圖像
// - argv[1]是輸入文件名(如"input.qoi")
// - &desc指向空結構體,qoi_read將填充該結構體
// - 0表示"使用文件中指定的通道數"
void *pixels = qoi_read(argv[1], &desc, 0);// 若pixels非空(讀取成功),desc將包含圖像的完整元數據
// 例如可獲取:
// int image_width = desc.width;
// int image_height = desc.height;
// int image_channels = desc.channels;
此時desc
變量已載入完整圖像參數,pixels
變量則存儲實際像素數據。
寫入圖像時的qoi_desc
應用
保存QOI文件時,需通過qoi_desc
告知庫文件圖像參數。參考qoiconv.c
中的保存示例:
// 摘自:qoiconv.c// 假設w、h、channels已存儲圖像參數
// pixels包含實際像素數據// 調用qoi_write保存圖像
// - argv[2]是輸出文件名(如"output.qoi")
// - 第三個參數即時構建qoi_desc結構體
int encoded = qoi_write(argv[2], pixels, &(qoi_desc)
{.width = w,.height = h,.channels = channels,.colorspace = QOI_SRGB // 明確指定sRGB色彩空間
});
此處我們主動構建圖像身份證并傳遞給編碼函數。
底層實現機制
QOI文件以14字節的頭部起始
,精確存儲qoi_desc
信息。
QOI編碼*qoi_encode
流程:
解碼*qoi_decode
過程示意圖:
QOI庫通過qoi_write_32
和qoi_read_32
輔助函數處理32位整數的讀寫,channels
和colorspace
則作為8位值處理。編碼函數核心實現:
// 摘自:qoi.h(QOI_IMPLEMENTATION段)void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {unsigned char *bytes; // QOI文件數據緩沖區int p = 0; // 緩沖區當前位置指針// 寫入魔數標識qoi_write_32(bytes, &p, QOI_MAGIC);// 寫入描述信息qoi_write_32(bytes, &p, desc->width);qoi_write_32(bytes, &p, desc->height);bytes[p++] = desc->channels;bytes[p++] = desc->colorspace;// ...(后續編碼過程)return bytes;
}
對應解碼實現:
// 摘自:qoi.h(QOI_IMPLEMENTATION段)void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {const unsigned char *bytes = (const unsigned char *)data;int p = 0;// 讀取并驗證魔數unsigned int header_magic = qoi_read_32(bytes, &p);// 填充描述變量desc->width = qoi_read_32(bytes, &p);desc->height = qoi_read_32(bytes, &p);desc->channels = bytes[p++];desc->colorspace = bytes[p++];// ...(后續解碼過程)return pixels;
}
總結
本章闡明了qoi_desc
作為QOI圖像元數據核心容器的關鍵作用,其包含的寬度、高度、通道數(RGB/RGBA)和色彩空間參數,既是圖像編碼的起點,也是解碼過程的基石。
下一章將深入解析這些元數據在QOI文件格式中的具體組織方式。
第二章:QOI 文件格式
在第一章:圖像描述(qoi_desc
)中,我們學習了qoi_desc
結構體,它就像一張"身份證",保存著圖像的寬度、高度和色彩類型等關鍵信息。
但僅僅了解圖像的元數據是不夠的;我們需要一種將圖像永久存儲在計算機磁盤上的方法,以便保存、共享或后續加載。
從像素到文件
假設我們有一張精美的數碼照片,當前僅以原始像素數據的形式存在于計算機內存中。為了永久保存,需要將其寫入文件。
但如何將這些信息——圖像的尺寸、顏色及壓縮方式——組織成單個文件,且保證任何QOI兼容程序都能正確解析?
這正是QOI文件格式要解決的問題。
它就像一本詳盡的食譜,精確說明圖像數據在.qoi
文件內的結構組織和壓縮方式。遵循這個規范,任何支持QOI的程序都能正確讀寫圖像,確保所有人看到的畫面完全一致。
.qoi
文件的內部結構
.qoi
文件采用極簡結構設計,便于計算機快速處理。我們可以將.qoi
文件視為專為圖像設計的容器,包含三個主要部分:
- 文件頭:位于文件起始位置,作為整張圖像的"身份證"
- 像素數據:圖像的實際顏色信息,通過智能分塊實現高效存儲
- 結束標記:文件末尾的特殊標識,宣告數據終止
讓我們深入解析每個部分。
第一部分:文件頭——圖像的增強版"身份證"
QOI文件始終以文件頭開始,固定長度為14字節。這個頭部至關重要,因為它包含第一章討論的所有圖像元數據。
除了qoi_desc
中的width
(寬度)、height
(高度)、channels
(通道數)和colorspace
(色彩空間)外,文件頭還包含4字節的"魔法標識":字符序列"qoif"
。這個魔法字符串告知打開文件的程序:“請注意,這是QOI圖像文件!”
QOI庫編碼圖像時,首先寫入這個頭部。以下是qoi_encode
(qoi_write
的核心編碼函數)的起始部分:
// 來源:qoi.h(位于 QOI_IMPLEMENTATION 中)
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {unsigned char *bytes; // 存儲文件原始字節int p = 0; // 'p' 跟蹤當前寫入位置// 1. 寫入魔法標識"qoif"qoi_write_32(bytes, &p, QOI_MAGIC); // QOI_MAGIC 是數值形式的'qoif'// 2. 寫入圖像寬度qoi_write_32(bytes, &p, desc->width);// 3. 寫入圖像高度qoi_write_32(bytes, &p, desc->height);// 4. 寫入通道數(3表示RGB,4表示RGBA)bytes[p++] = desc->channels;// 5. 寫入色彩空間(0表示sRGB,1表示線性)bytes[p++] = desc->colorspace;// ... 函數后續部分將寫入像素數據塊return bytes;
}
這段代碼顯示,文件最開始的字節依次是魔法標識、圖像寬度、高度、通道數和色彩空間。
這種設計確保讀取文件時,程序無需解碼像素數據即可立即獲取圖像基本信息。
第二部分:像素數據——智能打包的"數據塊"
文件頭之后是圖像顏色數據的主體部分。QOI在此展現其精妙的壓縮技術:不采用逐個像素記錄RGBA值的低效方式,而是使用多種"數據塊"。
這些數據塊如同不同類型的指令集。例如:
- “重復前一個像素10次”(運行塊/
QOI_OP_RUN
) - “此像素與前一個略有不同”(差值塊/
QOI_OP_DIFF
) - “此像素與之前某位置完全相同”(索引塊/
QOI_OP_INDEX
) - “直接記錄完整像素值”(
RGB/RGBA塊
,當其他塊不適用時使用)
QOI編碼器智能]選擇最緊湊的數據塊類型,這種智能打包使QOI文件體積小于未壓縮圖像。我們將在后續章節(如第七章:行程編碼和第八章:差值編碼)深入解析各數據塊類型。
像素始終按行編碼,從左到右,從圖像左上角開始
。
第三部分:結束標記——終止信號
每個QOI文件以固定的結束標記收尾:7個0x00
字節(全零)后接1個0x01
字節(一)。
結束標記如同電影的"劇終"提示,對解碼器至關重要。
它明確標識圖像數據的終點,防止解碼器越界讀取
可能導致錯誤或誤將隨機內容解析為像素數據。
以下是qoi_encode
添加結束標記的實現:
// 來源:qoi.h(位于 QOI_IMPLEMENTATION,編碼函數末尾)
void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {// ...(文件頭和像素塊的編碼代碼)// 寫入8字節結束標記for (int i = 0; i < (int)sizeof(qoi_padding); i++) {// qoi_padding 是預定義數組 {0,0,0,0,0,0,0,1}bytes[p++] = qoi_padding[i];}*out_len = p; // 更新文件總大小return bytes;
}
此循環簡單地將預定義的8字節序列寫入文件,標記數據終止。
完整的QOI文件結構
下表總結.qoi
文件的完整結構,展示各部分的順序和大致大小:
組成部分 | 大小(字節) | 描述 |
---|---|---|
魔法標識 ("qoif" ) | 4 | 標識QOI圖像文件 |
寬度 | 4 | 圖像寬度(像素單位,如1920) |
高度 | 4 | 圖像高度(像素單位,如1080) |
通道數 | 1 | 3 表示RGB圖像,4 表示RGBA(含透明度) |
色彩空間 | 1 | 0 表示sRGB(標準色彩空間),1 表示線性色彩空間 |
像素數據塊 | 可變 | 壓縮后的像素信息,由運行塊、差值塊、索引塊等組成,構成文件主體 |
結束標記 | 8 | 固定序列(7個0x00 后接0x01 ),標識文件結束 |
這種簡潔可預測的結構是QOI文件編解碼速度優異的關鍵。
庫函數如何處理文件格式
通過QOI庫API函數,我們可以直觀理解編解碼過程與文件格式的交互。
編碼圖像(寫入.qoi
文件)
使用qoi_write
(內部調用qoi_encode
)保存圖像時,庫函數按QOI規范組織數據:
QOI庫如同專業主廚,將原始像素"食材"按精確配方打包成標準文件。
解碼圖像(讀取.qoi
文件)
使用qoi_read
(內部調用qoi_decode
)加載文件時,庫函數按順序解析各部分數據:
此時,QOI庫如同細心的讀者,逐步解析文件各部分:先驗證"身份證",再按"配方步驟"重構圖像,最終確認終止標記。
總結
本章建立了對QOI文件格式的基礎認知:每個.qoi
文件包含固定大小的文件頭(元數據標識)、可變長度的像素數據塊(智能壓縮),以及明確的**結束標記**。
這種簡潔、高效且可預測的結構設計,正是QOI卓越性能的核心。
接下來,我們將通過QOI庫API深入探討如何以編程方式操作這些組件。