[QOI] qoi_desc | qoi_encode | qoi_decode

鏈接: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等格式)。

架構

在這里插入圖片描述

章節導航

  1. 圖像描述(qoi_desc)
  2. QOI文件格式
  3. QOI庫API接口
  4. 命令行轉換器(qoiconv)
  5. 基準測試工具(qoibench)
  6. 像素哈希與索引
  7. 行程長度編碼(QOI_OP_RUN)
  8. 差值編碼(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_32qoi_read_32輔助函數處理32位整數的讀寫,channelscolorspace則作為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文件視為專為圖像設計的容器,包含三個主要部分:

  1. 文件頭:位于文件起始位置,作為整張圖像的"身份證"
  2. 像素數據:圖像的實際顏色信息,通過智能分塊實現高效存儲
  3. 結束標記:文件末尾的特殊標識,宣告數據終止

讓我們深入解析每個部分。

第一部分:文件頭——圖像的增強版"身份證"

QOI文件始終文件頭開始,固定長度為14字節。這個頭部至關重要,因為它包含第一章討論的所有圖像元數據

除了qoi_desc中的width(寬度)、height(高度)、channels(通道數)和colorspace(色彩空間)外,文件頭還包含4字節的"魔法標識":字符序列"qoif"。這個魔法字符串告知打開文件的程序:“請注意,這是QOI圖像文件!”

QOI庫編碼圖像時,首先寫入這個頭部。以下是qoi_encodeqoi_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)
通道數13表示RGB圖像,4表示RGBA(含透明度)
色彩空間10表示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深入探討如何以編程方式操作這些組件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89420.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89420.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89420.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智慧城軌可視化:一屏智管全城

圖撲智慧城軌可視化系統&#xff0c;把地鐵線路、車站、列車都搬進三維畫面。列車晚點預警、站臺擁擠提示、設備故障定位…… 這些關鍵信息一屏聚合&#xff0c;調度員能快速調整發車頻次&#xff0c;疏導高峰客流。遇上突發情況&#xff0c;系統聯動應急方案&#xff0c;同步顯…

包新的Git安裝與使用教程(2024九月更新)

目錄 一、安裝git 1.下載git 2.git安裝 3.環境變量配置與測試 二、使用教程 1.創建版本庫 2.版本回退 3.刪除和恢復文件 一、安裝git 1.下載git 官方下載地址&#xff1a;https://git-scm.com/download 然后進入以下頁面&#xff0c;點擊下載鏈接即可(windows一般都是…

中望3D 2026亮點速遞(1)-全新槽功能螺紋功能,減少繁瑣操作

本文為CAD芯智庫整理&#xff0c;未經允許請勿復制、轉載&#xff01;中望3D 2026全新的槽功能&#xff0c;包括&#xff1a;&#xff08;1&#xff09;可快速生成多種槽形&#xff1b;&#xff08;2&#xff09;快速生成一個或多個槽&#xff1b;&#xff08;3&#xff09;支持…

2025毫米波雷達技術白皮書:智能汽車與物聯網的感知核心

隨著人工智能、物聯網&#xff08;IoT&#xff09;和智能汽車產業的迅猛發展&#xff0c;毫米波雷達技術正成為感知領域的核心驅動力。毫米波雷達憑借其高精度、全天候和強抗干擾能力&#xff0c;廣泛應用于智能汽車的自動駕駛、物聯網的環境感知以及工業自動化。2025年&#x…

用 React-Three-Fiber 實現雪花下落與堆積效果:從零開始的 3D 雪景模擬

在 Web3D 開發中&#xff0c;自然現象模擬一直是極具吸引力的主題。本文將基于 React-Three-Fiber&#xff08;R3F&#xff09;框架&#xff0c;詳解如何實現一個包含雪花下落、地面堆積的完整雪景效果。我們會從基礎粒子系統入手&#xff0c;逐步完善物理交互邏輯&#xff0c;…

從抓包GitHub Copilot認證請求,認識OAuth 2.0技術

引言 在現代開發工具中&#xff0c;GitHub Copilot 以智能、嵌入式的人工智能代碼補全能力著稱。作為一項涉及用戶敏感數據和付費授權的服務&#xff0c;其認證授權流程尤為值得技術研究。本文基于實際抓包 VS Code 中的 Copilot 登錄認證請求&#xff0c;系統梳理其 OAuth 2.…

Linux操作系統之線程:分頁式存儲管理

目錄 前言&#xff1a; 一、分頁式存儲管理 二、二級頁表的地址轉化 三、缺頁中斷 總結 前言&#xff1a; 我們上篇文章簡單介紹了線程的一些知識點&#xff0c;但是還有很多坑沒有給大家填上&#xff0c;包括頁表部分我們還沒為大家說明。 本篇文章我將會繼續為大家講解…

xss1-8

Level-1<script>alert()</script>基礎反射型 無任何過濾Level-2"> <script>alert()</script> <"閉合屬性&#xff1a;">用來閉合當前標簽的value屬性注入新標簽&#xff1a;閉合屬性后&#xff0c;插入獨立的<script>…

51c嵌入式~單片機~合集1

自己的原文哦~ https://blog.51cto.com/whaosoft/11897656 一、STM32的啟動模式配置與應用 三種BOOT模式 所謂啟動&#xff0c;一般來說就是指我們下好程序后&#xff0c;重啟芯片時&#xff0c;SYSCLK的第4個上升沿&#xff0c;BOOT引腳的值將被鎖存。用戶可以通過設…

Typecho分類導航欄開發指南:從基礎到高級實現

文章目錄 Typecho分類導航欄深度解析:父分類與子分類的完美呈現 引言 一、Typecho分類系統基礎 1.1 Typecho分類結構 1.2 獲取分類數據的基本方法 二、基礎分類導航輸出 2.1 簡單的平鋪式導航 2.2 帶計數器的分類導航 三、層級分類導航實現 3.1 遞歸輸出父子分類 3.2 使用Type…

C++異步編程工具 async promise-future packaged_task等

深入探討 C11 中引入的四個核心異步編程工具&#xff1a;std::async, std::future, std::promise, 和 std::packaged_task。它們共同構成了 C 現代并發編程的基礎。 為了更好地理解&#xff0c;我們可以使用一個餐廳點餐的類比&#xff1a; std::future (取餐憑證)&#xff1…

Linux-網絡管理

網絡管理1. 網絡基礎1.1 TCP/IP 協議棧&#xff08;四層模型&#xff09;1.2 網絡設備配置與基礎概念1.3 網絡接口命名規則1.4 網絡配置文件位置2. 常用網絡配置命令2.1 查看網絡接口信息2.2 配置 IP 地址2.3 啟用/禁用網卡2.4 修改網卡 MAC 地址2.5 配置網卡的 MTU&#xff08…

Linux鎖的概念及線程同步

目錄 1.常見鎖概念 死鎖 死鎖四個必要條件 避免死鎖 避免死鎖算法 2. Linux線程同步 條件變量 同步概念與競態條件 條件變量函數 初始化 銷毀 等待條件滿足 喚醒等待 簡單案例&#xff1a; 條件變量使用規范 1.常見鎖概念 死鎖 死鎖是指在一組進程中的各個進程均占有不會釋放的…

docker更換國內加速器-更換華為加速器2025-717親測可用docker 拉取鏡像出錯

[rootlocalhost ~]# docker pull nginx Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)報錯原因就是…

Unity VR多人手術模擬恢復2:客戶端移動同步問題分析與解決方案

Unity VR多人手術模擬恢復2&#xff1a;客戶端移動同步問題分析與解決方案 &#x1f3af; 問題背景 在開發基于Unity Mirror網絡架構的VR多人手術模擬系統時&#xff0c;我們遇到了一個復雜的客戶端移動同步問題&#xff1a; 主要操作者&#xff08;第一個客戶端&#xff09;&a…

uni-app開發的頁面跳轉全局加載中

uni-app開發的頁面跳轉全局加載中首先需要下載插件創建加載中頁面組件app.vue頁面中監聽跳轉首先需要下載插件 https://ext.dcloud.net.cn/plugin?id20613 創建加載中頁面組件 <!-- 全局自定義加載中 --> <template><view v-if"visible" class&qu…

XXE漏洞4-XXE無回顯文件讀取-PentesterLab靶場搭建

一.PentesterLab靶場搭建(實驗環境搭建)介紹&#xff1a;PentesterLab 是一個全面的漏洞演示平臺&#xff0c;但是它是收費的&#xff0c;我們這里只使用它的 xxe 演示案例。安裝 PentesterLab 虛擬機:下載好鏡像&#xff1a; 1.打開VMware新建虛擬機&#xff0c;選擇典型就行。…

【機器學習】圖片分類中增強常用方式詳解以及效果展示

圖片增強常用方式詳解 引言 圖片數據的質量和多樣性對模型的訓練效果起著至關重要的作用。然而&#xff0c;實際獲取的圖片數據往往存在數量不足、分布不均衡等問題。圖片增強技術應運而生&#xff0c;它通過對原始圖片進行一系列變換&#xff0c;生成更多具有多樣性的圖片&…

【URL 轉換為PDF】HTML轉換為PDF

1、方法1 pdfkit 安裝依賴 # 安裝 wkhtmltopdf&#xff08;系統級&#xff09; # Ubuntu/Debian sudo apt install wkhtmltopdf# macOS brew install wkhtmltopdf# Windows 下載安裝&#xff1a;https://wkhtmltopdf.org/downloads.html# 安裝 Python 庫 pip install pdfkitimp…

單鏈表的定義、插入和刪除

一、定義一個單鏈表 struct LNode{ //定義單鏈表節點類型ElemType data; //存放節點數據元素struct LNode *next; //指針指向下一個結點 }; //增加一個新節點&#xff1a;在內存中申請一個結點所需空間&#xff0c;并用指針p指向這個結點 struct LNode * p (struc…