title: Python: Extract Metadata from PNG
body: <p>I am able to extract the necessary information using R, but for consistency within the overall project, I would like to be able to do it with Python (preferably Python3).  I need the contents of a single tag called "Settings".  This tag contains XML which will then need to be parsed.  </p>
  
  <p>Getting the metadata in R is incredibly easy:</p>
  
  <pre><code>library(exifr)
  library(XML)
  
  path = file.path('path', 'to', 'file')
  
  x = read_exif(file.path(path,'image.png'))
  x$Settings
  </code></pre>
  
  <p>It doesn't look like Python can do it, which boggles my mind.  Or doing so requires me to have far more knowledge of Python and PNGs than I have at the moment.  How can I extract PNG metadata using Python?</p>
  
  <hr>
  
  <h2>Here's the list of things I've tried:</h2>
  
  <p><strong>PyPng</strong>
  PyPNG seems promising.  Examining the length of each chunk, it seems likely the "Settings" tag lives in the zTXt chunk.  </p>
  
  <pre><code>import png
  
  filename = "C:\\path\\to\\image.png"
  
  im = png.Reader(filename)
  
  for c in im.chunks():
      print(c[0], len(c[1]))
  
  &gt;&gt;&gt;
  IHDR 13
  tIME 7
  pHYs 9
  IDAT 47775
  zTXt 714
  IEND 0
  </code></pre>
  
  <p>The above was taken from <a href="https://stackoverflow.com/questions/37068414/how-to-modify-a-compressed-itxt-record-of-an-existing-file-in-python/37099807#37099807">this post</a>.  However, it's still unclear how to extract the zTXt data.</p>
  
  <p><strong>hachoir3</strong></p>
  
  <p>Using the <a href="https://hachoir3.readthedocs.io/" rel="noreferrer"><code>hachoir3</code></a> package, I tried the following:</p>
  
  <pre><code>from hachoir.parser import createParser
  from hachoir.metadata import extractMetadata
  
  filename = "C:\\path\\to\\file\\image.png"
  parser = createParser(filename)
  metadata = extractMetadata(parser)
  
  for line in metadata.exportPlaintext():
      print(line)
  </code></pre>
  
  <p>This gives me the following:</p>
  
  <pre><code>Metadata:
  - Image width: 1024 pixels
  - Image height: 46 pixels
  - Bits/pixel: 16
  - Pixel format: RGB
  - Compression rate: 2.0x
  - Image DPI width: 1 DPI
  - Image DPI height: 1 DPI
  - Creation date: 2016-07-13 19:09:28
  - Compression: deflate
  - MIME type: image/png
  - Endianness: Big endian
  </code></pre>
  
  <p>I can't seem to get at the field I need, the "Settings" one referenced in the R code.  I've had no luck with other methods, such as <code>metadata.get</code>.  As far as I can tell, those seem to be the two options for parsing PNG metadata.  The docs read,</p>
  
  <blockquote>
    <p>Some good (but not perfect ;-)) parsers:</p>
    
    <p>Matroska video Microsoft RIFF (AVI video, WAV audio, CDA file) PNG
    picture TAR and ZIP archive</p>
  </blockquote>
  
  <p>Maybe it just doesn't have the functionality I need?  </p>
  
  <p><strong>Pillow</strong></p>
  
  <p>Following the advice given in this <a href="https://stackoverflow.com/questions/24983135/metadata-extraction-from-png-images">post</a>:</p>
  
  <pre><code>from PIL import Image
  filename = "C:\\path\\to\\file\\image.png"
  im = Image.open(filename)
  </code></pre>
  
  <p>This reads in the image, but <code>im.info</code> only returns <code>{'aspect': (1, 1)}</code>.  Reading through the documentation, it doesn't look like any of the methods get at the metadata. I read through the <a href="http://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files" rel="noreferrer">PNG description</a> provided in the post.  Honestly, I don't know how to make use of its information nor how Pillow would facilitate me.</p>
  
  <p>There are some posts which imply that what I need can be done, but they do not work.  For example, <a href="https://stackoverflow.com/questions/21697645/how-to-extract-metadata-from-a-image-using-python">this post</a> suggests using the ExifTags library:</p>
  
  <pre><code>from PIL import Image, ExifTags
  filename = "C:\\path\\to\\file\\image.png"
  im = Image.open(filename)
  exif = { ExifTags.TAGS[k]: v for k, v in im._getexif().items() if k in ExifTags.TAGS}
  </code></pre>
  
  <p>The problem is, <code>AttributeError: 'PngImageFile' object has no attribute '_getexif'</code>.  According to the <a href="https://pillow.readthedocs.io/en/latest/releasenotes/3.1.0.html?highlight=_getexif" rel="noreferrer">documentation</a>, the <code>._getexif</code> feature is experimental and only applies to JPGs.</p>
  
  <p>Reading through the overall <a href="https://pillow.readthedocs.io/en/latest/handbook/tutorial.html" rel="noreferrer">Pillow documentation</a>, it really only talks about JPG and TIFF.  Processing PNG files doesn't seem to be part of the package at all.  So like <code>hachoir</code>, maybe it can't be done?</p>
  
  <p><strong>PIL</strong></p>
  
  <p>There's apparently another package PIL from which Pillow was forked.  It looks like it was abandoned in 2009.  </p>