A Python script for displaying EXIF metadata from JPEG, TIF, PNG, GIF, BMP, CR2 and NEF files. More information at https://code.adonline.id.au/reading-exif-data-in-python/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 https://gist.github.com/SamWhited/af58edaed66414bded84 | |
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) | |
else: | |
print(level * "\t" + name + ":") | |
raw_metadata(raw, ifd.subifds[name], level + 1) | |
else: | |
if isinstance(name, str): | |
if e.tag_type_key is 0x07: | |
print(level * "\t" + "{}: {}".format( | |
name, | |
"[Binary blob]" | |
)) | |
else: | |
print(level * "\t" + "{}: {}".format( | |
name, | |
ifd.get_value(e) | |
)) | |
def gen_metadata(image): | |
# Read image into imageio for data type | |
pic = imageio.imread(image) | |
# Read image into PIL to extract basic metadata | |
type = Image.open(image) | |
# 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("\n--Summary--\n") | |
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 | |
print("\n--Metadata--\n") | |
# For non-PNGs | |
if type.format != "PNG": | |
# Compile array from tags dict | |
for i in tags: | |
compile = i, str(tags[i]) | |
exif_array.append(compile) | |
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 https://stackoverflow.com/a/58399815 | |
metadata = PngInfo() | |
# Compile array from tags dict | |
for i in image.text: | |
compile = i, str(image.text[i]) | |
exif_array.append(compile) | |
# If XML metadata, pull out data by idenifying data type and gathering useful meta | |
if len(exif_array) > 0: | |
header = exif_array[0][0] | |
else: | |
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(' ') | |
print(xml_line) | |
elif header.startswith("Software"): | |
print("No available metadata") | |
# If no XML, print available metadata | |
else: | |
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.") | |
exit() | |
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) | |
else: | |
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) | |
raw.close() | |
else: | |
gen_metadata(image) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi Adams, Thank you.
I've learned a lot with this example. I'm making a python program that wil read my entire library, searching for all ny Favorites images (those with EXIF tag Rating: 5), and mopy (or link) them on a another directory.
But I have a lot of RAW (Nikon NEF) images . I tried this funcions of yours, it lists the Metadata of the files, but cannot find the EXIF data of it in the XMP section that stores the Rating.
If I run exiftool on the image, I get (among others):
TIFF-EP Standard ID : 1 0 0 0 Jpg From Raw : (Binary data 815067 bytes, use -b option to extract) Thumbnail TIFF : (Binary data 57816 bytes, use -b option to extract) ---- XMP ---- XMP Toolkit : XMP Core 4.4.0-Exiv2 Rating : 5 Rating Percent : 99 ---- MakerNotes ---- Maker Note Version : 2.10 Quality : RAW White Balance : Auto Focus Mode : AF-A
But these funcions show only the sections:
exif , raw_data and preview_image
Do you know how can I get the other information ?
Thanks.