auto-orientation, although no mirroring support yet
authorTero Marttila <terom@fixme.fi>
Mon, 15 Jun 2009 00:23:55 +0300
changeset 122 292aaba6d6ec
parent 121 3bed35e79f41
child 123 31c4a328ef96
auto-orientation, although no mirroring support yet
degal/exif.py
degal/image.py
degal/thumbnail.py
--- a/degal/exif.py	Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/exif.py	Mon Jun 15 00:23:55 2009 +0300
@@ -6,6 +6,15 @@
 
 import utils
 
+MIRROR_NONE         = 0
+MIRROR_HORIZONTAL   = 1
+MIRROR_VERTICAL     = 2
+
+ROTATE_NONE         = 0
+ROTATE_90           = 90
+ROTATE_180          = 180
+ROTATE_270          = 270
+
 class ExifHandler (object) :
     """
         Our generic interface for Exif data
@@ -18,12 +27,19 @@
 
         self.file = file
     
+    def image_tag_value (self, tag, default=None) :
+        """
+            Load and return the raw binary value for the given main image tag.
+        """
+
+        return default
+
     def image_tag (self, tag, default=None) :
         """
             Load and return the human-readable unicode-safe value for the given main image tag.
         """
 
-        abstract
+        return default
 
     def image_tags (self, tags) :
         """
@@ -41,6 +57,32 @@
             # filter out default not-found
             if value is not None :
                 yield tag, value
+    
+
+    def get_orientation (self) :
+        """
+            Get the orientation of the image in terms of mirroring and rotation in integer degrees clockwise.
+            
+            Returns a (MIRROR_*, ROTATE_*) tuple, or None if the Orientation tag could not be found.
+
+            Returns None if it could not be found.
+        """
+        
+        # load value
+        orientation = self.image_tag_value('Orientation')
+
+        # map to tuple or None
+        # XXX: these are from EXIFpy, verify
+        return {
+            1:  (MIRROR_NONE,       ROTATE_NONE ),  # Horizontal (normal)
+            2:  (MIRROR_HORIZONTAL, ROTATE_NONE ),  # Mirrored horizontal
+            3:  (MIRROR_NONE,       ROTATE_180  ),  # Rotated 180
+            4:  (MIRROR_VERTICAL,   ROTATE_NONE ),  # Mirrored vertical
+            5:  (MIRROR_HORIZONTAL, ROTATE_270  ),  # Mirrored horizontal then rotated 90 CCW
+            6:  (MIRROR_NONE,       ROTATE_90   ),  # Rotated 90 CW
+            7:  (MIRROR_HORIZONTAL, ROTATE_90   ),  # Mirrored horizontal then rotated 90 CW
+            8:  (MIRROR_NONE,       ROTATE_270  ),  # Rotated 90 CCW
+        }.get(orientation, None)
 
 try :
     import pyexiv2
@@ -63,6 +105,20 @@
             # load
             self.image.readMetadata()
         
+        def image_tag_value (self, tag, default=None) :
+            # try with likely prefixes
+            for prefix in ('Exif.Photo', 'Exif.Image') :
+                try :
+                    return self.image["%s.%s" % (prefix, tag)]
+                
+                except (IndexError, KeyError) :
+                    # nope...
+                    continue
+
+            else :
+                # not ofund
+                return default
+
         def image_tag (self, tag, default=None) :
             # try with likely prefixes
             for prefix in ('Exif.Photo', 'Exif.Image') :
--- a/degal/image.py	Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/image.py	Mon Jun 15 00:23:55 2009 +0300
@@ -63,6 +63,20 @@
         return exif.load(self.config, self)
     
     @lazy_load
+    def orientation (self) :
+        """
+            Loads the orientation of this image as a (mirroring, rotation) tuple, or None, if the information is not available.
+        """
+
+        if self.config.with_exif and self.exif :
+            # try and find it
+            return self.exif.get_orientation()
+
+        else :
+            # no exif data
+            return None
+
+    @lazy_load
     def metadata (self) :
         """
             Load and return the metadata for the image as a dictionary
--- a/degal/thumbnail.py	Sun Jun 14 23:57:50 2009 +0300
+++ b/degal/thumbnail.py	Mon Jun 15 00:23:55 2009 +0300
@@ -63,15 +63,49 @@
             height = thumb_height
         
         return width, height 
+    
+    def resize (self, img) :
+        """
+            Resize the give image as needed.
+        """
+
+        return img.resize(self.size, resample=PIL.Image.ANTIALIAS)
+    
+    def auto_orient (self, img, orient_info) :
+        """
+            Automatically orient the image using the given orientation info.
+        """
+        
+        # unpack
+        mirroring, rotation = orient_info
+
+        if mirroring :
+            # XXX
+            pass
+
+        if rotation :
+            # since these are in steps of 90 degrees, it should keep the right size
+            # but gah, PIL wants these as counter-clockwise!
+            img = img.rotate(360 - rotation)
+        
+        # ok
+        return img
 
     def update (self) :
         """
             Render new output thumbnail.
         """
 
+        # start with origional image
+        img = self.image.img
+
         # create resized copy of main image, using our size
-        thumb = self.image.img.resize(self.size, resample=PIL.Image.ANTIALIAS)
+        img = self.resize(img)
+
+        # got orientation info?
+        if self.image.orientation :
+            img = self.auto_orient(img, self.image.orientation)
 
         # write it out
-        thumb.save(self.path)
+        img.save(self.path)