EXIF data with a pelican-photos Gallery

avatar

Originally posted at jeffmackinnon.com

Share this article on: Twitter Linkedin Email

Way back in the day, when I was using Flickr as one of my daily visits, I really enjoyed learning HOW these amazing photographers made these pictures happen through the exif data. This is where I learned about the affect of aperature, etc. I would copy the settings with my old Canon XSi and grow.

When I added the galleries to this site with the pelican-photos the only way to do this was adding an exif.txt file to each folder. There is a recommended third-party software to do it, but I didn't find it intuitive and gave up.

This weekend however, I had a very little bit of time and decided to see what file metadata I could pull using python.

Using Python to extract EXIF data

I tried the piexif module without much luck, and then found this GIST from @jpstroop that did most of the work for me.

def _map_key(k):
    try:
        return TAGS[k]
    except KeyError:
        return GPSTAGS.get(k, k)

# Creates a dictionary of image EXIF meta data from an image, borrowed from a github gist
def get_exif(image_path):
metadata = {}
with Image.open(image_path) as i:
info = i._getexif()
try:
[ metadata.setitem(_map_key(k), v) for k, v in info.items() ]
return metadata
except AttributeError:
return None

I use the exact fucntions from @jpstroop.

Walking and writing with Python

The next step was to figure out how to walk all the directories in my photo gallery and make sure that I was getting them all, for that I do this:

# The directory of the source photos, the same as PHOTO_LIBRARY in pelicanconf.py
# I run this in a WSL instance, but pelican is run on the host windows machine, hence the sample directory
dir = '/mnt/c/Users/jeff/Pictures'

for root, subdirectories, files in os.walk(dir):
for subdirectory in subdirectories:
print(os.path.join(root, subdirectory))

This is goes through each directory and prints the path. I don't need that for the final product, but I like knowing that something is happening when I run the script, so I kept it.

Creating a text file with everything

Once I made sure that was working the way that I hoped, I wrote a function that will be called for every subdirectory. I called it write_exif_file(directory), this creates the text file and then loops over every .jpg file there and writes the exif data in the way needed for pelican-photos and that I think looks "good". [*]

That function looks like this: [†]

def write_exif_file(directory):
# Open the exif.txt file, create if it doesn't exist, in the current folder.
txt = open(directory+'/exif.txt', 'w')

for file in os.listdir(directory):

<span class="c1"># check the files which are end with specific extension</span>
<span class="c1"># Everything that I want to have EXIF information for is a jpg, but you can add others too.</span>
<span class="k">if</span> <span class="n">file</span><span class="o">.</span><span class="n">endswith</span><span class="p">(</span><span class="s2">".jpg"</span><span class="p">):</span>
    <span class="c1"># Need to iterate through the files in the list and pass each to the</span>
    <span class="n">image</span> <span class="o">=</span> <span class="n">get_exif</span><span class="p">(</span><span class="n">directory</span> <span class="o">+</span><span class="s1">'/'</span><span class="o">+</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">file</span><span class="p">))</span>
    <span class="n">txt</span><span class="o">.</span><span class="n">write</span><span class="p">(</span>
        <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">file</span><span class="p">)</span> <span class="o">+</span> <span class="s1">': '</span> <span class="o">+</span>
        <span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'Model'</span><span class="p">))</span> <span class="o">+</span>
        <span class="s1">' with a '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'LensModel'</span><span class="p">))</span> <span class="o">+</span>
        <span class="s1">' - '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'ExposureTime'</span><span class="p">))</span> <span class="o">+</span> <span class="s1">'s, '</span> <span class="o">+</span> <span class="c1"># I want to change this to fractions, but it works for now.</span>
        <span class="s1">'f/'</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'FNumber'</span><span class="p">))</span> <span class="o">+</span>
        <span class="s1">' at ISO-'</span> <span class="o">+</span>  <span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">'ISOSpeedRatings'</span><span class="p">))</span> <span class="o">+</span>
        <span class="s1">'</span><span class="se">\n</span><span class="s1">'</span>
        <span class="p">)</span>

# Close the text file now that we are done with it.
txt.close()
return None

For now I'm only including:

  • Model,
  • Lens,
  • Exposure,
  • F-Stop, and
  • ISO

I may add GPS to them in the future, but as for the settings needed to replicate these shots with your own camera, I think this is enough.

Finding the code snippet

I created a GIST for this code snippet here - pelican-photos-create-exif-files. Until this weekend I didn't even know that GIST was a site. As I go through creating these little scripts for myself I am way more likely to neeed a function, or code-snippet than an entire repo. I will probably be adding some more here as I go along, as a backup storage if nothing else.

If you have any suggestions on how to make this "slicker". If you think its useful and want to copy/paste it as a function in the plugin be my guest. I don't have the bandwidth right now to figure out how to do a proper pull-request for the plugin, or the expereince to know how to update the code in a way that won't break everything else.

Footnotes

[*]There is still some work to do there, especially for the exposure time. I want it to be fractional for less than a second, but its "good enough" for this iteration.
[†]If anyone knows CSS and wants to help me add syntac highlighting to this theme, let me know.

Originally posted at jeffmackinnon.com



3 comments
avatar

Thank you for your witness vote!
Have a !BEER on me!
To Opt-Out of my witness beer program just comment !STOP below

avatar

Congratulations @jeffmackinnon! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You distributed more than 38000 upvotes.
Your next target is to reach 39000 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

Check out the last post from @hivebuzz:

Hive Power Up Day - April 1st 2022
Support the HiveBuzz project. Vote for our proposal!
0
0
0.000