An image file (e.g. JPEG, PNG) contains metadata, i.e. information about the image, e.g. which camera model was used or when the picture has been taken. This information is usually stored at the beginning of the file. Three main formats, or bags of metadata, can coexist in a file and the information they contain partly overlap:
The IPTC IIM format (often just called IPTC format) and the Exif format represent sets of key-value pairs, whereas the newer XMP format is an XML representation of a more complex RDF graph. The XMP Specification Part 3 specifies how the XMP metadata are to be serialized and stored in each image file format (e.g. JPEG, PNG).
The IPTC council has defined a standard for storing Image Regions in XMP. Image Regions are useful for describing specific areas of the image (e.g. objects, people) or for indicating how the image should be cropped or rotated to best fit a given container. The Frameright app can be used to define such Image Regions and insert them in the metadata of a picture.
This tutorial shows how to read the Image Region metadata of an image from the command line.
Installing the necessary tools
Let’s install the following tools:
- ExifTool, for extracting any metadata from any file format;
- XMLStarlet, for formatting and querying any XML document.
On Ubuntu 22.04 for example these tools can be installed with:
sudo apt update
sudo apt install libimage-exiftool-perl xmlstarlet
These tools are also available on macOS and Windows. The rest of this tutorial assumes you are running a bash terminal.
Accessing the XMP metadata from an image
The IPTC Photo Metadata Standard 2021.1 Reference Image file contains several recently-defined XMP metadata items including Image Regions. Download it with:
mkdir -p /tmp/frameright
cd /tmp/frameright
curl -O \
https://iptc.org/std/photometadata/examples/IPTC-PhotometadataRef-Std2021.1.jpg
ExifTool can be used to extract and dump all the XMP metadata by running:
exiftool -XMP -b IPTC-PhotometadataRef-Std2021.1.jpg
Simplified output:
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 12.41'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<rdf:Description rdf:about=''
xmlns:Iptc4xmpCore='http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/'>
<rdf:Description rdf:about=''
xmlns:Iptc4xmpExt='http://iptc.org/std/Iptc4xmpExt/2008-02-29/'
xmlns:exif='http://ns.adobe.com/exif/1.0/'
xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
<Iptc4xmpExt:ImageRegion>
<!-- ... -->
This large XML output can then be piped into XMLStarlet in order to specifically extract the ImageRegion element and its descendants by using an XPath query:
exiftool -XMP -b IPTC-PhotometadataRef-Std2021.1.jpg | \
xmlstarlet sel -t -c "//*[local-name() = 'ImageRegion']"
Simplified output:
<Iptc4xmpExt:ImageRegion xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:x="adobe:ns:meta/">
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:Name>
<rdf:Alt>
<rdf:li xml:lang="x-default">Listener 1</rdf:li>
</rdf:Alt>
</Iptc4xmpExt:Name>
<Iptc4xmpExt:OrganisationInImageName>
<rdf:Bag>
<rdf:li>Organisation name no 1 in region persltr2 (ref2021.1)</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:OrganisationInImageName>
<Iptc4xmpExt:PersonInImage>
<rdf:Bag>
<rdf:li>Person name no 1 in region persltr2 (ref2021.1)</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:PersonInImage>
<Iptc4xmpExt:RegionBoundary rdf:parseType="Resource">
<Iptc4xmpExt:rbH>0.385</Iptc4xmpExt:rbH>
<Iptc4xmpExt:rbShape>rectangle</Iptc4xmpExt:rbShape>
<Iptc4xmpExt:rbUnit>relative</Iptc4xmpExt:rbUnit>
<Iptc4xmpExt:rbW>0.127</Iptc4xmpExt:rbW>
<Iptc4xmpExt:rbX>0.31</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.18</Iptc4xmpExt:rbY>
</Iptc4xmpExt:RegionBoundary>
<Iptc4xmpExt:rRole>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:Name>
<rdf:Alt>
<rdf:li xml:lang="x-default">Region Boundary Content Role Name (ref2021.1)</rdf:li>
</rdf:Alt>
</Iptc4xmpExt:Name>
<xmp:Identifier>
<rdf:Bag>
<rdf:li>https://example.org/rrole/role2021.1a</rdf:li>
<rdf:li>https://example.org/rrole/role2021.1b</rdf:li>
</rdf:Bag>
</xmp:Identifier>
</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:rRole>
</rdf:li>
<!-- ... -->
We can also specifically query the RegionBoundary and rRole descendants. This will clearly show us the geometrical regions defined in our image. The resulting indentation gets a bit broken, so we pipe the output again in XMLStarlet in order to properly format it:
exiftool -XMP -b IPTC-PhotometadataRef-Std2021.1.jpg | \
xmlstarlet sel -R -t -c \
"//*[local-name() = 'RegionBoundary'] | //*[local-name() = 'rRole']" | \
xmlstarlet fo
Output:
<?xml version="1.0"?>
<xsl-select xmlns:x="adobe:ns:meta/">
<Iptc4xmpExt:RegionBoundary xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:parseType="Resource">
<Iptc4xmpExt:rbH>0.385</Iptc4xmpExt:rbH>
<Iptc4xmpExt:rbShape>rectangle</Iptc4xmpExt:rbShape>
<Iptc4xmpExt:rbUnit>relative</Iptc4xmpExt:rbUnit>
<Iptc4xmpExt:rbW>0.127</Iptc4xmpExt:rbW>
<Iptc4xmpExt:rbX>0.31</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.18</Iptc4xmpExt:rbY>
</Iptc4xmpExt:RegionBoundary>
<Iptc4xmpExt:rRole xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:Name>
<rdf:Alt>
<rdf:li xml:lang="x-default">Region Boundary Content Role Name (ref2021.1)</rdf:li>
</rdf:Alt>
</Iptc4xmpExt:Name>
<xmp:Identifier>
<rdf:Bag>
<rdf:li>https://example.org/rrole/role2021.1a</rdf:li>
<rdf:li>https://example.org/rrole/role2021.1b</rdf:li>
</rdf:Bag>
</xmp:Identifier>
</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:rRole>
<Iptc4xmpExt:RegionBoundary xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:parseType="Resource">
<Iptc4xmpExt:rbRx>0.068</Iptc4xmpExt:rbRx>
<Iptc4xmpExt:rbShape>circle</Iptc4xmpExt:rbShape>
<Iptc4xmpExt:rbUnit>relative</Iptc4xmpExt:rbUnit>
<Iptc4xmpExt:rbX>0.59</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.426</Iptc4xmpExt:rbY>
</Iptc4xmpExt:RegionBoundary>
<Iptc4xmpExt:rRole xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:Name>
<rdf:Alt>
<rdf:li xml:lang="x-default">Region Boundary Content Role Name (ref2021.1)</rdf:li>
</rdf:Alt>
</Iptc4xmpExt:Name>
<xmp:Identifier>
<rdf:Bag>
<rdf:li>https://example.org/rrole/role2021.1a</rdf:li>
<rdf:li>https://example.org/rrole/role2021.1b</rdf:li>
</rdf:Bag>
</xmp:Identifier>
</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:rRole>
<Iptc4xmpExt:RegionBoundary xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" rdf:parseType="Resource">
<Iptc4xmpExt:rbShape>polygon</Iptc4xmpExt:rbShape>
<Iptc4xmpExt:rbUnit>relative</Iptc4xmpExt:rbUnit>
<Iptc4xmpExt:rbVertices>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:rbX>0.05</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.713</Iptc4xmpExt:rbY>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:rbX>0.148</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.041</Iptc4xmpExt:rbY>
</rdf:li>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:rbX>0.375</Iptc4xmpExt:rbX>
<Iptc4xmpExt:rbY>0.863</Iptc4xmpExt:rbY>
</rdf:li>
</rdf:Seq>
</Iptc4xmpExt:rbVertices>
</Iptc4xmpExt:RegionBoundary>
<Iptc4xmpExt:rRole xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<Iptc4xmpExt:Name>
<rdf:Alt>
<rdf:li xml:lang="x-default">Region Boundary Content Role Name (ref2021.1)</rdf:li>
</rdf:Alt>
</Iptc4xmpExt:Name>
<xmp:Identifier>
<rdf:Bag>
<rdf:li>https://example.org/rrole/role2021.1a</rdf:li>
<rdf:li>https://example.org/rrole/role2021.1b</rdf:li>
</rdf:Bag>
</xmp:Identifier>
</rdf:li>
</rdf:Bag>
</Iptc4xmpExt:rRole>
</xsl-select>
Summary
In this tutorial we have learned:
- what the three existing image metadata formats (IPTC, Exif and XMP) are;
- what the Image Region XMP metadata is;
- How to read this metadata using the command line, with the help of ExifTool and XMLStarlet.