当前位置: 首页 > ops >正文

[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.xdnf.cn/news/15657.html

相关文章:

  • WEB安全架构
  • 【Python】LEGB作用域 + re模块 + 正则表达式
  • 【PTA数据结构 | C语言版】左堆的合并操作
  • 加速度计和气压计、激光互补滤波融合算法
  • IDEA插件离线安装
  • 反序列化漏洞3-反序列化漏洞讲解演示
  • 5W8-3D牢游戏超级大集合[2012年6月] 地址 + 解压密码
  • 智慧后厨检测算法构建智能厨房防护网
  • 瀚高数据库开启Oracle兼容模块
  • 私有服务器AI智能体搭建配置选择记录
  • 如何更改 SQLserver 数据库存储的位置 想从C盘换到D盘
  • ​​Docker 速通教程
  • 机器人行业10年巨变从协作机器人到具身智能的萌芽、突破和成长——从 Automatic慕尼黑10 年看协作机器人到具身智能的发展
  • 加了pm2
  • (数据结构)线性表(上):SeqList 顺序表
  • vue自定义指令bug
  • Skia 的核心类---深入画布SkCanvas
  • Jfinal+SQLite处理 sqlite数据库执行FIND_IN_SET报错
  • Spring AI:程序调用 AI 大模型
  • Python编程进阶知识之第二课学习网络爬虫(selenium)
  • Java HashMap key为Integer时,遍历是有序还是无序?
  • 信息学奥赛一本通 1575:【例 1】二叉苹果树 | 洛谷 P2015 二叉苹果树
  • 基于LiteNetLib的Server/Client Demo
  • 深入理解 Redis 集群化看门狗机制:原理、实践与风险
  • 当OT遇见IT:Apache IoTDB如何用“时序空间一体化“技术破解工业物联网数据孤岛困局?
  • iOS 文件深度调试实战 查看用户文件 App 沙盒 系统文件与日志全指南
  • iOS WebView 调试实战 全流程排查接口异常 请求丢失与跨域问题
  • 深入理解进程地址空间:虚拟内存与进程独立性
  • 首个直播流扩散(LSD)AI模型:MirageLSD,它可以实时把任意视频流转换成你的自定义服装风格——虚拟换装新体验
  • LVS(Linux Virtual Server)详细笔记(实战篇)