97 个 OpenCV 实例(十七):图片读写,FileStorage 与 JPEG 采样

上一篇把粘连的水果分开了。这一篇离开 imgproc,进入第三阶段「图像读写与视频 I/O」------先把「读进来、写出去」这条链路走通。官方 samples 里三个例子对号入座:imagelist_creator 把一堆图片路径存进 yaml,imagelist_reader 再把它读出来逐张显示,imgcodecs_jpeg 则在内存里把同一张图按不同采样因子压缩、看失真。这一课还挖到一个彩蛋:OpenCV 4.x 的 lena.jpg 已经不是经典 Lena 了。

一、效果先行

先看 JPEG 采样因子。上排是 5 张一模一样的原图,下排是同一张图按 5 种色度采样因子压缩后的结果------从 444 到 411,圆环边缘的彩色晕染越来越明显:

再看图片列表。imagelist_creator 把 4 张图写进一个 yaml,imagelist_reader 读出来逐张灰度显示,按一下键翻一张:

两张图记住两件事:JPEG 靠「牺牲色度」换体积;FileStorage 靠「序列化」统一管理一堆图片路径。

二、FileStorage:把图片列表写进文件

批量处理几百上千张图时,最笨也最稳的办法是先把它们的路径存成一个清单,程序按清单逐张读。OpenCV 自带一套序列化框架 FileStorage,能把数据写成 YAML 或 XML。imagelist_creator 只做一件事------把命令行传进来的图片路径存进文件:

cpp 复制代码
FileStorage fs(outputname, FileStorage::WRITE);   // 打开文件,写模式
fs << "images" << "[";                             // 开一个叫 images 的序列
for (int i = 2; i < ac; i++)
    fs << string(av[i]);                           // 逐条压入路径
fs << "]";                                         // 闭合序列

有个细节很贴心:它先 imread(outputname) 试探一下输出名,如果这个文件本身是一张图片(!m.empty()),立刻报错退出。为什么?防止用户手滑,把输出清单写成一张已有图片的名字,把原图覆盖掉。这个防覆盖检查,正是工程师该有的谨慎。

imagelist_reader 负责把清单读回来。核心是拿到顶层节点、确认它是序列,再逐条取出:

cpp 复制代码
FileStorage fs(filename, FileStorage::READ);
FileNode n = fs.getFirstTopLevelNode();            // 顶层节点
if (n.type() != FileNode::SEQ) return false;       // 必须是序列
for (FileNodeIterator it = n.begin(); it != n.end(); ++it)
    l.push_back((string)*it);                      // 逐条取出路径

一个写、一个读,合起来就是完整的「图片清单」工作流。工业上做批量质检、数据集管理,第一步永远是这么个清单文件。

三、图片编解码四兄弟

读图写图有四个 API,两两对应,很多人分不清 imwrite 和 imencode 的区别:

API 方向 介质 典型场景
imread 文件 → Mat 磁盘 加载图片
imwrite Mat → 文件 磁盘 保存图片
imencode Mat → 内存 buffer 内存 压缩成字节流(发网络、写数据库)
imdecode 内存 buffer → Mat 内存 从字节流解码(收网络、读数据库)

关键区别:imencode 和 imdecode 全程在内存里完成编解码,不落盘。相机抓到一帧、要压缩后通过网络发出去,走的就是 imencode;接收方拿到字节流,用 imdecode 还原成 Mat。imgcodecs_jpeg 这个例子,正是用这俩 API 演示内存编解码:

cpp 复制代码
vector<int> param;
param.push_back(IMWRITE_JPEG_SAMPLING_FACTOR);     // 指定参数名
param.push_back(config[i].sampling_factor);        // 411/420/422/440/444
vector<uint8_t> jpeg;
imencode(".jpg", img, jpeg, param);                // 压缩到内存
Mat lossy_img = imdecode(Mat(jpeg), -1);           // 从内存解码还原

四、JPEG 采样因子:色度抽样

JPEG 压缩的核心洞察是:人眼对亮度敏感、对色度不敏感。所以它把图像拆成亮度分量和色度分量,亮度全采样,色度可以降采样------这就是「色度抽样」。采样因子控制色度被抽掉多少:

采样因子 比例 含义
444 4:4:4 色度不降采样,质量最高
422 4:2:2 水平色度减半,折中
420 4:2:0 水平垂直都减半,相机/网络默认
440 4:4:0 垂直色度减半,较少用
411 4:1:1 色度压得最狠,体积最小

官方测试图故意用一张粉紫色同心圆------高频的彩色边缘,最能暴露色度抽样的失真。从 444 到 411,圆环边缘的彩色晕染越来越重。而对灰度图,这参数几乎没影响,因为灰度图本来就没有色度。

五、实测

三个实例跑下来,两个是纯命令行、一个要弹窗。imagelist_creator 一行命令生成清单,4 张图的绝对路径整整齐齐写进 yaml;imgcodecs_jpeg 无参数直接吐出 800×320 的对比图。只有 imagelist_reader 需要 GUI 逐张显示,我用 Xvfb 虚拟屏加 xdotool 模拟按键翻页,4 张灰度图全部截了下来。

这里挖到一个彩蛋:OpenCV 4.x 的 lena.jpg 已经不是经典 Lena 了。那张 1972 年《花花公子》插图的扫描图,因为版权争议被 OpenCV 换掉,现在 reader 第一张显示的是个戴针织帽的人像。测试图也讲究版权------这大概是工业工程里最容易被忽略的一课。

六、踩坑记录

现象 正确姿势
读图失败不报错 imread 读不存在的文件返回空 Mat,不抛异常 用 empty() 判断,别依赖 try-catch
清单结构不对 reader 读不出、返回失败 顶层节点必须是序列(SEQ),用 << "[" ... "]" 写出来
相对路径失效 换目录跑 reader 找不到图 清单里存绝对路径
采样因子无效 灰度图看不出差别 色度抽样只对彩色图有意义

七、AI 与 LLM Wiki:这一课沉淀了什么

LLM Wiki 从 26 页涨到 27 页,这一课新增「图片读写与JPEG采样」概念页,沉淀了 FileStorage 序列化的写读范式、imread/imwrite/imencode/imdecode 四兄弟的区分,以及 JPEG 色度抽样的 5 档采样因子对照。进度表上 imagelist_creator、imagelist_reader、imgcodecs_jpeg 三个勾选完成,从 32 个变成 35 个,进度 36%。

这一课最值得进知识库的,是那个防覆盖设计------imread 先试探输出名、是图片就拒绝覆盖。它不是算法,却比算法更值钱,因为它体现的是「写代码时多想一步用户会怎么误操作」。把这类工程细节攒进知识库,比单纯记 API 有用得多。

写在最后

97 个实例,今天完成第 35 个,进度 36%,第三阶段「图像读写与视频 I/O」开了个头。这一站把「读进来、写出去」的链路铺平了:FileStorage 管清单,四兄弟管编解码,采样因子管体积。下一篇继续这条链路,上摄像头------从打开摄像头、调分辨率帧率,到写视频文件,把视频采集摸一遍。

本文示例代码均出自 OpenCV 官方 samples,遵循 Apache 2.0 协议。

相关推荐
回眸&啤酒鸭1 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智1 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
AI的探索之旅1 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein1 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
LaughingZhu1 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
wukangjupingbb1 天前
智能网联汽车安全能力框架
人工智能
龙亘川1 天前
明月照湾区,智启新赛道:从顶流文旅IP盛会看智慧文旅升级路径
人工智能·智慧城市·开源软件·数据可视化
飞猫的边缘AI1 天前
边缘AI应用:家用AI摄像头怎么做数据训练?
人工智能·边缘计算·ai算法·边缘ai