ActiveState Code

Recipe 550811: JPG files redater by EXIF data


Iterates through a directory, reading the EXIF data from each jpg. Parses the date/time from EXIF data and if it differs from file modification date/time then changes file date/time.

Python
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#!/usr/bin/python

__version__ = '$Id: exif_redater.py 470 2008-03-06 06:40:14Z mn $'

USAGE = "exif_redater.py\n\tredate .jpg/.jpeg files according to EXIF data"

"""
Iterates through a directory, reading the EXIF data from each jpg/jpeg file.
Parses the date/time from EXIF data and if it differs from file
modification date/time then changes file date/time

author: Michal Niklas
"""

import os
import os.path
import sys
import time

import EXIF

ALL_CNT = 0
CHANGED_CNT = 0

# which file should be checked
EXTENSIONS = ('.jpg', '.jpeg')

# what tags use to redate file (use first found)
DT_TAGS = ["Image DateTime", "EXIF DateTimeOriginal", "DateTime"]


def show_fdt(fdt):
	"""human readable format of file modification datetime"""
	return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(fdt))


def exif_info2time(ts):
	"""changes EXIF date ('2005:10:20 23:22:28') to number of seconds since 1970-01-01"""
	tpl = time.strptime(ts, '%Y:%m:%d %H:%M:%S')
	return time.mktime(tpl)


def redate_by_exif(fn):
	"""reads EXIF from jpg/jpeg and if file datetime differs from EXIF changes file date"""
	global ALL_CNT, CHANGED_CNT
	ALL_CNT += 1
	dt_value = None
	exif_time = None
	s = os.stat(fn)
	f = open(fn, 'rb')
	try:
		tags = EXIF.process_file(f)
		for dt_tag in DT_TAGS:
			try:
				dt_value = "%s" % tags[dt_tag]
				break
			except:
				continue
		if dt_value:
			file_time = s[8]
			exif_time = exif_info2time(dt_value)
	finally:
		f.close()
	if exif_time:
		secs_diff = file_time - exif_time
		MAX_DIFF = 130.0
		if secs_diff > MAX_DIFF or secs_diff < -MAX_DIFF:
			print "%s    %s -> %s" % (fn, show_fdt(file_time), show_fdt(exif_time))
			os.utime(fn, (exif_time, exif_time))
			CHANGED_CNT += 1


def process_dir(_, dir_name, files):
	"""looks for .jpg/.jpeg file in dir"""
	for fname in files:
		can_change = False
		fnl = fname.lower()
		for ext in EXTENSIONS:
			if fnl.endswith(ext):
				can_change = True
				break
		if can_change:
			fname = os.path.join(dir_name, fname)
			if os.path.isfile(fname):
				try:
					redate_by_exif(fname)
				except:
					pass


def main():
	os.path.walk('.', process_dir, None)
	print '\nChecked: %d\nChanged %d\n' % (ALL_CNT, CHANGED_CNT)


if '--version' in sys.argv:
	print __version__
else:
	if __name__ == '__main__':
		if '--help' in sys.argv:
			print USAGE
		else:
			main()

Discussion

Needs EXIF.py (from sourceforge, tested with VERSION 1.0.7)

Chnaged posixpath to os.path. Thanks Frederic Mantegazza :)

I also extended EXIF tags to look for. My Canon writes "Image DateTime" while hp photosmart 120 does not, but hp writes "EXIF DateTimeOriginal". You should check what tags you camera writes.

Comments

  1. 1. At 2:29 a.m. on 4 mar 2008, Frederic Mantegazza said:

    posixpath import. Is there any reason to directly import the posixpath module, instead of os.path? I think this recipe works on all plateformes...

  2. 2. At 10:17 p.m. on 4 mar 2008, Michal Niklas (the author) said:

    Changed. Changed posixpath to os.path. Thanks :)

  3. 3. At 10:34 a.m. on 5 mar 2008, Jean Brouwers said:

    Use os.path.join(). Instead of

    fname = dir_name + '/' + fname
    

    you may want to use

    fname = os.path.join(dir_name, fname)
    

    for more platform indepedent.

  4. 4. At 11:02 p.m. on 5 mar 2008, Michal Niklas (the author) said:

    OK. Changed.

  5. 5. At 7:33 a.m. on 18 feb 2009, Denis Barmenkov said:

    Python Image Library (PIL) extracts EXIF date in smaller amount of code:

    Sample: http://code.activestate.com/recipes/576646/

Sign in to comment