Source code for pyctools.components.photo.reorient

#  Pyctools - a picture processing algorithm development kit.
#  http://github.com/jim-easterbrook/pyctools
#  Copyright (C) 2019-22  Pyctools contributors
#
#  This program is free software: you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see
#  <http://www.gnu.org/licenses/>.

__all__ = ['Reorient']
__docformat__ = 'restructuredtext en'

import numpy

from pyctools.core.config import ConfigEnum
from pyctools.core.base import Transformer


[docs] class Reorient(Transformer): """Rotate and/or reflect an image. This can be used to convert photographs to the normal viewing orientation, rather than relying on the metadata orientation flag. The ``orientation`` parameter sets the current orientation of the image. If it's ``auto`` the value is taken from the image metadata. =============== === ==== Config =============== === ==== ``orientation`` str The current orientation. Possible values: {}. =============== === ==== """ orientations = { 'auto': 0, 'normal': 1, 'rotate -90': 6, 'rotate +90': 8, 'rotate 180': 3, 'reflect left-right': 2, 'reflect top-bottom': 4, 'reflect tr-bl': 5, 'reflect tl-br': 7 } __doc__ = __doc__.format(', '.join( ['``{}``'.format(x) for x in orientations])) def initialise(self): self.config['orientation'] = ConfigEnum(choices=self.orientations) def orient_text(self, int_val): for key, value in self.orientations.items(): if value == int_val: return key return 'unknown' def transform(self, in_frame, out_frame): self.update_config() # get orientation orientation = self.orientations[self.config['orientation']] if not orientation: orientation = 1 for tag, data in ( ('Exif.Image.Orientation', out_frame.metadata.exif_data), ('Xmp.tiff.Orientation', out_frame.metadata.xmp_data)): if tag in data: orientation = int(data[tag]) break # clear metadata orientation flag for tag, data in ( ('Exif.Image.Orientation', out_frame.metadata.exif_data), ('Xmp.tiff.Orientation', out_frame.metadata.xmp_data)): if tag in data: del data[tag] # do transformation orient_bits = orientation - 1 if orient_bits: data = out_frame.as_numpy() if orient_bits & 0b100: # transpose horizontal & vertical data = numpy.swapaxes(data, 0, 1) flip_v, flip_h = False, False if orient_bits & 0b010: # rotate 180 flip_h = not flip_h flip_v = not flip_v if orient_bits & 0b001: # reflect left-right flip_h = not flip_h if flip_v: data = numpy.flipud(data) if flip_h: data = numpy.fliplr(data) out_frame.data = data # add audit audit = out_frame.metadata.get('audit') audit += 'data = Reorient(data)\n' audit += ' from "{}"\n'.format(self.orient_text(orientation)) audit += self.config.audit_string() out_frame.metadata.set('audit', audit) return True