Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
A Python script for displaying EXIF metadata from JPEG, TIF, PNG, GIF, BMP, CR2 and NEF files. More information at
#!/usr/bin/env python
import imageio
import exifread
from PIL import Image, ExifTags
from PIL.ExifTags import TAGS
from PIL.PngImagePlugin import PngImageFile, PngInfo
import re
import os
from rawphoto.cr2 import Cr2
from rawphoto.nef import Nef
import argparse
def options():
parser = argparse.ArgumentParser(description="Read image metadata")
parser.add_argument("-i", "--image", help="Input image file.", required=True)
args = parser.parse_args()
return args
# Via
def raw_metadata(raw, ifd, level=1):
for name in ifd.entries:
e = ifd.entries[name]
if name in ifd.subifds or isinstance(name, tuple):
if isinstance(name, tuple):
for n in name:
print(level * "\t" + n + ":")
raw_metadata(raw, ifd.subifds[n], level + 1)
print(level * "\t" + name + ":")
raw_metadata(raw, ifd.subifds[name], level + 1)
if isinstance(name, str):
if e.tag_type_key is 0x07:
print(level * "\t" + "{}: {}".format(
"[Binary blob]"
print(level * "\t" + "{}: {}".format(
def gen_metadata(image):
# Read image into imageio for data type
pic = imageio.imread(image)
# Read image into PIL to extract basic metadata
type =
# Calculations
megapixels = (type.size[0]*type.size[1]/1000000) # Megapixels
d = re.sub(r'[a-z]', '', str(pic.dtype)) # Dtype
t = len(Image.Image.getbands(type)) # Number of channels
print("Filename: ",type.filename)
print("Format: ", type.format)
print("Data Type:", pic.dtype)
print("Bit Depth (per Channel):", d)
print("Bit Depth (per Pixel): ", int(d)*int(t))
print("Number of Channels: ", t)
print("Mode: ",type.mode)
print("Palette: ",type.palette)
print("Width: ", type.size[0])
print("Height: ", type.size[1])
print("Megapixels: ",megapixels)
# Open image with ExifMode to collect EXIF data
exif_tags = open(image, 'rb')
tags = exifread.process_file(exif_tags)
# Create an empty array
exif_array = []
# Print header
# For non-PNGs
if type.format != "PNG":
# Compile array from tags dict
for i in tags:
compile = i, str(tags[i])
for properties in exif_array:
if properties[0] != 'JPEGThumbnail':
print(': '.join(str(x) for x in properties))
if type.format == "PNG":
image = PngImageFile(image) #via
metadata = PngInfo()
# Compile array from tags dict
for i in image.text:
compile = i, str(image.text[i])
# If XML metadata, pull out data by idenifying data type and gathering useful meta
if len(exif_array) > 0:
header = exif_array[0][0]
header = ""
print("No available metadata")
xml_output = []
if header.startswith("XML"):
xml = exif_array[0][1]
xml_output.extend(xml.splitlines()) # Use splitlines so that you have a list containing each line
# Remove useless meta tags
for line in xml.splitlines():
if "<" not in line:
if "xmlns" not in line:
# Remove equal signs, quotation marks, /> characters and leading spaces
xml_line = re.sub(r'[a-z]*:', '', line).replace('="', ': ')
xml_line = xml_line.rstrip(' />')
xml_line = xml_line.rstrip('\"')
xml_line = xml_line.lstrip(' ')
elif header.startswith("Software"):
print("No available metadata")
# If no XML, print available metadata
for properties in exif_array:
if properties[0] != 'JPEGThumbnail':
print(': '.join(str(x) for x in properties))
# Explanation for GIF or BMP
if type.format == "GIF" or type.format == "BMP":
print("No available metadata")
def main():
# Get options
args = options()
image = args.image
# Check for RAW images
name, extension = os.path.splitext(image)
# List valid extensions
ext = [".png", ".jpg", ".jpeg", ".cr2", ".nef", ".tif", ".bmp"]
if extension not in ext:
print("File format ",extension," not supported.")
if extension == ".CR2":
metadata = {}
filepath = image
(filepath_no_ext, ext) = os.path.splitext(filepath)
filename_no_ext = os.path.basename(filepath_no_ext)
ext = ext.upper()
if ext == '.CR2':
raw = Cr2(filename=filepath)
elif ext == '.NEF':
raw = Nef(filename=filepath)
raise TypeError("Format not supported")
for i in range(len(raw.ifds)):
ifd = raw.ifds[i]
print("IFD #{}".format(i))
raw_metadata(raw, ifd)
# Hax.
for subifd in ifd.subifds:
if isinstance(subifd, int):
print("Subifd ", subifd)
raw_metadata(raw, ifd.subifds[subifd], 1)
if __name__ == '__main__':
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment