|Exif sucks (if all you want to do is add tags)
||[May. 1st, 2006|11:41 pm]
OpenStreetMap (so I can take photos of road signs, to prove I didn't copy metadata from a copyrighted map), but also for doing groovey Google Maps mashups showing photos on a map.One of the things I've wanted do to for a little while now is extend the GPS related code on my phone to be able to take photos, and then tag them with where they were taken. Firstly this would be for use with |
As you may be aware, with Jpeg (and also Tiff), it's possible to store a bunch of metadata along with your photos. This could be the zoom level, the camera used, some sort of comment, if the flash was used, all sorts of things. This metadata is stored according to something called Exif. The Exif Standard defines how to store GPS information in the Exif tag set. So, you might initially think it would be quite easy to add in the GPS info to a Jpeg image.
This afternoon, I tried to do just that, and discovered it's much harder then you might initially think. Firstly, we need to find the Exif segment in the Jpeg file (I won't even start on trying to add one). Jpeg files seem to always start with 0xFFD8. After that, you have a two byte segment marker (to tell you what kind of thing the segment is). Then you have a two byte little endian number to tell you the length of the section (inclusive of headers). Then you have a null terminated identifier string (eg "Exif"). Then you might have a null byte to pad it to an even number of bytes. Then you have your segment data. In many cases (but perhaps not all?), you then have two null bytes. Finally, you have the next segment.
So, assuming you can find your Exif segment (by stepping forward over segments, guessing about the two byte nulls after them), you then need to grok the contents. Exif data is stored in an APP1 segment (marker 0xFFE1) with the identifier "Exif", and a single null padding. Within that you have a header (either II or MM to indicate little or big endian numbers), the version (0x2A00 = 42 for Exif 4.2), and then our first offset.
At that first offset, we find an IFD. This has a two byte number to tell us how many tags it has. Then we have that many tags, in any order they fancy. For each tag, you get an ID, a type, a count, and either a value (1, 2 or 4 bytes) or an offset (4 bytes). Then we have a two byte offset to the next IFD (or all 0s if no more). Then we have null terminated strings, at the offsets specified in the offset fields. Then we have another IFD (perhaps a main IFD, perhaps an Exif IFD, perhaps a GPS IFD). Rinse and repeat.
So, in order to add one single tag in, we have to:
- Parse all the IFDs, into their tags (including figuring out where the next tag will start in the IFD data)
- Match up all the next IFD offsets to IFDs
- Match up all the string to tag offsets
- Figure out which IFD to add our tag to
- Add it in
- Figure out where all our IFDs and strings now live at
- Go through the IFD next offsets, and put in the new values
- Find all the tags which are offsets (instead of values), figure out what their string is, and update the offset
- Update the segment length
- Write out
This means we need a complete, fully featured Exif tag and IFD parser just to be able to add a new tag. Surely they could've come up with a better scheme than this?
(Now I just need to write such a parser in python, then add support for adding a GPS IFD and all the GPS tags I want, then handle the fact that when my phone takes a photo, it doesn't initially have an Exif segment, and all this before I go to map the IoW next weekend.....)
Update: As you may have guessed, I failed to write such a parser in time. I'm hoping to attack the task at some point in the near future. That said, if you spot a suitable python library in the mean time, please do let me know :)
Update 2: Benno's excellent pexif python exif library now supports adding gps exif tags to images, no matter if they have exif tags to start with or not. I'm thankfully saved from the nasty task of writing my own!