Replacing Exif data with Python

A while back I had the sensibility to download all my pictures off Facebook to a local folder. In fact, I wrote a Chrome extension to do that because I couldn’t find that functionality in Facebook’s farcical interface. I’m kind of surprised I never hit a Facebook spamfilter while downloading a solid 550 pictures through essentially a bot-extension, but I guess they were too busy finding alternative ways to give Cambridge Analytica access to all their data back then.


Anyways

Most of those pictures had gotten stripped of all their Exif data along the way. I had been smart enough to extract the upload date of the picture, and name the file after it, but Synology Moments doesn’t use the name of the file to set its date. It parses the Exif data only, and if that doesn’t exist, it will just use the day the files were created. Result: I had 550 pictures taken on a rainy October day in 2016. That can’t be right.


Scripting it up

As luck would have it, all of these images got uploaded to the same directory in the ~/Drive/Moments/Drive/2016-10-05/ folder on my Synology. That means I had to do three things:

  • Mount the Synology directory over NFS on my laptop
  • Slap together a Python script that parses filenames to a date and writes it to the Exif data
  • Run it on the mounted directory

Python function

I won’t go through the details of mounting an NFS drive. Actually, I’m not going to go through the details of the rest either, you should be able to figure that out. The long and short of it: my function relies on piexif and PIL. The rest should be self-explanatory: loop through the files in the directory and call the function. This is all in python3 though I’m fairly certain it would work with minor adjustments in python2 as well.

def replace_exif(file, datetime_object):
	""" 
	Replaces the createdTime Exif data of image with date parsed from the datetime_object

	This function replaces the following three properties of the Exif data of 
	the image file that is passed to it:
		Exif.Image.DateTime, at offset 306
		Exif.Photo.DateTimeOriginal at offset 36867
		Exif.Photo.DateTimeDigitized at offset 36868
	This function expects that both PIL and piexif are imported

	For more information about Exif and offsets, see http://www.exiv2.org/tags.html

	Parameters
	----------
	file : string
		Absolute or relative path to the relevant image file of which the Exif data should 
		be changed
	datetime_object : datetime object
		A datetime object generated by the standard datetime module which 
		contains the date to which the image's Exif data should be changed
	
	"""

	img = Image.open(file)

	try:
		# If the image already contains data, we only replace the relevant properties
		exif_dict = piexif.load(img.info['exif'])
		print(f"Exif load for file '{file}'' successful")
	except KeyError:
		# If the image has no Exif data, we create the relevant properties
		print(f"No Exif data for file '{file}', creating Exif data instead...")
		exif_dict = {}
		exif_dict["0th"] = {}
		exif_dict["Exif"] = {}

	# We now have a useful Exif dict, time to adjust the values
	d = datetime_object.strftime("%Y:%m:%d %H:%M:%S")
	exif_dict["Exif"][36868] = d.encode("utf-8")
	exif_dict["Exif"][36867] = d.encode("utf-8")
	exif_dict["0th"][306] = d.encode("utf-8")

	# Convert into bytes and dump into file
	exif_bytes = piexif.dump(exif_dict)
	piexif.insert(exif_bytes, file)

	print(f"Exif data replacement for file '{file}' successful")