summaryrefslogtreecommitdiff
path: root/rapid/metadata.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/metadata.py')
-rwxr-xr-xrapid/metadata.py173
1 files changed, 159 insertions, 14 deletions
diff --git a/rapid/metadata.py b/rapid/metadata.py
index 1d4b2df..f8886d7 100755
--- a/rapid/metadata.py
+++ b/rapid/metadata.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: latin1 -*-
-### Copyright (C) 2007 Damon Lynch <damonlynch@gmail.com>
+### Copyright (C) 2007-10 Damon Lynch <damonlynch@gmail.com>
### 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
@@ -20,7 +20,9 @@
import re
import datetime
import sys
+import subprocess
import config
+import types
try:
import pyexiv2
@@ -28,18 +30,98 @@ except ImportError:
sys.stderr.write("You need to install pyexiv2, the python binding for exiv2, to run this program.\n" )
sys.exit(1)
-#only pyexiv2 0.1.2 and 0.1.3 use the "Rational" class
-#is there a superior way to find which version of pyexiv2 is being used?
+#only pyexiv2 <= 0.1.1 does not use the "Rational" class
if 'Rational' in dir(pyexiv2):
usesRational = True
else:
usesRational = False
+#get versions of pyexiv2 and exiv2 libraries
+if 'version_info' in dir(pyexiv2):
+ pyexiv2_version = pyexiv2.version_info
+ exiv2_version = pyexiv2.exiv2_version_info
+ baseclass = eval('pyexiv2.metadata.ImageMetadata')
+else:
+ pyexiv2_version = (0,1,'x')
+ # try to determine the version of exiv2 from it's standard output
+ try:
+ proc = subprocess.Popen(['exiv2', '-V'], stdout=subprocess.PIPE)
+ output = proc.communicate()[0]
+ except:
+ output = None
+ exiv2_version = None
+ if output:
+ # assume output contains the line 'exiv2 0.x' or possibly
+ # 'exiv2 0.x.x'
+ start = output.find('exiv2 ')
+ if start < 0:
+ exiv2_version = None
+ else:
+ end = output.find('\n', start)
+ if end:
+ exiv2_v = output[6:end]
+ else:
+ exiv2_v = output[6:]
+
+ exiv2_version = []
+ dot = exiv2_v.find('.')
+ while dot > 0:
+ exiv2_version += [int(exiv2_v[:dot])]
+ exiv2_v = exiv2_v[dot+1:]
+ dot = exiv2_v.find('.')
+ exiv2_version += [int(exiv2_v)]
+ exiv2_version = tuple(exiv2_version)
+
+
+ baseclass = eval('pyexiv2.Image')
+
+def __version_info(version):
+ if not version:
+ return ''
+ else:
+ v = ''
+ for i in version:
+ v += '.%s' % i
+ return v[1:]
+
+def version_info():
+ return __version_info(pyexiv2_version)
+
+def exiv2_version_info():
+ return __version_info(exiv2_version)
+
+RAW_FILE_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mrw',
+ 'nef', 'orf', 'pef', 'raf', 'raw', 'sr2']
-class MetaData(pyexiv2.Image):
+#exiv2 0.18.1 introduces support for Panasonic .RW2 files
+#pyexiv2 in combination with exiv2 0.18 segfaults when trying to read an
+#RW2 files, so we should not read those! exiv2 0.17 & pyexiv2 segfaults
+#with MEF files.
+
+if exiv2_version[0] > 0:
+ RAW_FILE_EXTENSIONS += ['rw2', 'mef']
+else:
+ if exiv2_version[1] > 17:
+ RAW_FILE_EXTENSIONS += ['mef']
+ if exiv2_version[1] > 18:
+ RAW_FILE_EXTENSIONS += ['rw2']
+ else:
+ if len(exiv2_version) > 2:
+ if exiv2_version[2] >= 1:
+ RAW_FILE_EXTENSIONS += ['rw2']
+
+RAW_FILE_EXTENSIONS.sort()
+
+NON_RAW_IMAGE_FILE_EXTENSIONS = ['jpg', 'jpe', 'jpeg', 'tif', 'tiff']
+
+
+class MetaData(baseclass):
"""
Class providing human readable access to image metadata
+
"""
+
+ __version01__ = pyexiv2_version[0] == 0 and pyexiv2_version[1] == 1
def aperture(self, missing=''):
"""
@@ -97,10 +179,16 @@ class MetaData(pyexiv2.Image):
try:
if usesRational:
+
e = str(self["Exif.Photo.ExposureTime"])
+
e0, e1 = e.split('/')
e0 = int(e0)
e1 = int(e1)
+ # some values, e.g. Nikon, are in the format "10/1600"
+ if (e0 > 1) and (e0 < e1):
+ e1 = e1 / e0
+ e0 = 1
else:
e0, e1 = self["Exif.Photo.ExposureTime"]
@@ -111,7 +199,7 @@ class MetaData(pyexiv2.Image):
else:
return str(e0)
else:
- return e
+ return "%s/%s" % (e0,e1)
elif e0 > e1:
e = float(e0) / e1
if alternativeFormat:
@@ -171,7 +259,7 @@ class MetaData(pyexiv2.Image):
def cameraSerial(self, missing=''):
try:
- keys = self.exifKeys()
+ keys = self.rpd_keys()
if 'Exif.Canon.SerialNumber' in keys:
v = self['Exif.Canon.SerialNumber']
elif 'Exif.Nikon3.SerialNumber' in keys:
@@ -196,9 +284,11 @@ class MetaData(pyexiv2.Image):
def shutterCount(self, missing=''):
try:
- keys = self.exifKeys()
+ keys = self.rpd_keys()
if 'Exif.Nikon3.ShutterCount' in keys:
v = self['Exif.Nikon3.ShutterCount']
+ elif 'Exif.Canon.FileNumber' in keys:
+ v = self['Exif.Canon.FileNumber']
elif 'Exif.Canon.ImageNumber' in keys:
v = self['Exif.Canon.ImageNumber']
else:
@@ -290,12 +380,13 @@ class MetaData(pyexiv2.Image):
Returns missing either metadata value is not present.
"""
- keys = self.exifKeys()
+ keys = self.rpd_keys()
try:
if "Exif.Photo.DateTimeOriginal" in keys:
- return self["Exif.Photo.DateTimeOriginal"]
+ v = self["Exif.Photo.DateTimeOriginal"]
else:
- return self["Exif.Image.DateTime"]
+ v = self["Exif.Image.DateTime"]
+ return v
except:
return missing
@@ -309,11 +400,66 @@ class MetaData(pyexiv2.Image):
def orientation(self, missing=''):
"""
Returns the orientation of the image, as recorded by the camera
+ Return type int
"""
try:
- return self['Exif.Image.Orientation']
+ v = self['Exif.Image.Orientation']
+ if isinstance(v, types.StringType):
+ # pyexiv2 >= 0.2 returns a string, not an int
+ v = int(v)
+ return v
except:
return missing
+
+ # following class methods are designed to cope with using both
+ # pyexiv2 0.1.x and pyexiv2 0.2.x
+
+ def getThumbnailData(self, max_size_needed=0):
+ """
+ Returns a thumbnail of the image.
+
+ If the image supports multiple thumbnails, and max_size_needed
+ is not 0, then it will search for the smallest thumbnail that
+ matches the size required
+
+ The image will be in whatever format the thumbnail itself is,
+ typically a jpeg or tiff.
+ """
+ if self.__version01__:
+ return pyexiv2.Image.getThumbnailData(self)[1]
+
+ else:
+ if not self.previews:
+ return None, None
+ else:
+ if max_size_needed:
+ for thumbnail in self.previews:
+ if thumbnail.dimensions[0] >= max_size_needed or thumbnail.dimensions[1] >= max_size_needed:
+ break
+ else:
+ thumbnail = self.previews[-1]
+
+ return thumbnail.data
+
+ def read(self):
+ if self.__version01__:
+ self.readMetadata()
+ else:
+ pyexiv2.metadata.ImageMetadata.read(self)
+
+ def rpd_keys(self):
+ if self.__version01__:
+ return pyexiv2.Image.exifKeys(self)
+ else:
+ return self.exif_keys
+
+ def __getitem__(self, key):
+ if self.__version01__:
+ return pyexiv2.Image.__getitem__(self, key)
+ else:
+ return pyexiv2.metadata.ImageMetadata.__getitem__(self, key).raw_value
+
+
class DummyMetaData(MetaData):
"""
@@ -376,16 +522,15 @@ class DummyMetaData(MetaData):
if __name__ == '__main__':
import sys
+
if (len(sys.argv) != 2):
print 'Usage: ' + sys.argv[0] + ' path/to/photo/containing/metadata'
m = DummyMetaData()
else:
m = MetaData(sys.argv[1])
- m.readMetadata()
+ m.read()
-# for i in m.exifKeys():
-# print i
print "f"+ m.aperture('missing ')
print "ISO " + m.iso('missing ')
print m.exposureTime(missing='missing ') + " sec"