Tyf
Foreword
The main goal of this package is to provide pythonic way to read and edit EXIF data from JPEG file.
>>> import Tyf >>> jpg = Tyf.open("a_file.jpg") >>> jpg.ifd0.gps["GPSLatitude"] 51.1597896 >>> jpg.ifd0.gps["GPSLatitude"] = 45.3265487 >>> jpg.save("another_file.jpg")
Where are EXIF in JPEG file ?
Googling « Exif file format » you can find any pages that explain how JPEG file is build :
SOI Marker | Marker XX size=SSSS | Marker YY size=TTTT | SOS Marker size=UUUU | Image stream | EOI Marker | ||||||
FFD8 | FFXX | SSSS | DDDD… | FFYY | TTTT | DDDD… | FFDA | UUUU | DDDD… | IIII… | FFD9 |
EXIF informations are contained in APP1 segment of JPEG File (marker number E1) :
FFE1 | APP1 Marker | ||
SSSS | APP1 Data Size | ||
45786966 0000 | APP1 Data | Exif Header → b"Exif\x00\x00" | |
49492A00 08000000 | TIFF Header | ||
XXXX… | IFD0 (main image) | Directory | |
LLLLLLLL | Link to IFD1 | ||
XXXX… | Data area of IFD0 | ||
XXXX… | Exif SubIFD | Directory | |
00000000 | End of Link | ||
XXXX… | Data area of Exif SubIFD | ||
XXXX… | IFD1 (thumbnail image) | Directory | |
00000000 | End of Link | ||
XXXX… | Data area of IFD1 | ||
FFD8XXXX…XXXXFFD9 | Thumbnail image |
EXIF is actualy a Sub Image File Directory (SubIFD) contained in a TIFF IFD. So once EXIF data extracted from JPEG file we have to deal with data[6:] (striped from Exif Header) as a TIFF (Tagged Image File Format) file. The idea here is to base Tyf package on TIFF Tag and Ifd classes implementing TIFF specification. Tyf has to be able to read and edit TIFF tags…
Main idea
Firstly, as end-up user, I only want to assign a value to a tag name or to get value from a tag name. It is easily associatd to well known dictionary Python built-in type. It is the mean reason why Tyf.ifd.Ifd class is an implementation of dict object.
Secondly, tag values can be tricky to understand. For example, GPS longitude is a set of 6 integers that have to be interpreted as three rationals for degrees, minutes and seconds. It means that code has to provide encoders and decoders according to IFD entries type or tag. They are defined in Tyf.encoders and Tyf.decoders.
>>> enc = getattr(Tyf.encoders, "_0x2") # 0x2 for GPSLongitude >>> enc(5.3212315165454) (5, 1, 19, 1, 25842026, 1572525) >>> enc = getattr(Tyf.encoders, "_0x1") # 0x1 for GPSLongitudeRef >>> enc(5.3212315165454) # should return the north hemisphere value b'N\x00'
To keep a maximum flexibility with my code, I wish to have a « human » and a « raw » way to read or write values in IFD :
- The human way, using Tyf.ifd.Ifd.__getitem__ and Tyf.ifd.Ifd.__setitem__ methods, to work directly with Python values (ie datetime object instead of formated string, float instead of rational…)
- The raw way, using Tyf.ifd.Ifd.get and Tyf.ifd.Ifd.set methods, to work directly with tag values which will exclusively be a bytes sequence or a tuple (even with only one value)
Finaly I want the Tyf.ifd.Tag class to be end user friendly. A TIFF IFD is a collection of entries each one defining a tag with 4 entities : id number, type data, count of data and data itself. Tag id are integers and for each tag id is associated a type and sometime a default value. So Tyf.ifd.Tag class should know about those values when I instanciate it with nothing but a keyword.
>>> tag = Tyf.ifd.Tag("ReferenceBlackWhite") >>> tag.value (0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) >>> tag.tag 532 >>> tag.type 5 >>> tag.count 24
A Tyf.tags module containing a database of all tag should do the work. See this page for more informations.