加载中...
libExiv2-读写图像元数据
第1节:libfswatch-文件变动监控
第2节:libiconv-字符集编码转换
第3节:CLI11-命令行参数解析
第4节:nlohmann/json-自然的JSON库
第5节:libb64-理解并玩转base64编码
第6节:libSnappy-快速压缩工具
第7节:spdlog-首选的C++日志库
第8节:libUSB-脱掉USB的外套
第9节:libxlsxwriter-让数据说话
第10节:sqlite-orm-迅速上手.上
第11节:sqlite-orm-迅速上手.下
第12节:libExiv2-读写图像元数据
第13节:webview-让浏览器颤抖的力量
第14节:pystring-躺平的C++字符串工具库
第15节:cpr-自制Deepseek心理陪聊师
第16节:ExprTk-计算的力量
课文封面

一堆照片,如何读取它们光圈、快门数据?
如何为它们打标签,评级?
有 libExiv2 ,轻松搞定图像文件中的三种常用元数据读取。

你花了多少钱?买过多少把智能手机?
你用智能手机拍过的照片,很有可能已经过万!
身为程序员,你就没想过写个程序管理这些照片?

0. 基础知识

libExiv2 可处理图像文件的,以下三种元数据:

  • EXIF : Exchangeable Image File Format,可交换图像文件格式,用于记录拍摄设备、环境参数、版权信息等关键数据。它最初由日本电子工业发展协会(JEIDA)于 1996 年发布,最新版本为 2.2(2002 年),目前仍在广泛应用;

  • IPTC : International Press Telecommunications Council(国际新闻电信理事会) 制定的元数据,广泛用于图像、视频等数据内容的管理与分发;

  • XMP : 可扩展元数据平台 (Extensible Metadata Platform) 是由 Adobe 开发的开放元数据标准,旨在为数字资产(如图像、文档、音视频)提供统一的元数据管理框架。它通过 XML 格式存储结构化信息,支持跨平台、跨应用的元数据交换与协作,是现代数字内容工作流的核心技术之一。

1. 课堂视频

2. 关键知识

2.1 打开图像

使用工厂方法 Exiv2::ImageFactor::open(“图像文件.jpg”),得到的是 std::unique_ptr<Exiv2::Image> 智能指针,代码如下:

// 1. 用 exiv2 来打开一个图像文件 Exiv2::Image::UniquePtr img = Exiv2::ImageFactory::open("bhcpp.jpg");

其中 Exiv2::Image::UniquePtrstd::unique_ptr<Exiv2::Image> 的类型别名。

2.2 读元数据

打开图像后,并不直接读取其元数据,需调用以下方法:

img->readMetadata();

再接着,调用对应方法,即可读取到不同类型的元数据(EXIF、IPTC、XMP):

// 1 读取 EXIF 信息 Exiv2::ExifData const& exifData = img->exifData(); // 2 读取 IPTC 信息 Exiv2::IptcData const& iptcData = img->iptcData(); // 3 读取 XMP 信息 Exiv2::XmpData const& xmpData = img->xmpData();

2.3 写元数据

三种数据均采用 [key] 形式读写具体的数据项,共中 key 可为 xml 数据项的路径,使用 . 分隔。

写元数据有几个关键点:

(一)、作为后期图像加工程序(而非图像的创建者,通常指相机、截屏软件、摄像头等),不去修改其 EXIF 数据,以确保 EXIF 忠实记录图像的生成信息;

(二)、IPTC 此类数据的键有标准约束,如程序使用标准中不存在键值,libExiv2 将抛出异常;

(三)、XMP 数据项需入特别注意所要写入的数据项,是否为“数组”类型,如是,将新数据内容将以追加而非覆盖的形式写入;

(四)、完成数据项编辑后,需调用 writeMetadata() 方法加以保存。

2.4 异常处理

课堂视频未展现 libExiv2 的异常处理,实际项目在读写元数据读写时,应考虑异常处理,如:

int main() { Exiv2::Image::UniquePtr img = Exiv2::ImageFactory::open("bhcpp.jpg"); if (!img) return -1; try { write_ipct_data(img.get()); write_xmp_data(img.get()); read_info(img.get()); } catch(Exiv2::Error const& e) { std::cerr << e.what() << std::endl; } }

3. 配套代码

  • CMakeLists.txt
cmake_minimum_required(VERSION 3.15) set(CMAKE_CXX_STANDARD 17) project(HelloExiv2 CXX) # c:/msys64/ucrt64/lib 请更换为你的 msys64 库路径 find_library(libExiv2 exiv2 PATHS c:/msys64/ucrt64/lib) add_executable(HelloExiv2 main.cpp) # c:/msys64/ucrt64/include 请更换为你的 msys64 头文件路径 target_include_directories(HelloExiv2 PRIVATE c:/msys64/ucrt64/include) target_link_libraries(HelloExiv2 PRIVATE libExiv2)
  • main.cpp
#include <cassert> #include <iostream> #include <exiv2/exiv2.hpp> template<typename TData> void print_data(TData const& t, char const* data_type) { if (t.empty()) { std::cout << "此图像无 " << data_type << " 信息\n"; return; } std::cout << "==== 图像 " << data_type << " 信息 ====\n"; for (auto const& i : t) { std::cout << i.key() << " -> " << i.value() << "\n"; } } void write_ipct_data(Exiv2::Image *img) { // 先读出 img->readMetadata(); auto & iptcData = img->iptcData(); // 库会检查我们所添加的数据的键(key)是否合法,不合法将抛异常 // 版权声明 iptcData["Iptc.Application2.Copyright"] = "© 2025 南郁. 保留所有权利"; // 描述(备注) iptcData["Iptc.Application2.Caption"] = "一本学习C++的好书!"; img->writeMetadata(); std::cout << "IPTC 元数据写入成功!" << std::endl; } void write_xmp_data(Exiv2::Image *img) { // 先读出 img->readMetadata(); auto & xmpData = img->xmpData(); // 删除指定键的 xmp 数据项 auto del_xmp_item = [&xmpData] (char const* key) { if (auto it = xmpData.findKey(Exiv2::XmpKey(key)); it != xmpData.end()) { xmpData.erase(it); } }; // 创造者 del_xmp_item("Xmp.dc.creator"); xmpData["Xmp.dc.creator"] = "南郁"; // 关键词 del_xmp_item("Xmp.dc.subject"); xmpData["Xmp.dc.subject"] = "书"; xmpData["Xmp.dc.subject"] = "C++"; xmpData["Xmp.dc.subject"] = "练武(下)"; xmpData["Xmp.dc.subject"] = "第2学堂"; // XMP 标题 xmpData["Xmp.dc.title"] = "《白话C++ 练武篇(下)》"; // 打分 xmpData["Xmp.xmp.Rating"] = 3; img->writeMetadata(); std::cout << "XMP 元数据写入成功!" << std::endl; } void read_info(Exiv2::Image *img) { assert(img != nullptr); img->readMetadata(); // 1 读取 EXIF 信息 Exiv2::ExifData const& exifData = img->exifData(); print_data(exifData, "EXIF"); // 2 读取 IPTC 信息 Exiv2::IptcData const& iptcData = img->iptcData(); print_data(iptcData, "IPTC"); // 3 读取 XMP 信息 Exiv2::XmpData const& xmpData = img->xmpData(); print_data(xmpData, "XMP"); } int main() { // 1. 用 exiv2 来打开一个图像文件 Exiv2::Image::UniquePtr img = Exiv2::ImageFactory::open("bhcpp.jpg"); if (!img) { std::cerr << "无法打开图片文件" << std::endl; return -1; } std::cout << "图片文件已打开!" << std::endl; write_ipct_data(img.get()); write_xmp_data(img.get()); read_info(img.get()); }