EXIF fixes
- 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
This commit is contained in:
parent
a020391d90
commit
a180ca264e
@ -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)
|
||||
|
@ -80,20 +80,21 @@
|
||||
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 %}
|
||||
@ -194,7 +195,8 @@
|
||||
{% 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 %}
|
||||
|
168
mediagoblin/tools/exif.py
Normal file
168
mediagoblin/tools/exif.py
Normal file
@ -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
|
1
mediagoblin/tools/extlib/EXIF.py
Symbolic link
1
mediagoblin/tools/extlib/EXIF.py
Symbolic link
@ -0,0 +1 @@
|
||||
../../../extlib/exif/EXIF.py
|
0
mediagoblin/tools/extlib/__init__.py
Normal file
0
mediagoblin/tools/extlib/__init__.py
Normal file
Loading…
x
Reference in New Issue
Block a user