33

I want to be able to ascertain the provenance of the figures I create using matplotlib, i.e. to know which version of my code and data created these figures. (See this essay for more on provenance.)

I imagine the most straightforward approach would be to add the revision numbers of the code and data to the metadata of the saved figures, or as comments in a postscript file for example.

Is there any easy way to do this in Matplotlib? The savefig function doesn't seem to be capable of this but has someone come up with a workable solution?

ihuston
  • 893
  • 6
  • 10
  • 1
    Just add some text to the plot... – Fredrik Pihl May 10 '12 at 11:03
  • 4
    That might be straightforward but I don't want to have to submit figures for publication with "commit 5d3414b19986fe3c08df4088d87b8786a660c387" written underneath. – ihuston May 10 '12 at 11:06
  • Then hide it using [Steganography](http://en.wikipedia.org/wiki/Steganography). Sorry for stupid suggestions but I'm not aware of any support for this in matplotlib. What I'm suggesting is something like adding a pixelvalue in position (0,0) that differs from background with a value you can correlate with the revision... – Fredrik Pihl May 10 '12 at 11:29
  • You could look at putting it in EXIF data? I guess you don't want to use JPEGs, but apparently TIFF supports EXIF as well. – Thomas K May 10 '12 at 11:40
  • 1
    I mainly use PDFs or EPS, but I did think EXIF would be a good approach for the others. I might look at writing a wrapper for savefig that adds a string to EXIF for JPEGs, a comment to an EPS file or adds metadata to a PDF. I was interested in whether anyone had already tried to do this. – ihuston May 10 '12 at 12:37
  • 3
    EPS files are just text files, with lines beginning with % being a comment. So it would be easy to add a few lines yourself. PDFs are compressed EPS (more or less) so above should work too, best done with some PDF library. (I salute your efforts to track provenance. I've been doing it for model runs but not for figures so far, may start now.) – Mauro May 17 '12 at 04:43
  • 1
    Did you ever get around of writing such a wrapper? I'd be interested. Alternative would be to write a wrapper that simply stores a text-file next to every stored figure. – gerrit Feb 13 '13 at 20:26

4 Answers4

20

I don't know of a way using matplotlib, but you can add metadata to png's with PIL:

f = "test.png"
METADATA = {"version":"1.0", "OP":"ihuston"}

# Create a sample image
import pylab as plt
import numpy as np
X = np.random.random((50,50))
plt.imshow(X)
plt.savefig(f)

# Use PIL to save some image metadata
from PIL import Image
from PIL import PngImagePlugin

im = Image.open(f)
meta = PngImagePlugin.PngInfo()

for x in METADATA:
    meta.add_text(x, METADATA[x])
im.save(f, "png", pnginfo=meta)

im2 = Image.open(f)
print im2.info

This gives:

{'version': '1.0', 'OP': 'ihuston'}
Hooked
  • 84,485
  • 43
  • 192
  • 261
  • I'm going to accept this answer for the time being, given that there seems to be no way of adding metadata in matplotlib in a format agnostic manner. – ihuston May 28 '12 at 13:59
11

If you are interested in PDF files, then you can have a look at the matplotlib module matplotlib.backends.backend_pdf. At this link there is a nice example of its usage, which could be "condensed" into the following:

import pylab as pl
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages

pdffig = PdfPages('figure.pdf')

x=np.arange(10)

pl.plot(x)
pl.savefig(pdffig, format="pdf")

metadata = pdffig.infodict()
metadata['Title'] = 'Example'
metadata['Author'] = 'Pluto'
metadata['Subject'] = 'How to add metadata to a PDF file within matplotlib'
metadata['Keywords'] = 'PdfPages example'

pdffig.close()
Alessandro
  • 371
  • 3
  • 11
9

As of matplotlib version 2.1.0, the savefig command accepts the keyword argument metadata. You pass in a dictionary with string key/value pairs to be saved.

This only fully works with the 'agg' backend for PNG files.

For PDF and PS files you can use a pre-defined list of tags.

JoeZuntz
  • 1,092
  • 10
  • 16
3

If you are generating SVG files, you can simply append text as an XML comment at the end of the SVG file. Editors like Inkscape appear to preserve this text, even if you subsequently edit an image.

Here's an example, based on the answer from Hooked:

import pylab as plt
import numpy as np

f = "figure.svg"
X = np.random.random((50,50))
plt.imshow(X)
plt.savefig(f)

open(f, 'a').write("<!-- Here is some invisible metadata. -->\n")
Mark
  • 3,357
  • 30
  • 31
  • Btw, this metadata can be accessed in JavaScript like this: document.getElementsByTagName('svg')[0].nextSibling – MrTomRod Mar 17 '22 at 13:39