图像元数据 与 Exiv2 的简单使用

963 阅读4分钟

资料援引及你可能会需要的工具

简介

Exiv2是一个跨平台的C++库和用于管理图像元数据的命令行实用程序。它提供快速简便的读写访问Exif、IPTC和XMP元数据以及嵌入在各种格式的数字图像中的ICC配置文件。

元数据

什么是元数据

百度百科:baike.baidu.com/item/元数据/19…

元数据(Matedata),又称中介数据、中继数据,是描述数据的数据(data about data),主要是描述数据属性(property)的信息。用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。

元数据的应用 —— 都柏林核心元数据

都柏林核心元素集(Dublin Core Metadata Initiative,DCMI)就是元数据的一种应用,也是下文中会提到的一种概念。

百度百科:baike.baidu.com/item/都柏林核心元…

图像元数据有哪些

而上文提到了EXIF、 IPTC和XMP三种图像元数据:

  • EXIF:通常被数码相机在拍摄照片时自动添加,比如相机型号、镜头、曝光、图片尺寸等信息
  • IPTC:比如图片标题、关键字、说明、作者、版权等信息。主要是由人工在后期通过软件写入的数据。
  • XMP:是一种元数据存储和管理的标准,可以将Exif,IPTC或其他的数据都按XMP统一的格式存放在图像文件中。

元数据的嵌入方式因图像格式而异,不同格式的图像文件(如JPGTIFDNS等)有不同的嵌入方式。

EXIF的嵌入方式详见这篇博客

Exiv2 支持的元数据

而 Exiv2 支持的元数据详见Metadata reference tables

XMP

namespace

首先,需要明确一个概念——namespace,在Metadata reference tables展示的:dcxmpGPano等都是命名空间(namespace)。

命名空间有什么用呢?Exiv2 的 XMP 代码示例 xmpsample.cpp 中是这样介绍的:

Any properties can be set provided the namespace is known. Values of any type can be assigned to an Xmpdatum, if they have an output operator. The default XMP value type for unknown properties is a simple text value.

最重要的就是第一句话:只要命名空间已知,就可以设置任何属性。 因此我们可以通过命名空间自定义属性,并将数据写入属性:

// The XMP property container
Exiv2::XmpData xmpData;

xmpData["Xmp.dc.one"]     = -1;
xmpData["Xmp.dc.two"]     = 3.1415;
xmpData["Xmp.dc.three"]   = Exiv2::Rational(5, 7);
xmpData["Xmp.dc.four"]    = uint16_t(255);
xmpData["Xmp.dc.five"]    = int32_t(256);
xmpData["Xmp.dc.six"]     = false;

// 此外,Exiv2::Value 还有一个专用赋值运算符
Exiv2::XmpTextValue val("Seven");
xmpData["Xmp.dc.seven"]   = val;
xmpData["Xmp.dc.eight"]   = true;

上面代码中的dc都柏林核心元数据命名空间,它本身就设定好了一些属性,我们可以直接使用这些属性,对它们赋值:

xmpData["Xmp.dc.source"]  = "xmpsample.cpp";    // a simple text value
xmpData["Xmp.dc.subject"] = "Palmtree";         // an array item
xmpData["Xmp.dc.subject"] = "Rubbertree";       // add a 2nd array item
// a language alternative with two entries and without default
xmpData["Xmp.dc.title"]   = "lang=de-DE Sonnenuntergang am Strand";
xmpData["Xmp.dc.title"]   = "lang=en-US Sunset on the beach";  

Register namespace

注册命名空间有什么用呢?Exiv2 中这样介绍:

Register a namespace which Exiv2 doesn't know yet. This is only needed when properties are added manually. If the XMP metadata is read from an image, namespaces are decoded and registered at the same time.

那么就可以注册一个namespace,用来存放业务需要但通用标准不支持的属性,比如焦点位置信息:

// Register namespace
// 如果 prefix( 第二个参数)是已知的或之前注册过的,则相应的命名空间URI将被覆盖。
Exiv2::XmpProperties::registerNs("myNs/", "myNs");

// Add a property in the new custom namespace.
xmpData["Xmp.myNs.FocusLocation"] = Exiv2::Rational(focus.x_coordinate, focus.y_coordinate);

注意事项

AutoPtr' is deprecated

Stack Overflow:'AutoPtr' is deprecated

GitHub:Move from auto_ptr to unique_ptr

严格按照文档中的类型来读写元数据

在开发过程中遇到这么个BUG,将读出来的元数据原封不动写入图片,iOS端会造成图片左旋90度:

image.png

文档中明确说明orientation(方向)、resolutionUnit(分辨率单位)字段是用unsigned short类型存储的,XResolution(X分辨率)、YResolution(Y分辨率)字段是用Rational类型(等价于c++中的pair类型)存储的,而在实际开发中,使用了int对它们进行统一存储,而非遵循文档,这就导致了BUG的出现。