## [Originally posted at jeffmackinnon.com](https://jeffmackinnon.com/getting-exif-data-in-the-galley-with-pelican-photos.html)
<p class="share">Share this article on:
<span><a class="" href="https://twitter.com/intent/tweet?text=EXIF data with a pelican-photos Gallery&url=https://jeffmackinnon.com/getting-exif-data-in-the-galley-with-pelican-photos.html&via=jeffmackinnon" target="_blank" title="Share on Twitter"><i class="fab fa-twitter"></i> Twitter</a></span>
<span><a class="" href="https://www.linkedin.com/sharing/share-offsite/?url=https://jeffmackinnon.com/getting-exif-data-in-the-galley-with-pelican-photos.html" target="_blank" title="Share via Linkedin"><i class="icon brands fa-linkedin"></i> Linkedin</a></span>
<span><a class="" href="mailto:?subject=EXIF data with a pelican-photos Gallery&body=Way back in the day, when I was using Flickr as one of my daily visits, I really enjoyed learning … - https://jeffmackinnon.com/getting-exif-data-in-the-galley-with-pelican-photos.html" target="_blank" title="Share via Email"><i class="fas fa-envelope"></i> Email</a></span>
</p>
<img src="https://jeffmackinnon.com/photos\thefog/screenshot2022-03-07-090425a.jpg" style="max-width:100%; display: block; margin: 0px auto;"/>
</header>
<div class="entry-content">
<p>Way back in the day, when I was using <a class="reference external" href="https://www.flickr.com/photos/jeffmackinnon/">Flickr</a> 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.</p>
<p>When I added the galleries to this site with the <a class="reference external" href="https://github.com/pelican-plugins/photos">pelican-photos</a> 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.</p>
<p>This weekend however, I had a very little bit of time and decided to see what file metadata I could pull using python.</p>
<div class="section" id="using-python-to-extract-exif-data">
<h2>Using Python to extract EXIF data</h2>
<p>I tried the <tt class="docutils literal">piexif</tt> module without much luck, and then found this <a class="reference external" href="https://gist.github.com/jpstroop/58a21d02370c8ba34dc8f0fdd4206d70">GIST</a> from <a class="reference external" href="https://gist.github.com/jpstroop/">@jpstroop</a> that did most of the work for me.</p>
<pre class="code python literal-block">
<span class="k">def</span> <span class="nf">_map_key</span><span class="p">(</span><span class="n">k</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="n">TAGS</span><span class="p">[</span><span class="n">k</span><span class="p">]</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="k">return</span> <span class="n">GPSTAGS</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
<span class="c1"># Creates a dictionary of image EXIF meta data from an image, borrowed from a github gist</span>
<span class="k">def</span> <span class="nf">get_exif</span><span class="p">(</span><span class="n">image_path</span><span class="p">):</span>
<span class="n">metadata</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">with</span> <span class="n">Image</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">image_path</span><span class="p">)</span> <span class="k">as</span> <span class="n">i</span><span class="p">:</span>
<span class="n">info</span> <span class="o">=</span> <span class="n">i</span><span class="o">.</span><span class="n">_getexif</span><span class="p">()</span>
<span class="k">try</span><span class="p">:</span>
<span class="p">[</span> <span class="n">metadata</span><span class="o">.</span><span class="fm">__setitem__</span><span class="p">(</span><span class="n">_map_key</span><span class="p">(</span><span class="n">k</span><span class="p">),</span> <span class="n">v</span><span class="p">)</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">info</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="p">]</span>
<span class="k">return</span> <span class="n">metadata</span>
<span class="k">except</span> <span class="ne">AttributeError</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">None</span>
</pre>
<p>I use the exact fucntions from @jpstroop.</p>
</div>
<div class="section" id="walking-and-writing-with-python">
<h2>Walking and writing with Python</h2>
<p>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:</p>
<pre class="code python literal-block">
<span class="c1"># The directory of the source photos, the same as PHOTO_LIBRARY in pelicanconf.py</span>
<span class="c1"># I run this in a WSL instance, but pelican is run on the host windows machine, hence the sample directory</span>
<span class="nb">dir</span> <span class="o">=</span> <span class="s1">'/mnt/c/Users/jeff/Pictures'</span>
<span class="k">for</span> <span class="n">root</span><span class="p">,</span> <span class="n">subdirectories</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="nb">dir</span><span class="p">):</span>
<span class="k">for</span> <span class="n">subdirectory</span> <span class="ow">in</span> <span class="n">subdirectories</span><span class="p">:</span>
<span class="nb">print</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">root</span><span class="p">,</span> <span class="n">subdirectory</span><span class="p">))</span>
</pre>
<p>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.</p>
</div>
<div class="section" id="creating-a-text-file-with-everything">
<h2>Creating a text file with everything</h2>
<p>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 <tt class="docutils literal">write_exif_file(directory)</tt>, this creates the text file and then loops over every .jpg file there and writes the exif data in the way needed for <tt class="docutils literal"><span class="pre">pelican-photos</span></tt> and that I think looks "good". <a class="footnote-reference" href="#footnote-1" id="footnote-reference-1">[*]</a></p>
<p>That function looks like this: <a class="footnote-reference" href="#footnote-2" id="footnote-reference-2">[†]</a></p>
<pre class="code python literal-block">
<span class="k">def</span> <span class="nf">write_exif_file</span><span class="p">(</span><span class="n">directory</span><span class="p">):</span>
<span class="c1"># Open the exif.txt file, create if it doesn't exist, in the current folder.</span>
<span class="n">txt</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">directory</span><span class="o">+</span><span class="s1">'/exif.txt'</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">directory</span><span class="p">):</span>
<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>
<span class="c1"># Close the text file now that we are done with it.</span>
<span class="n">txt</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">return</span> <span class="kc">None</span>
</pre>
<p>For now I'm only including:</p>
<ul class="simple">
<li>Model,</li>
<li>Lens,</li>
<li>Exposure,</li>
<li>F-Stop, and</li>
<li>ISO</li>
</ul>
<p>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.</p>
</div>
<div class="section" id="finding-the-code-snippet">
<h2>Finding the code snippet</h2>
<p>I created a <a class="reference external" href="https://gist.github.com/Jeffmackinnon">GIST</a> for this code snippet here - <a class="reference external" href="https://gist.github.com/Jeffmackinnon/92c747b30a873129a28cddacfc61fbe1">pelican-photos-create-exif-files</a>. 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.</p>
<p>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.</p>
<p class="rubric">Footnotes</p>
<table class="docutils footnote" frame="void" id="footnote-1" rules="none">
<colgroup><col class="label"/><col/></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-1">[*]</a></td><td>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.</td></tr>
</tbody>
</table>
<table class="docutils footnote" frame="void" id="footnote-2" rules="none">
<colgroup><col class="label"/><col/></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#footnote-reference-2">[†]</a></td><td>If anyone knows CSS and wants to help me add syntac highlighting to this theme, let me know.</td></tr>
</tbody>
</table>
</div>
## [Originally posted at jeffmackinnon.com](https://jeffmackinnon.com/getting-exif-data-in-the-galley-with-pelican-photos.html)