1

Given a set of images, and (x,y) coordinates associated with each image, I'd like to create a "composite" plot of my set of images, each at its (x,y) coordinate.

For instance, given the following set, where each item in the list is a (x, y, image) tuple:

images = [(0,0,'image1.jpg'), (0,1,'image2.jpg'), (1,0,'image3.jpg)]

I'd like to create a plot where the image corresponding to image1.jpg is plotted on an x-y plot at coordinates (0,0), the image corresponding to image2.jpg is plotted at (0, 1), etc...

I had been tackling this with a very manual approach using PIL, where I would do a lot of manual calculation, scaling, and even hand-drawing axes and such to "paste" the composite image together. It works, but my code is a mess, the resulting image isn't too pretty, and it seems like the PIL library has some portability issues.

Is there a way to do this with perhaps Matplotlib? I tried searching through their examples, but none of them were quite what I was looking for, and there's so much you can do with Matplotlib that it makes my head spin.

If anyone has any pointers that might get me started, it would be highly appreciated.

For reference, I'm trying to target Python 2.7, though I'm savvy enough to translate any 3.x code.

Self edit: perhaps this is what I'm looking for:

Placing Custom Images in a Plot Window--as custom data markers or to annotate those markers

EDIT: see accepted answer. For the sake of posterity, here's a basic working example. I also added black borders around the images, which gives it a nice touch:

import matplotlib.pyplot as plt
from matplotlib._png import read_png
from matplotlib.pylab import Rectangle, gca

def main():
    ax = plt.subplot(111)
    ax.set_autoscaley_on(False)
    ax.set_autoscalex_on(False)
    ax.set_ylim([0,10])
    ax.set_xlim([0,10])

    imageData = read_png('image1.png')
    plt.imshow(imageData, extent=[0,2,0,1])
    gca().add_patch(Rectangle((0,0),2, 1, facecolor=(0,0,0,0)))

    imageData = read_png('image2.png')
    plt.imshow(imageData, extent=[2,4,1,2])
    gca().add_patch(Rectangle((2,1),2, 1, facecolor=(0,0,0,0)))

    imageData = read_png('image4.png')
    plt.imshow(imageData, extent=[4,6,2,3])
    gca().add_patch(Rectangle((4,2),2, 1, facecolor=(0,0,0,0)))

    plt.draw()
    plt.savefig('out.png', dpi=300)
Mike Bell
  • 1,356
  • 11
  • 9
  • Are you really looking to use xy coordinates like (0, 1), or is this a simplified example? If that's all you need, you could use `imshow` within a subplot grid. If you need to place them in a more complicated arrangement, it will be tricker. – mwaskom Mar 28 '14 at 19:09
  • Yea that's a simplified example. Essentially I want a "scatterplot" of images. – Mike Bell Mar 28 '14 at 19:18
  • 1
    Have you had a look at this question: http://stackoverflow.com/q/4860417/1025391 ? – moooeeeep Mar 28 '14 at 19:51
  • 2
    You can also use the `extent` kwarg to `imshow` to put it where ever you want in data-space https://stackoverflow.com/questions/12425395/matplotlib-imshow-editing-x-axis/12439944#12439944 – tacaswell Mar 31 '14 at 14:36
  • Ah, THANK YOU, @tcaswell, that's what I wanted. I spent a good chunk of my weekend reading about artists and boxes and other confusing (to me) aspects of Matplotlib. Seems like the confusion between image-space and data-space is an overarching headache in these types of problems, and I didn't see an obvious way to scale annotations to data-space. Your solution is simple and gets me what I want. – Mike Bell Mar 31 '14 at 15:08

1 Answers1

4

To control where an image is shown in data-space use the extent kwarg of imshow which sets the location of the [left, right, bottom, top] edges. Something like:

list_of_corners = [(left0, right0, bottom0, top0), ...]
list_of_images = [im0, im1, ...]
ax, fig = plt.subplots(1, 1)

for extent, img in zip(list_of_corners, list_of_images):
    ax.imshow(img, extent=extent, ...)

should do the trick.

tacaswell
  • 84,579
  • 22
  • 210
  • 199