Python and EXIF Metadata: There’s more than one way to do it!

Topic: Technology| 2 Comments »

So, at the persistent urging of my friend Stephen, I decided to dip my toe once more into the snake-infested waters of the Pythonian Amazon instead of reaching into my back pocket and hacking the problem to pieces with my Perl. But later, I found out after having reached a couple of IT professional experts at Blue Whale Media Ltd that I did the right thing.

The problem:

I love Windows Live Photo Gallery.  It has changed my photo-organizing life.  However, someone at Microsoft  decided to be briliant and make the import wizard (a jewel that groups your pictures by time so you can import groups of pictures taken at approximately the same time), groups picture by DATE MODIFIED instead of date taken.  Which is brilliant.  Especially if you are on a trip and want to “prematurely” edit or tag any picture while it is still on the memory card.

So essentially, I just get back from a trip with my in-laws and I have about 300 pictures suffering from this “date modified” sickness.

The solution:

This time, instead of Perlifying up a solution, I turn to the “There should be one– and preferably only one –obvious way to do it” Python way to do it, and discover that “There are 10, or more, non-obvious ways to do it, but only some of them do everything you want and most of them do part of what you want.”

Eventually, I grabbed PIL (Python Imaging Library) from comment on a similar rant against Python EXIF handling and called it good:

from PIL import Image
from PIL.ExifTags import TAGS

def get_exif(fn):
ret = {}
i = Image.open(fn)
info = i._getexif()
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value
return ret

My quick and dirty script to change “date modified” on all pictures in a directory (recursively!) turned out to look like this:

from PIL import Image
from PIL.ExifTags import TAGS
import pprint
import time
import datetime
import os
import sys

def get_exif(fn):
ret = {}
i = Image.open(fn)
info = i._getexif()
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
ret[decoded] = value
return ret

def set_modify_time(_filename):
exif_info = get_exif(_filename);
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(exif_info)
print exif_info[‘DateTime’]

#Get EXIF Creation Date
exif_date,exif_time  = exif_info[‘DateTime’].split(‘ ‘)
print exif_date,exif_time
(y,mm,d) = exif_date.split(‘:’)
(h,min,s) = exif_time.split(‘:’)
date_list = (y,mm,d,h,min,s)
print date_list
#       int_date_list = [map(int, i) for i in date_list]
#WTF< why the H is that this:
#(’2006′, ’18′, ’12′, ’17′, ’18′, ’26′)
#[[2, 0, 0, 6], [1, 8], [1, 2], [1, 7], [1, 8], [2, 6]]
int_date_list = map(int, date_list)
print "int_date_list:", int_date_list
(y,mm,d,h,min,s) = int_date_list
print y,mm,d,h,min,s
#create unix timestamp object from EXIF date
exif_dateTaken = datetime.datetime(y,mm,d,h,min,s)
print str(exif_dateTaken)
unix_timestamp = time.mktime(exif_dateTaken.timetuple())

#set modify,access times on file
os.utime(_filename, (unix_timestamp, unix_timestamp))

return 1

def dir_files(_dir):
fileList = []
rootdir = _dir
for root, subFolders, files in os.walk(rootdir):
for file in files:
fileList.append(os.path.join(root,file))
print ‘fileList’, fileList
return fileList

def listFiles(dir):
basedir = dir
print "Files in ", os.path.abspath(dir), ": "
subdirlist = []
for item in os.listdir(dir):
if os.path.isfile(item):
print item
else:
subdirlist.append(os.path.join(basedir, item))
for subdir in subdirlist:
listFiles(subdir)

if __name__ == "__main__":
file = ‘C:\\tmp\\DSCN1229.JPG’
files = dir_files(‘E:\\Raw’)
print files
for file in files:
print file
if(file.find(‘JPG’) != -1):
set_modify_time(file)

Of course Python sucks and uses stupid whitespace, so I had to wrap the above in a <pre> tag.  Ugh. I’m sorry I spared you the fun game of “guess where the indentation goes!”, but I digress.

Anyway, that only gets me halfway there, because what I really want is a way to be able to both READ and WRITE EXIF metadata in Python. Because my camera decided it was 2037 for a few dozen pictures. And now we come to the title of my post. Apparently, there are several hundred ways to do that in Python, and not all of them will do what I want (read and write EXIF stuff) or are supported anymore (which is kind-of important).

So here, there’s more than one way to do it.

I’ll let you do the research and decide:

http://www.blog.pythonlibrary.org/2010/03/28/getting-photo-metadata-exif-using-python/

http://wolfram.kriesing.de/blog/index.php/2006/reading-out-exif-data-via-python

http://www.google.com/search?q=python+exif

Meanwhile, I’ll be doing some old-fashioned date math and changing some metadata. One hundred and five times. Thanks.

P.S. I know that the “there should be one way to do it” probably applies to the Python language syntax and structure, but that still doesn’t make me any less irritated that there isn’t an “anointed” way to read metadata on files. For the love, this is the BIGGEST problem facing our information society over the next hundred years, we need to get a grip on it. Which Microsoft, of all people, seems to have done by integrating Metadata editing into every aspect of the Windows Vista and Windows 7 file manager. Linux needs to catch up. But that deserves another rantpost.


 

 


2 Responses to “Python and EXIF Metadata: There’s more than one way to do it!”

  1. Como modificar a data da criação de uma foto usando a data que a fotografia foi tirada | Blog do Robson Dantas Says:

    [...] script final, utilizei com base em outro que estava na internet, bugado, o qual corrigi e estou publicando aqui para download. Também encaminhei o patch ao [...]

  2. aksel Says:

    I am not going to comment on whether or not there is one or many pythonic ways to do it, but I did find your script tremendously useful after having all my “date modified” changed after reloading data from a USB drive after a hard drive failure. Only needed to catch exceptions for old JPG’s without (proper) exif info.

Leave a Reply