EXIF fixes
authorJoar Wandborg <git@wandborg.com>
Mon, 16 Jan 2012 02:45:58 +0000 (03:45 +0100)
committerJoar Wandborg <git@wandborg.com>
Wed, 25 Jan 2012 22:44:59 +0000 (23:44 +0100)
- Moved exif functions from mediagoblin.media_types.image.processing
  to mediagoblin.tools.exif
- Moved EXIF.py link from mediagoblin.media_types to mediagoblin.tools.extlib
- Refractored and updated EXIF exctraction and presentation

mediagoblin/media_types/image/processing.py
mediagoblin/templates/mediagoblin/user_pages/media.html
mediagoblin/tools/exif.py [new file with mode: 0644]
mediagoblin/tools/extlib/EXIF.py [new symlink]
mediagoblin/tools/extlib/__init__.py [new file with mode: 0644]

index 9eb8fa1669cfccca9ab858ce0fa9fc37ca29df22..f669e1a5b1251e261e2dfa5c6f5a6758bf711c87 100644 (file)
@@ -20,8 +20,8 @@ import os
 from mediagoblin import mg_globals as mgg
 from mediagoblin.processing import BadMediaFail, \
     create_pub_filepath, THUMB_SIZE, MEDIUM_SIZE
-from mediagoblin.media_types.image.EXIF import process_file
-from mediagoblin.tools.translate import pass_to_ugettext as _
+from mediagoblin.tools.exif import exif_fix_image_orientation, \
+    extract_exif, clean_exif, get_gps_data, get_useful
 
 def process_image(entry):
     """
@@ -110,112 +110,15 @@ def process_image(entry):
 
     # Insert exif data into database
     media_data = entry.setdefault('media_data', {})
-    media_data['exif'] = clean_exif(exif_tags)
+    media_data['exif'] = {
+        'clean': clean_exif(exif_tags)}
+    media_data['exif']['useful'] = get_useful(
+        media_data['exif']['clean'])
     media_data['gps'] = gps_data
 
     # clean up workbench
     workbench.destroy_self()
 
-def exif_fix_image_orientation(im, exif_tags):
-    """
-    Translate any EXIF orientation to raw orientation
-
-    Cons:
-    - REDUCES IMAGE QUALITY by recompressig it
-
-    Pros:
-    - Cures my neck pain
-    """
-    # Rotate image
-    if 'Image Orientation' in exif_tags:
-        rotation_map = {
-            3: 180,
-            6: 270,
-            8: 90}
-        orientation = exif_tags['Image Orientation'].values[0]
-        if orientation in rotation_map.keys():
-            im = im.rotate(
-                rotation_map[orientation])
-
-    return im
-
-def extract_exif(filename):
-    """
-    Returns EXIF tags found in file at ``filename``
-    """
-    exif_tags = {}
-
-    try:
-        image = open(filename)
-        exif_tags = process_file(image)
-    except IOError:
-        BadMediaFail(_('Could not read the image file.'))
-
-    return exif_tags
-
-def clean_exif(exif):
-    # Clean the result from anything the database cannot handle
-
-    # Discard any JPEG thumbnail, for database compatibility
-    # and that I cannot see a case when we would use it.
-    # It takes up some space too.
-    disabled_tags = [
-        'Thumbnail JPEGInterchangeFormatLength',
-        'JPEGThumbnail',
-        'Thumbnail JPEGInterchangeFormat']
-
-    clean_exif = {}
-
-    for key, value in exif.items():
-        if not key in disabled_tags:
-            clean_exif[key] = str(value)
-
-    return clean_exif
-    
-
-def get_gps_data(exif):
-    """
-    Processes EXIF data returned by EXIF.py
-    """
-    if not 'Image GPSInfo' in exif:
-        return False
-
-    gps_data = {}
-
-    try:
-        dms_data = {
-            'latitude': exif['GPS GPSLatitude'],
-            'longitude': exif['GPS GPSLongitude']}
-
-        for key, dat in dms_data.items():
-            gps_data[key] = (
-                lambda v:
-                    float(v[0].num) / float(v[0].den) \
-                    + (float(v[1].num) / float(v[1].den) / 60 )\
-                    + (float(v[2].num) / float(v[2].den) / (60 * 60))
-                )(dat.values)
-    except KeyError:
-        pass
-
-    try:
-        gps_data['direction'] = (
-            lambda d:
-                float(d.num) / float(d.den)
-            )(exif['GPS GPSImgDirection'].values[0])
-    except KeyError:
-        pass
-
-    try:
-        gps_data['altitude'] = (
-            lambda a:
-                float(a.num) / float(a.den)
-            )(exif['GPS GPSAltitude'].values[0])
-    except KeyError:
-        pass
-
-    return gps_data
-
-
 if __name__ == '__main__':
     import sys
     import pprint
@@ -224,9 +127,11 @@ if __name__ == '__main__':
 
     result = extract_exif(sys.argv[1])
     gps = get_gps_data(result)
+    clean = clean_exif(result)
+    useful = get_useful(clean)
 
     import pdb
     pdb.set_trace()
 
     print pp.pprint(
-        result)
+        clean)
index 944d7f6ed4d9c23d43ec5af6d22a47155c7d5c81..60fca710d6895ece1a265d3bb172885d864052c0 100644 (file)
                                    media= media._id) %}
         <a class="button_action" href="{{ delete_url }}">{% trans %}Delete{% endtrans %}</a>
       {% endif %}
-      {% if media.media_data.exif %}
+      {% if media.media_data.has_key('exif') 
+            and media.media_data.exif.has_key('useful') %}
        {#-
        TODO:
         - Render GPS data in a human-readable format
-       
+       #}
         <h4>EXIF</h4>
        <table>
-       {% for tag, value in media.media_data.exif.items() %}
+       {% for key, tag in media.media_data.exif.useful.items() %}
          <tr>
-           <td>{{ tag }}</td>
-           <td>{{ value }}</td>
+           <td>{{ key }}</td>
+           <td>{{ tag.printable }}</td>
          </tr>
         {% endfor %}
-       </table>#}
+       </table>
       {% endif %}
     </p>
     {% if comments %}
       {% include "mediagoblin/utils/tags.html" %}
     {% endif %}
 
-    {% if media.media_data.gps %}
+    {% if media.media_data.has_key('gps')
+           and media.media_data.gps %}
       <h4>Map</h4>
       <div>
        {% set gps = media.media_data.gps %}
diff --git a/mediagoblin/tools/exif.py b/mediagoblin/tools/exif.py
new file mode 100644 (file)
index 0000000..445907b
--- /dev/null
@@ -0,0 +1,168 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011 MediaGoblin contributors.  See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from mediagoblin.tools.extlib.EXIF import process_file, Ratio
+from mediagoblin.processing import BadMediaFail
+from mediagoblin.tools.translate import pass_to_ugettext as _
+
+from collections import OrderedDict
+
+# A list of tags that should be stored for faster access
+USEFUL_TAGS = [
+    'Image Make',
+    'Image Model',
+    'EXIF FNumber',
+    'EXIF Flash',
+    'EXIF FocalLength',
+    'EXIF ExposureTime',
+    'EXIF ApertureValue',
+    'EXIF ExposureMode',
+    'EXIF ISOSpeedRatings',
+    'EXIF UserComment',
+    ]
+
+def exif_fix_image_orientation(im, exif_tags):
+    """
+    Translate any EXIF orientation to raw orientation
+
+    Cons:
+    - REDUCES IMAGE QUALITY by recompressig it
+
+    Pros:
+    - Cures my neck pain
+    """
+    # Rotate image
+    if 'Image Orientation' in exif_tags:
+        rotation_map = {
+            3: 180,
+            6: 270,
+            8: 90}
+        orientation = exif_tags['Image Orientation'].values[0]
+        if orientation in rotation_map.keys():
+            im = im.rotate(
+                rotation_map[orientation])
+
+    return im
+
+def extract_exif(filename):
+    """
+    Returns EXIF tags found in file at ``filename``
+    """
+    exif_tags = {}
+
+    try:
+        image = open(filename)
+        exif_tags = process_file(image)
+    except IOError:
+        raise BadMediaFail(_('Could not read the image file.'))
+
+    return exif_tags
+
+def clean_exif(exif):
+    '''
+    Clean the result from anyt
+hing the database cannot handle
+    '''
+    # Discard any JPEG thumbnail, for database compatibility
+    # and that I cannot see a case when we would use it.
+    # It takes up some space too.
+    disabled_tags = [
+        'Thumbnail JPEGInterchangeFormatLength',
+        'JPEGThumbnail',
+        'Thumbnail JPEGInterchangeFormat']
+
+    clean_exif = {}
+
+    for key, value in exif.items():
+        if not key in disabled_tags:
+            clean_exif[key] = _ifd_tag_to_dict(value)
+
+    return clean_exif
+
+def _ifd_tag_to_dict(tag):
+    data = {
+        'printable': tag.printable,
+        'tag': tag.tag,
+        'field_type': tag.field_type,
+        'field_offset': tag.field_offset,
+        'field_length': tag.field_length,
+        'values': None}
+    if type(tag.values) == list:
+        data['values'] = []
+        for val in tag.values:
+            if isinstance(val, Ratio):
+                data['values'].append(
+                    _ratio_to_list(val))
+            else:
+                data['values'].append(val)
+    else:
+        data['values'] = tag.values
+
+    return data
+
+def _ratio_to_list(ratio):
+    return [ratio.num, ratio.den]
+
+def get_useful(tags):
+    useful = {}
+    for key, tag in tags.items():
+        if key in USEFUL_TAGS:
+            useful[key] = tag
+
+    return useful
+            
+
+def get_gps_data(tags):
+    """
+    Processes EXIF data returned by EXIF.py
+    """
+    if not 'Image GPSInfo' in tags:
+        return False
+
+    gps_data = {}
+
+    try:
+        dms_data = {
+            'latitude': tags['GPS GPSLatitude'],
+            'longitude': tags['GPS GPSLongitude']}
+
+        for key, dat in dms_data.items():
+            gps_data[key] = (
+                lambda v:
+                    float(v[0].num) / float(v[0].den) \
+                    + (float(v[1].num) / float(v[1].den) / 60 )\
+                    + (float(v[2].num) / float(v[2].den) / (60 * 60))
+                )(dat.values)
+    except KeyError:
+        pass
+
+    try:
+        gps_data['direction'] = (
+            lambda d:
+                float(d.num) / float(d.den)
+            )(tags['GPS GPSImgDirection'].values[0])
+    except KeyError:
+        pass
+
+    try:
+        gps_data['altitude'] = (
+            lambda a:
+                float(a.num) / float(a.den)
+            )(tags['GPS GPSAltitude'].values[0])
+    except KeyError:
+        pass
+
+    return gps_data
diff --git a/mediagoblin/tools/extlib/EXIF.py b/mediagoblin/tools/extlib/EXIF.py
new file mode 120000 (symlink)
index 0000000..82a2fb3
--- /dev/null
@@ -0,0 +1 @@
+../../../extlib/exif/EXIF.py
\ No newline at end of file
diff --git a/mediagoblin/tools/extlib/__init__.py b/mediagoblin/tools/extlib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29