# -*- coding: utf-8 -*-
"""
EXIF Data Manipulation
======================
Exif data manipulation routines based on *exiftool*:
- :func:`colour_hdri.parse_exif_data`
- :func:`colour_hdri.read_exif_tags`
- :func:`colour_hdri.copy_exif_tags`
- :func:`colour_hdri.update_exif_tags`
- :func:`colour_hdri.delete_exif_tags`
- :func:`colour_hdri.read_exif_tag`
- :func:`colour_hdri.write_exif_tag`
"""
from __future__ import division, unicode_literals
import logging
import numpy as np
import re
import subprocess # nosec
from collections import namedtuple
from fractions import Fraction
from six import text_type
from colour.utilities.documentation import DocstringText
from colour_hdri.utilities import vivification
__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2015-2019 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'
__all__ = [
'EXIF_EXECUTABLE', 'ExifTag', 'parse_exif_string', 'parse_exif_numeric',
'parse_exif_fraction', 'parse_exif_array', 'parse_exif_data',
'read_exif_tags', 'copy_exif_tags', 'update_exif_tags', 'delete_exif_tags',
'read_exif_tag', 'write_exif_tag'
]
LOGGER = logging.getLogger(__name__)
EXIF_EXECUTABLE = DocstringText('exiftool')
EXIF_EXECUTABLE.__doc__ = """
Command line exif manipulation application, usually Phil Harvey's *ExifTool*.
EXIF_EXECUTABLE : unicode
"""
[docs]class ExifTag(namedtuple('ExifTag', ('group', 'name', 'value', 'identifier'))):
"""
Hunt colour appearance model induction factors.
Parameters
----------
group : unicode, optional
Exif tag group name.
name : unicode, optional
Exif tag name.
value : object, optional
Exif tag value.
identifier : numeric, optional
Exif tag identifier.
"""
def __new__(cls, group=None, name=None, value=None, identifier=None):
"""
Returns a new instance of the :class:`colour_hdri.ExifTag` class.
"""
return super(ExifTag, cls).__new__(cls, group, name, value, identifier)
[docs]def parse_exif_string(exif_tag):
"""
Parses given exif tag assuming it is a string and return its value.
Parameters
----------
exif_tag : ExifTag
Exif tag to parse.
Returns
-------
unicode
Parsed exif tag value.
"""
return text_type(exif_tag.value)
[docs]def parse_exif_numeric(exif_tag, dtype=np.float_):
"""
Parses given exif tag assuming it is a numeric type and return its value.
Parameters
----------
exif_tag : ExifTag
Exif tag to parse.
dtype : object, optional
Return value data type.
Returns
-------
numeric
Parsed exif tag value.
"""
return dtype(exif_tag.value)
[docs]def parse_exif_fraction(exif_tag, dtype=np.float_):
"""
Parses given exif tag assuming it is a fraction and return its value.
Parameters
----------
exif_tag : ExifTag
Exif tag to parse.
dtype : object, optional
Return value data type.
Returns
-------
numeric
Parsed exif tag value.
"""
return dtype(Fraction(exif_tag.value))
[docs]def parse_exif_array(exif_tag, dtype=np.float_, shape=None):
"""
Parses given exif tag assuming it is an array and return its value.
Parameters
----------
exif_tag : ExifTag
Exif tag to parse.
dtype : object, optional
Return value data type.
shape : array_like, optional
Shape of
Returns
-------
ndarray
Parsed exif tag value.
"""
return np.array(exif_tag.value.split()).astype(dtype).reshape(shape)
[docs]def parse_exif_data(data):
"""
Parses given exif data output from *exiftool*.
Parameters
----------
data : unicode
Exif data.
Returns
-------
list
Parsed exif data.
"""
search = re.search(
r'\[(?P<group>\w+)\]\s*(?P<id>(\d+|-))?(?P<tag>.*?):(?P<value>.*$)',
data)
return [
group.strip() if group is not None else group
for group in (search.group('group'), search.group('id'),
search.group('tag'), search.group('value'))
]
[docs]def read_exif_tag(image, tag):
"""
Returns given image exif tag value.
Parameters
----------
image : unicode
Image file.
tag : unicode
Tag.
Returns
-------
unicode
Tag value.
"""
value = text_type(
subprocess.check_output( # nosec
[EXIF_EXECUTABLE, '-{0}'.format(tag), image]),
'utf-8',
'ignore').split(':').pop().strip()
LOGGER.info("Reading '{0}' image '{1}' exif tag value: '{2}'".format(
image, tag, value))
return value
[docs]def write_exif_tag(image, tag, value):
"""
Sets given image exif tag value.
Parameters
----------
image : unicode
Image file.
tag : unicode
Tag.
value : unicode
Value.
Returns
-------
bool
Definition success.
"""
LOGGER.info("Writing '{0}' image '{1}' exif tag with '{2}' value.".format(
image, tag, value))
arguments = [EXIF_EXECUTABLE, '-overwrite_original']
arguments += ['-{0}={1}'.format(tag, value), image]
subprocess.check_output(arguments) # nosec
return True