#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
EXIF file format data, including tag names, types, etc.
Most of this was copied with modifications from EXIFpy:
# Library to extract EXIF information from digital camera image files
# http://sourceforge.net/projects/exif-py/
#
# VERSION 1.1.0
#
# Copyright (c) 2002-2007 Gene Cash All rights reserved
# Copyright (c) 2007-2008 Ianaré Sévi All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# 3. Neither the name of the authors nor the names of its contributors
# may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import decimal, itertools
def filter_ascii (values) :
"""
Default post-filter for ASCII values.
This takes a single item of string data, splits it up into strings by ASCII-NUL, and trims the induvidual strings
"""
return [string.rstrip() for string in values[0].split('\x00') if string]
def build_ratio (num, denom) :
"""
Builds a Decimal ratio out of the given numerator and denominator
"""
return decimal.Decimal(num) / decimal.Decimal(denom)
def filter_ratio (values) :
"""
Default post-filter for Ratio values.
This takes the pairs of numerator/denominator values and builds Decimals out of them
"""
return [build_ratio(values[i], values[i + 1]) for i in xrange(0, len(values), 2)]
# IFD Tag type information, indexed by code
# { type_code: (type_fmt, name, filter_func) }
#
# type_fmt's that are one char will be prefixed with the count for use with struct.unpack, those with more chars will
# be repeated as many times for use with struct.unpack.
FIELD_TYPES = {
# 0x0000: (None, 'Proprietary' ), # ??? no such type
0x0001: ('B', 'Byte', None ),
0x0002: ('s', 'ASCII', filter_ascii ),
0x0003: ('H', 'Short', None ),
0x0004: ('L', 'Long', None ),
0x0005: ('LL', 'Ratio', filter_ratio ),
0x0006: ('b', 'Signed Byte', None ),
0x0007: ('s', 'Undefined', None ),
0x0008: ('h', 'Signed Short', None ),
0x0009: ('l', 'Signed Long', None ),
0x000A: ('ll', 'Signed Ratio', filter_ratio ),
}
def map_value (spec, value) :
"""
Map the given tag value to a printable string using the given value spec.
"""
if callable(spec):
# call mapping function
return spec(value)
else:
return spec.get(value, repr(value))
def make_string (seq):
"""
Filter a string to strip out non-printing chars
"""
# screen out non-printing characters
str = ''.join(c for c in seq if 32 <= c < 256)
if not str:
# no printing chars
return seq
else :
return str
def make_string_uc (seq) :
"""
# Special version to deal with the code in the first 8 bytes of a user comment.
# First 8 bytes gives coding system e.g. ASCII vs. JIS vs Unicode
XXX: decode?
"""
code = seq[0:8]
seq = seq[8:]
## Of course, this is only correct if ASCII, and the standard explicitly
## allows JIS and Unicode.
return make_string(seq)
# dictionary of main EXIF tag names
# first element of tuple is tag name, optional second element is
# another dictionary giving names to values
# { tag_type : (name, spec?) }
EXIF_TAGS = {
0x0100: ('ImageWidth', ),
0x0101: ('ImageLength', ),
0x0102: ('BitsPerSample', ),
0x0103: ('Compression',
{1: 'Uncompressed',
2: 'CCITT 1D',
3: 'T4/Group 3 Fax',
4: 'T6/Group 4 Fax',
5: 'LZW',
6: 'JPEG (old-style)',
7: 'JPEG',
8: 'Adobe Deflate',
9: 'JBIG B&W',
10: 'JBIG Color',
32766: 'Next',
32769: 'Epson ERF Compressed',
32771: 'CCIRLEW',
32773: 'PackBits',
32809: 'Thunderscan',
32895: 'IT8CTPAD',
32896: 'IT8LW',
32897: 'IT8MP',
32898: 'IT8BL',
32908: 'PixarFilm',
32909: 'PixarLog',
32946: 'Deflate',
32947: 'DCS',
34661: 'JBIG',
34676: 'SGILog',
34677: 'SGILog24',
34712: 'JPEG 2000',
34713: 'Nikon NEF Compressed',
65000: 'Kodak DCR Compressed',
65535: 'Pentax PEF Compressed'}),
0x0106: ('PhotometricInterpretation', ),
0x0107: ('Thresholding', ),
0x010A: ('FillOrder', ),
0x010D: ('DocumentName', ),
0x010E: ('ImageDescription', ),
0x010F: ('Make', ),
0x0110: ('Model', ),
0x0111: ('StripOffsets', ),
0x0112: ('Orientation',
{1: 'Horizontal (normal)',
2: 'Mirrored horizontal',
3: 'Rotated 180',
4: 'Mirrored vertical',
5: 'Mirrored horizontal then rotated 90 CCW',
6: 'Rotated 90 CW',
7: 'Mirrored horizontal then rotated 90 CW',
8: 'Rotated 90 CCW'}),
0x0115: ('SamplesPerPixel', ),
0x0116: ('RowsPerStrip', ),
0x0117: ('StripByteCounts', ),
0x011A: ('XResolution', ),
0x011B: ('YResolution', ),
0x011C: ('PlanarConfiguration', ),
0x011D: ('PageName', make_string),
0x0128: ('ResolutionUnit',
{1: 'Not Absolute',
2: 'Pixels/Inch',
3: 'Pixels/Centimeter'}),
0x012D: ('TransferFunction', ),
0x0131: ('Software', ),
0x0132: ('DateTime', ),
0x013B: ('Artist', ),
0x013E: ('WhitePoint', ),
0x013F: ('PrimaryChromaticities', ),
0x0156: ('TransferRange', ),
0x0200: ('JPEGProc', ),
0x0201: ('JPEGInterchangeFormat', ),
0x0202: ('JPEGInterchangeFormatLength', ),
0x0211: ('YCbCrCoefficients', ),
0x0212: ('YCbCrSubSampling', ),
0x0213: ('YCbCrPositioning',
{1: 'Centered',
2: 'Co-sited'}),
0x0214: ('ReferenceBlackWhite', ),
0x4746: ('Rating', ),
0x828D: ('CFARepeatPatternDim', ),
0x828E: ('CFAPattern', ),
0x828F: ('BatteryLevel', ),
0x8298: ('Copyright', ),
0x829A: ('ExposureTime', ),
0x829D: ('FNumber', ),
0x83BB: ('IPTC/NAA', ),
0x8769: ('ExifOffset', ),
0x8773: ('InterColorProfile', ),
0x8822: ('ExposureProgram',
{0: 'Unidentified',
1: 'Manual',
2: 'Program Normal',
3: 'Aperture Priority',
4: 'Shutter Priority',
5: 'Program Creative',
6: 'Program Action',
7: 'Portrait Mode',
8: 'Landscape Mode'}),
0x8824: ('SpectralSensitivity', ),
0x8825: ('GPSInfo', ),
0x8827: ('ISOSpeedRatings', ),
0x8828: ('OECF', ),
0x9000: ('ExifVersion', make_string),
0x9003: ('DateTimeOriginal', ),
0x9004: ('DateTimeDigitized', ),
0x9101: ('ComponentsConfiguration',
{0: '',
1: 'Y',
2: 'Cb',
3: 'Cr',
4: 'Red',
5: 'Green',
6: 'Blue'}),
0x9102: ('CompressedBitsPerPixel', ),
0x9201: ('ShutterSpeedValue', ),
0x9202: ('ApertureValue', ),
0x9203: ('BrightnessValue', ),
0x9204: ('ExposureBiasValue', ),
0x9205: ('MaxApertureValue', ),
0x9206: ('SubjectDistance', ),
0x9207: ('MeteringMode',
{0: 'Unidentified',
1: 'Average',
2: 'CenterWeightedAverage',
3: 'Spot',
4: 'MultiSpot',
5: 'Pattern'}),
0x9208: ('LightSource',
{0: 'Unknown',
1: 'Daylight',
2: 'Fluorescent',
3: 'Tungsten',
9: 'Fine Weather',
10: 'Flash',
11: 'Shade',
12: 'Daylight Fluorescent',
13: 'Day White Fluorescent',
14: 'Cool White Fluorescent',
15: 'White Fluorescent',
17: 'Standard Light A',
18: 'Standard Light B',
19: 'Standard Light C',
20: 'D55',
21: 'D65',
22: 'D75',
255: 'Other'}),
0x9209: ('Flash',
{0: 'No',
1: 'Fired',
5: 'Fired (?)', # no return sensed
7: 'Fired (!)', # return sensed
9: 'Fill Fired',
13: 'Fill Fired (?)',
15: 'Fill Fired (!)',
16: 'Off',
24: 'Auto Off',
25: 'Auto Fired',
29: 'Auto Fired (?)',
31: 'Auto Fired (!)',
32: 'Not Available'}),
0x920A: ('FocalLength', ),
0x9214: ('SubjectArea', ),
0x927C: ('MakerNote', ),
0x9286: ('UserComment', make_string_uc),
0x9290: ('SubSecTime', ),
0x9291: ('SubSecTimeOriginal', ),
0x9292: ('SubSecTimeDigitized', ),
# used by Windows Explorer
0x9C9B: ('XPTitle', ),
0x9C9C: ('XPComment', ),
0x9C9D: ('XPAuthor', ), #(ignored by Windows Explorer if Artist exists)
0x9C9E: ('XPKeywords', ),
0x9C9F: ('XPSubject', ),
0xA000: ('FlashPixVersion', make_string),
0xA001: ('ColorSpace',
{1: 'sRGB',
2: 'Adobe RGB',
65535: 'Uncalibrated'}),
0xA002: ('ExifImageWidth', ),
0xA003: ('ExifImageLength', ),
0xA005: ('InteroperabilityOffset', ),
0xA20B: ('FlashEnergy', ), # 0x920B in TIFF/EP
0xA20C: ('SpatialFrequencyResponse', ), # 0x920C
0xA20E: ('FocalPlaneXResolution', ), # 0x920E
0xA20F: ('FocalPlaneYResolution', ), # 0x920F
0xA210: ('FocalPlaneResolutionUnit', ), # 0x9210
0xA214: ('SubjectLocation', ), # 0x9214
0xA215: ('ExposureIndex', ), # 0x9215
0xA217: ('SensingMethod', # 0x9217
{1: 'Not defined',
2: 'One-chip color area',
3: 'Two-chip color area',
4: 'Three-chip color area',
5: 'Color sequential area',
7: 'Trilinear',
8: 'Color sequential linear'}),
0xA300: ('FileSource',
{1: 'Film Scanner',
2: 'Reflection Print Scanner',
3: 'Digital Camera'}),
0xA301: ('SceneType',
{1: 'Directly Photographed'}),
0xA302: ('CVAPattern', ),
0xA401: ('CustomRendered',
{0: 'Normal',
1: 'Custom'}),
0xA402: ('ExposureMode',
{0: 'Auto Exposure',
1: 'Manual Exposure',
2: 'Auto Bracket'}),
0xA403: ('WhiteBalance',
{0: 'Auto',
1: 'Manual'}),
0xA404: ('DigitalZoomRatio', ),
0xA405: ('FocalLengthIn35mmFilm', ),
0xA406: ('SceneCaptureType',
{0: 'Standard',
1: 'Landscape',
2: 'Portrait',
3: 'Night)'}),
0xA407: ('GainControl',
{0: 'None',
1: 'Low gain up',
2: 'High gain up',
3: 'Low gain down',
4: 'High gain down'}),
0xA408: ('Contrast',
{0: 'Normal',
1: 'Soft',
2: 'Hard'}),
0xA409: ('Saturation',
{0: 'Normal',
1: 'Soft',
2: 'Hard'}),
0xA40A: ('Sharpness',
{0: 'Normal',
1: 'Soft',
2: 'Hard'}),
0xA40B: ('DeviceSettingDescription', ),
0xA40C: ('SubjectDistanceRange', ),
0xA500: ('Gamma', ),
0xC4A5: ('PrintIM', ),
0xEA1C: ('Padding', ),
}
# interoperability tags
INTR_TAGS = {
0x0001: ('InteroperabilityIndex', ),
0x0002: ('InteroperabilityVersion', ),
0x1000: ('RelatedImageFileFormat', ),
0x1001: ('RelatedImageWidth', ),
0x1002: ('RelatedImageLength', ),
}
# GPS tags (not used yet, haven't seen camera with GPS)
GPS_TAGS = {
0x0000: ('GPSVersionID', ),
0x0001: ('GPSLatitudeRef', ),
0x0002: ('GPSLatitude', ),
0x0003: ('GPSLongitudeRef', ),
0x0004: ('GPSLongitude', ),
0x0005: ('GPSAltitudeRef', ),
0x0006: ('GPSAltitude', ),
0x0007: ('GPSTimeStamp', ),
0x0008: ('GPSSatellites', ),
0x0009: ('GPSStatus', ),
0x000A: ('GPSMeasureMode', ),
0x000B: ('GPSDOP', ),
0x000C: ('GPSSpeedRef', ),
0x000D: ('GPSSpeed', ),
0x000E: ('GPSTrackRef', ),
0x000F: ('GPSTrack', ),
0x0010: ('GPSImgDirectionRef', ),
0x0011: ('GPSImgDirection', ),
0x0012: ('GPSMapDatum', ),
0x0013: ('GPSDestLatitudeRef', ),
0x0014: ('GPSDestLatitude', ),
0x0015: ('GPSDestLongitudeRef', ),
0x0016: ('GPSDestLongitude', ),
0x0017: ('GPSDestBearingRef', ),
0x0018: ('GPSDestBearing', ),
0x0019: ('GPSDestDistanceRef', ),
0x001A: ('GPSDestDistance', ),
0x001D: ('GPSDate', ),
}
# Ignore these tags when quick processing
# 0x927C is MakerNote Tags
# 0x9286 is user comment
IGNORE_TAGS=(0x9286, 0x927C)
# http://tomtia.plala.jp/DigitalCamera/MakerNote/index.asp
def nikon_ev_bias(seq):
# First digit seems to be in steps of 1/6 EV.
# Does the third value mean the step size? It is usually 6,
# but it is 12 for the ExposureDifference.
#
# Check for an error condition that could cause a crash.
# This only happens if something has gone really wrong in
# reading the Nikon MakerNote.
if len( seq ) < 4 : return ""
#
if seq == [252, 1, 6, 0]:
return "-2/3 EV"
if seq == [253, 1, 6, 0]:
return "-1/2 EV"
if seq == [254, 1, 6, 0]:
return "-1/3 EV"
if seq == [0, 1, 6, 0]:
return "0 EV"
if seq == [2, 1, 6, 0]:
return "+1/3 EV"
if seq == [3, 1, 6, 0]:
return "+1/2 EV"
if seq == [4, 1, 6, 0]:
return "+2/3 EV"
# Handle combinations not in the table.
a = seq[0]
# Causes headaches for the +/- logic, so special case it.
if a == 0:
return "0 EV"
if a > 127:
a = 256 - a
ret_str = "-"
else:
ret_str = "+"
b = seq[2] # Assume third value means the step size
whole = a / b
a = a % b
if whole != 0:
ret_str = ret_str + str(whole) + " "
if a == 0:
ret_str = ret_str + "EV"
else:
r = Ratio(a, b)
ret_str = ret_str + r.__repr__() + " EV"
return ret_str
# Nikon E99x MakerNote Tags
MAKERNOTE_NIKON_NEWER_TAGS={
0x0001: ('MakernoteVersion', make_string), # Sometimes binary
0x0002: ('ISOSetting', make_string),
0x0003: ('ColorMode', ),
0x0004: ('Quality', ),
0x0005: ('Whitebalance', ),
0x0006: ('ImageSharpening', ),
0x0007: ('FocusMode', ),
0x0008: ('FlashSetting', ),
0x0009: ('AutoFlashMode', ),
0x000B: ('WhiteBalanceBias', ),
0x000C: ('WhiteBalanceRBCoeff', ),
0x000D: ('ProgramShift', nikon_ev_bias),
# Nearly the same as the other EV vals, but step size is 1/12 EV (?)
0x000E: ('ExposureDifference', nikon_ev_bias),
0x000F: ('ISOSelection', ),
0x0011: ('NikonPreview', ),
0x0012: ('FlashCompensation', nikon_ev_bias),
0x0013: ('ISOSpeedRequested', ),
0x0016: ('PhotoCornerCoordinates', ),
# 0x0017: Unknown, but most likely an EV value
0x0018: ('FlashBracketCompensationApplied', nikon_ev_bias),
0x0019: ('AEBracketCompensationApplied', ),
0x001A: ('ImageProcessing', ),
0x001B: ('CropHiSpeed', ),
0x001D: ('SerialNumber', ), # Conflict with 0x00A0 ?
0x001E: ('ColorSpace', ),
0x001F: ('VRInfo', ),
0x0020: ('ImageAuthentication', ),
0x0022: ('ActiveDLighting', ),
0x0023: ('PictureControl', ),
0x0024: ('WorldTime', ),
0x0025: ('ISOInfo', ),
0x0080: ('ImageAdjustment', ),
0x0081: ('ToneCompensation', ),
0x0082: ('AuxiliaryLens', ),
0x0083: ('LensType', ),
0x0084: ('LensMinMaxFocalMaxAperture', ),
0x0085: ('ManualFocusDistance', ),
0x0086: ('DigitalZoomFactor', ),
0x0087: ('FlashMode',
{0x00: 'Did Not Fire',
0x01: 'Fired, Manual',
0x07: 'Fired, External',
0x08: 'Fired, Commander Mode ',
0x09: 'Fired, TTL Mode'}),
0x0088: ('AFFocusPosition',
{0x0000: 'Center',
0x0100: 'Top',
0x0200: 'Bottom',
0x0300: 'Left',
0x0400: 'Right'}),
0x0089: ('BracketingMode',
{0x00: 'Single frame, no bracketing',
0x01: 'Continuous, no bracketing',
0x02: 'Timer, no bracketing',
0x10: 'Single frame, exposure bracketing',
0x11: 'Continuous, exposure bracketing',
0x12: 'Timer, exposure bracketing',
0x40: 'Single frame, white balance bracketing',
0x41: 'Continuous, white balance bracketing',
0x42: 'Timer, white balance bracketing'}),
0x008A: ('AutoBracketRelease', ),
0x008B: ('LensFStops', ),
0x008C: ('NEFCurve1', ), # ExifTool calls this 'ContrastCurve'
0x008D: ('ColorMode', ),
0x008F: ('SceneMode', ),
0x0090: ('LightingType', ),
0x0091: ('ShotInfo', ), # First 4 bytes are a version number in ASCII
0x0092: ('HueAdjustment', ),
# ExifTool calls this 'NEFCompression', should be 1-4
0x0093: ('Compression', ),
0x0094: ('Saturation',
{-3: 'B&W',
-2: '-2',
-1: '-1',
0: '0',
1: '1',
2: '2'}),
0x0095: ('NoiseReduction', ),
0x0096: ('NEFCurve2', ), # ExifTool calls this 'LinearizationTable'
0x0097: ('ColorBalance', ), # First 4 bytes are a version number in ASCII
0x0098: ('LensData', ), # First 4 bytes are a version number in ASCII
0x0099: ('RawImageCenter', ),
0x009A: ('SensorPixelSize', ),
0x009C: ('Scene Assist', ),
0x009E: ('RetouchHistory', ),
0x00A0: ('SerialNumber', ),
0x00A2: ('ImageDataSize', ),
# 00A3: unknown - a single byte 0
# 00A4: In NEF, looks like a 4 byte ASCII version number ('0200')
0x00A5: ('ImageCount', ),
0x00A6: ('DeletedImageCount', ),
0x00A7: ('TotalShutterReleases', ),
# First 4 bytes are a version number in ASCII, with version specific
# info to follow. Its hard to treat it as a string due to embedded nulls.
0x00A8: ('FlashInfo', ),
0x00A9: ('ImageOptimization', ),
0x00AA: ('Saturation', ),
0x00AB: ('DigitalVariProgram', ),
0x00AC: ('ImageStabilization', ),
0x00AD: ('Responsive AF', ), # 'AFResponse'
0x00B0: ('MultiExposure', ),
0x00B1: ('HighISONoiseReduction', ),
0x00B7: ('AFInfo', ),
0x00B8: ('FileInfo', ),
# 00B9: unknown
0x0100: ('DigitalICE', ),
0x0103: ('PreviewCompression',
{1: 'Uncompressed',
2: 'CCITT 1D',
3: 'T4/Group 3 Fax',
4: 'T6/Group 4 Fax',
5: 'LZW',
6: 'JPEG (old-style)',
7: 'JPEG',
8: 'Adobe Deflate',
9: 'JBIG B&W',
10: 'JBIG Color',
32766: 'Next',
32769: 'Epson ERF Compressed',
32771: 'CCIRLEW',
32773: 'PackBits',
32809: 'Thunderscan',
32895: 'IT8CTPAD',
32896: 'IT8LW',
32897: 'IT8MP',
32898: 'IT8BL',
32908: 'PixarFilm',
32909: 'PixarLog',
32946: 'Deflate',
32947: 'DCS',
34661: 'JBIG',
34676: 'SGILog',
34677: 'SGILog24',
34712: 'JPEG 2000',
34713: 'Nikon NEF Compressed',
65000: 'Kodak DCR Compressed',
65535: 'Pentax PEF Compressed',}),
0x0201: ('PreviewImageStart', ),
0x0202: ('PreviewImageLength', ),
0x0213: ('PreviewYCbCrPositioning',
{1: 'Centered',
2: 'Co-sited'}),
0x0010: ('DataDump', ),
}
MAKERNOTE_NIKON_OLDER_TAGS = {
0x0003: ('Quality',
{1: 'VGA Basic',
2: 'VGA Normal',
3: 'VGA Fine',
4: 'SXGA Basic',
5: 'SXGA Normal',
6: 'SXGA Fine'}),
0x0004: ('ColorMode',
{1: 'Color',
2: 'Monochrome'}),
0x0005: ('ImageAdjustment',
{0: 'Normal',
1: 'Bright+',
2: 'Bright-',
3: 'Contrast+',
4: 'Contrast-'}),
0x0006: ('CCDSpeed',
{0: 'ISO 80',
2: 'ISO 160',
4: 'ISO 320',
5: 'ISO 100'}),
0x0007: ('WhiteBalance',
{0: 'Auto',
1: 'Preset',
2: 'Daylight',
3: 'Incandescent',
4: 'Fluorescent',
5: 'Cloudy',
6: 'Speed Light'}),
}
# decode Olympus SpecialMode tag in MakerNote
def olympus_special_mode(v):
a={
0: 'Normal',
1: 'Unknown',
2: 'Fast',
3: 'Panorama'}
b={
0: 'Non-panoramic',
1: 'Left to right',
2: 'Right to left',
3: 'Bottom to top',
4: 'Top to bottom'}
if v[0] not in a or v[2] not in b:
return v
return '%s - sequence %d - %s' % (a[v[0]], v[1], b[v[2]])
MAKERNOTE_OLYMPUS_TAGS={
# ah HAH! those sneeeeeaky bastids! this is how they get past the fact
# that a JPEG thumbnail is not allowed in an uncompressed TIFF file
0x0100: ('JPEGThumbnail', ),
0x0200: ('SpecialMode', olympus_special_mode),
0x0201: ('JPEGQual',
{1: 'SQ',
2: 'HQ',
3: 'SHQ'}),
0x0202: ('Macro',
{0: 'Normal',
1: 'Macro',
2: 'SuperMacro'}),
0x0203: ('BWMode',
{0: 'Off',
1: 'On'}),
0x0204: ('DigitalZoom', ),
0x0205: ('FocalPlaneDiagonal', ),
0x0206: ('LensDistortionParams', ),
0x0207: ('SoftwareRelease', ),
0x0208: ('PictureInfo', ),
0x0209: ('CameraID', make_string), # print as string
0x0F00: ('DataDump', ),
0x0300: ('PreCaptureFrames', ),
0x0404: ('SerialNumber', ),
0x1000: ('ShutterSpeedValue', ),
0x1001: ('ISOValue', ),
0x1002: ('ApertureValue', ),
0x1003: ('BrightnessValue', ),
0x1004: ('FlashMode', ),
0x1004: ('FlashMode',
{2: 'On',
3: 'Off'}),
0x1005: ('FlashDevice',
{0: 'None',
1: 'Internal',
4: 'External',
5: 'Internal + External'}),
0x1006: ('ExposureCompensation', ),
0x1007: ('SensorTemperature', ),
0x1008: ('LensTemperature', ),
0x100b: ('FocusMode',
{0: 'Auto',
1: 'Manual'}),
0x1017: ('RedBalance', ),
0x1018: ('BlueBalance', ),
0x101a: ('SerialNumber', ),
0x1023: ('FlashExposureComp', ),
0x1026: ('ExternalFlashBounce',
{0: 'No',
1: 'Yes'}),
0x1027: ('ExternalFlashZoom', ),
0x1028: ('ExternalFlashMode', ),
0x1029: ('Contrast int16u',
{0: 'High',
1: 'Normal',
2: 'Low'}),
0x102a: ('SharpnessFactor', ),
0x102b: ('ColorControl', ),
0x102c: ('ValidBits', ),
0x102d: ('CoringFilter', ),
0x102e: ('OlympusImageWidth', ),
0x102f: ('OlympusImageHeight', ),
0x1034: ('CompressionRatio', ),
0x1035: ('PreviewImageValid',
{0: 'No',
1: 'Yes'}),
0x1036: ('PreviewImageStart', ),
0x1037: ('PreviewImageLength', ),
0x1039: ('CCDScanMode',
{0: 'Interlaced',
1: 'Progressive'}),
0x103a: ('NoiseReduction',
{0: 'Off',
1: 'On'}),
0x103b: ('InfinityLensStep', ),
0x103c: ('NearLensStep', ),
# TODO - these need extra definitions
# http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-6.90/html/TagNames/Olympus.html
0x2010: ('Equipment', ),
0x2020: ('CameraSettings', ),
0x2030: ('RawDevelopment', ),
0x2040: ('ImageProcessing', ),
0x2050: ('FocusInfo', ),
0x3000: ('RawInfo ', ),
}
# 0x2020 CameraSettings
MAKERNOTE_OLYMPUS_TAG_0x2020={
0x0100: ('PreviewImageValid',
{0: 'No',
1: 'Yes'}),
0x0101: ('PreviewImageStart', ),
0x0102: ('PreviewImageLength', ),
0x0200: ('ExposureMode',
{1: 'Manual',
2: 'Program',
3: 'Aperture-priority AE',
4: 'Shutter speed priority AE',
5: 'Program-shift'}),
0x0201: ('AELock',
{0: 'Off',
1: 'On'}),
0x0202: ('MeteringMode',
{2: 'Center Weighted',
3: 'Spot',
5: 'ESP',
261: 'Pattern+AF',
515: 'Spot+Highlight control',
1027: 'Spot+Shadow control'}),
0x0300: ('MacroMode',
{0: 'Off',
1: 'On'}),
0x0301: ('FocusMode',
{0: 'Single AF',
1: 'Sequential shooting AF',
2: 'Continuous AF',
3: 'Multi AF',
10: 'MF'}),
0x0302: ('FocusProcess',
{0: 'AF Not Used',
1: 'AF Used'}),
0x0303: ('AFSearch',
{0: 'Not Ready',
1: 'Ready'}),
0x0304: ('AFAreas', ),
0x0401: ('FlashExposureCompensation', ),
0x0500: ('WhiteBalance2',
{0: 'Auto',
16: '7500K (Fine Weather with Shade)',
17: '6000K (Cloudy)',
18: '5300K (Fine Weather)',
20: '3000K (Tungsten light)',
21: '3600K (Tungsten light-like)',
33: '6600K (Daylight fluorescent)',
34: '4500K (Neutral white fluorescent)',
35: '4000K (Cool white fluorescent)',
48: '3600K (Tungsten light-like)',
256: 'Custom WB 1',
257: 'Custom WB 2',
258: 'Custom WB 3',
259: 'Custom WB 4',
512: 'Custom WB 5400K',
513: 'Custom WB 2900K',
514: 'Custom WB 8000K', }),
0x0501: ('WhiteBalanceTemperature', ),
0x0502: ('WhiteBalanceBracket', ),
0x0503: ('CustomSaturation', ), # (3 numbers: 1. CS Value, 2. Min, 3. Max)
0x0504: ('ModifiedSaturation',
{0: 'Off',
1: 'CM1 (Red Enhance)',
2: 'CM2 (Green Enhance)',
3: 'CM3 (Blue Enhance)',
4: 'CM4 (Skin Tones)'}),
0x0505: ('ContrastSetting', ), # (3 numbers: 1. Contrast, 2. Min, 3. Max)
0x0506: ('SharpnessSetting', ), # (3 numbers: 1. Sharpness, 2. Min, 3. Max)
0x0507: ('ColorSpace',
{0: 'sRGB',
1: 'Adobe RGB',
2: 'Pro Photo RGB'}),
0x0509: ('SceneMode',
{0: 'Standard',
6: 'Auto',
7: 'Sport',
8: 'Portrait',
9: 'Landscape+Portrait',
10: 'Landscape',
11: 'Night scene',
13: 'Panorama',
16: 'Landscape+Portrait',
17: 'Night+Portrait',
19: 'Fireworks',
20: 'Sunset',
22: 'Macro',
25: 'Documents',
26: 'Museum',
28: 'Beach&Snow',
30: 'Candle',
35: 'Underwater Wide1',
36: 'Underwater Macro',
39: 'High Key',
40: 'Digital Image Stabilization',
44: 'Underwater Wide2',
45: 'Low Key',
46: 'Children',
48: 'Nature Macro'}),
0x050a: ('NoiseReduction',
{0: 'Off',
1: 'Noise Reduction',
2: 'Noise Filter',
3: 'Noise Reduction + Noise Filter',
4: 'Noise Filter (ISO Boost)',
5: 'Noise Reduction + Noise Filter (ISO Boost)'}),
0x050b: ('DistortionCorrection',
{0: 'Off',
1: 'On'}),
0x050c: ('ShadingCompensation',
{0: 'Off',
1: 'On'}),
0x050d: ('CompressionFactor', ),
0x050f: ('Gradation',
{'-1 -1 1': 'Low Key',
'0 -1 1': 'Normal',
'1 -1 1': 'High Key'}),
0x0520: ('PictureMode',
{1: 'Vivid',
2: 'Natural',
3: 'Muted',
256: 'Monotone',
512: 'Sepia'}),
0x0521: ('PictureModeSaturation', ),
0x0522: ('PictureModeHue?', ),
0x0523: ('PictureModeContrast', ),
0x0524: ('PictureModeSharpness', ),
0x0525: ('PictureModeBWFilter',
{0: 'n/a',
1: 'Neutral',
2: 'Yellow',
3: 'Orange',
4: 'Red',
5: 'Green'}),
0x0526: ('PictureModeTone',
{0: 'n/a',
1: 'Neutral',
2: 'Sepia',
3: 'Blue',
4: 'Purple',
5: 'Green'}),
0x0600: ('Sequence', ), # 2 or 3 numbers: 1. Mode, 2. Shot number, 3. Mode bits
0x0601: ('PanoramaMode', ), # (2 numbers: 1. Mode, 2. Shot number)
0x0603: ('ImageQuality2',
{1: 'SQ',
2: 'HQ',
3: 'SHQ',
4: 'RAW'}),
0x0901: ('ManometerReading', ),
}
MAKERNOTE_CASIO_TAGS={
0x0001: ('RecordingMode',
{1: 'Single Shutter',
2: 'Panorama',
3: 'Night Scene',
4: 'Portrait',
5: 'Landscape'}),
0x0002: ('Quality',
{1: 'Economy',
2: 'Normal',
3: 'Fine'}),
0x0003: ('FocusingMode',
{2: 'Macro',
3: 'Auto Focus',
4: 'Manual Focus',
5: 'Infinity'}),
0x0004: ('FlashMode',
{1: 'Auto',
2: 'On',
3: 'Off',
4: 'Red Eye Reduction'}),
0x0005: ('FlashIntensity',
{11: 'Weak',
13: 'Normal',
15: 'Strong'}),
0x0006: ('Object Distance', ),
0x0007: ('WhiteBalance',
{1: 'Auto',
2: 'Tungsten',
3: 'Daylight',
4: 'Fluorescent',
5: 'Shade',
129: 'Manual'}),
0x000B: ('Sharpness',
{0: 'Normal',
1: 'Soft',
2: 'Hard'}),
0x000C: ('Contrast',
{0: 'Normal',
1: 'Low',
2: 'High'}),
0x000D: ('Saturation',
{0: 'Normal',
1: 'Low',
2: 'High'}),
0x0014: ('CCDSpeed',
{64: 'Normal',
80: 'Normal',
100: 'High',
125: '+1.0',
244: '+3.0',
250: '+2.0'}),
}
MAKERNOTE_FUJIFILM_TAGS={
0x0000: ('NoteVersion', make_string),
0x1000: ('Quality', ),
0x1001: ('Sharpness',
{1: 'Soft',
2: 'Soft',
3: 'Normal',
4: 'Hard',
5: 'Hard'}),
0x1002: ('WhiteBalance',
{0: 'Auto',
256: 'Daylight',
512: 'Cloudy',
768: 'DaylightColor-Fluorescent',
769: 'DaywhiteColor-Fluorescent',
770: 'White-Fluorescent',
1024: 'Incandescent',
3840: 'Custom'}),
0x1003: ('Color',
{0: 'Normal',
256: 'High',
512: 'Low'}),
0x1004: ('Tone',
{0: 'Normal',
256: 'High',
512: 'Low'}),
0x1010: ('FlashMode',
{0: 'Auto',
1: 'On',
2: 'Off',
3: 'Red Eye Reduction'}),
0x1011: ('FlashStrength', ),
0x1020: ('Macro',
{0: 'Off',
1: 'On'}),
0x1021: ('FocusMode',
{0: 'Auto',
1: 'Manual'}),
0x1030: ('SlowSync',
{0: 'Off',
1: 'On'}),
0x1031: ('PictureMode',
{0: 'Auto',
1: 'Portrait',
2: 'Landscape',
4: 'Sports',
5: 'Night',
6: 'Program AE',
256: 'Aperture Priority AE',
512: 'Shutter Priority AE',
768: 'Manual Exposure'}),
0x1100: ('MotorOrBracket',
{0: 'Off',
1: 'On'}),
0x1300: ('BlurWarning',
{0: 'Off',
1: 'On'}),
0x1301: ('FocusWarning',
{0: 'Off',
1: 'On'}),
0x1302: ('AEWarning',
{0: 'Off',
1: 'On'}),
}
MAKERNOTE_CANON_TAGS = {
0x0006: ('ImageType', ),
0x0007: ('FirmwareVersion', ),
0x0008: ('ImageNumber', ),
0x0009: ('OwnerName', ),
}
# this is in element offset, name, optional value dictionary format
MAKERNOTE_CANON_TAG_0x001 = {
1: ('Macromode',
{1: 'Macro',
2: 'Normal'}),
2: ('SelfTimer', ),
3: ('Quality',
{2: 'Normal',
3: 'Fine',
5: 'Superfine'}),
4: ('FlashMode',
{0: 'Flash Not Fired',
1: 'Auto',
2: 'On',
3: 'Red-Eye Reduction',
4: 'Slow Synchro',
5: 'Auto + Red-Eye Reduction',
6: 'On + Red-Eye Reduction',
16: 'external flash'}),
5: ('ContinuousDriveMode',
{0: 'Single Or Timer',
1: 'Continuous'}),
7: ('FocusMode',
{0: 'One-Shot',
1: 'AI Servo',
2: 'AI Focus',
3: 'MF',
4: 'Single',
5: 'Continuous',
6: 'MF'}),
10: ('ImageSize',
{0: 'Large',
1: 'Medium',
2: 'Small'}),
11: ('EasyShootingMode',
{0: 'Full Auto',
1: 'Manual',
2: 'Landscape',
3: 'Fast Shutter',
4: 'Slow Shutter',
5: 'Night',
6: 'B&W',
7: 'Sepia',
8: 'Portrait',
9: 'Sports',
10: 'Macro/Close-Up',
11: 'Pan Focus'}),
12: ('DigitalZoom',
{0: 'None',
1: '2x',
2: '4x'}),
13: ('Contrast',
{0xFFFF: 'Low',
0: 'Normal',
1: 'High'}),
14: ('Saturation',
{0xFFFF: 'Low',
0: 'Normal',
1: 'High'}),
15: ('Sharpness',
{0xFFFF: 'Low',
0: 'Normal',
1: 'High'}),
16: ('ISO',
{0: 'See ISOSpeedRatings Tag',
15: 'Auto',
16: '50',
17: '100',
18: '200',
19: '400'}),
17: ('MeteringMode',
{3: 'Evaluative',
4: 'Partial',
5: 'Center-weighted'}),
18: ('FocusType',
{0: 'Manual',
1: 'Auto',
3: 'Close-Up (Macro)',
8: 'Locked (Pan Mode)'}),
19: ('AFPointSelected',
{0x3000: 'None (MF)',
0x3001: 'Auto-Selected',
0x3002: 'Right',
0x3003: 'Center',
0x3004: 'Left'}),
20: ('ExposureMode',
{0: 'Easy Shooting',
1: 'Program',
2: 'Tv-priority',
3: 'Av-priority',
4: 'Manual',
5: 'A-DEP'}),
23: ('LongFocalLengthOfLensInFocalUnits', ),
24: ('ShortFocalLengthOfLensInFocalUnits', ),
25: ('FocalUnitsPerMM', ),
28: ('FlashActivity',
{0: 'Did Not Fire',
1: 'Fired'}),
29: ('FlashDetails',
{14: 'External E-TTL',
13: 'Internal Flash',
11: 'FP Sync Used',
7: '2nd("Rear")-Curtain Sync Used',
4: 'FP Sync Enabled'}),
32: ('FocusMode',
{0: 'Single',
1: 'Continuous'}),
}
MAKERNOTE_CANON_TAG_0x004 = {
7: ('WhiteBalance',
{0: 'Auto',
1: 'Sunny',
2: 'Cloudy',
3: 'Tungsten',
4: 'Fluorescent',
5: 'Flash',
6: 'Custom'}),
9: ('SequenceNumber', ),
14: ('AFPointUsed', ),
15: ('FlashBias',
{0xFFC0: '-2 EV',
0xFFCC: '-1.67 EV',
0xFFD0: '-1.50 EV',
0xFFD4: '-1.33 EV',
0xFFE0: '-1 EV',
0xFFEC: '-0.67 EV',
0xFFF0: '-0.50 EV',
0xFFF4: '-0.33 EV',
0x0000: '0 EV',
0x000C: '0.33 EV',
0x0010: '0.50 EV',
0x0014: '0.67 EV',
0x0020: '1 EV',
0x002C: '1.33 EV',
0x0030: '1.50 EV',
0x0034: '1.67 EV',
0x0040: '2 EV'}),
19: ('SubjectDistance', ),
}
# ratio object that eventually will be able to reduce itself to lowest
# common denominator for printing
def gcd(a, b):
if b == 0:
return a
else:
return gcd(b, a % b)
class Ratio:
def __init__(self, num, den):
self.num = num
self.den = den
def __repr__(self):
self.reduce()
if self.den == 1:
return str(self.num)
return '%d/%d' % (self.num, self.den)
def reduce(self):
div = gcd(self.num, self.den)
if div > 1:
self.num = self.num / div
self.den = self.den / div