2

I'm making movies by saving a series of images, then using ImageJ to put them together, as so:

for i in range(n):
    fname = 'out_' + str(1000+i)
    plt.imsave(fname, A[i], cmap=cmapA)

I'd like to somehow "concatenate" two images (so they appear side-by-side for example) and save, produced with different colormaps. So hypothetically:

for i in range(n):
    fname = 'out_' + str(1000+i)
    plt.hypothetical_imsave(fname,  (A[i], B[i]),  cmap=(cmapA, cmapB),  axis=1)

Of course, this is pseudocode, but is there some way to do this with good old numpy and matplotlib without installing a whole new package?

uhoh
  • 3,713
  • 6
  • 42
  • 95

2 Answers2

4

Instead of creating the two interim images like my answer above, an alternative way is to make the two arrays using imshow, like in the example below.

We will grab the numpy array from the imshow object using ._rgbacache, but to generate this you have to display the imshow object on an axis, hence the fig and ax at the beginning.

import numpy as np
import matplotlib.pyplot as plt

# Need to display the result of imshow on an axis
fig=plt.figure()
ax=fig.add_subplot(111)

# Save fig a with one cmap
a=np.random.rand(20,20)
figa=ax.imshow(a,cmap='jet')

# Save fig b with a different cmap
b=np.random.rand(20,20)
figb=ax.imshow(a,cmap='copper')

# Have to show the figure to generate the rgbacache
fig.show()

# Get the array of data
figa=figa._rgbacache
figb=figb._rgbacache

# Stitch the two arrays together
figc=np.concatenate((figa,figb),axis=1)

# Save without a cmap, to preserve the ones you saved earlier
plt.imsave('figc.png',figc,cmap=None)

EDIT:

To do this with imsave and a file-like object, you need cStringIO:

import numpy as np
import matplotlib.pyplot as plt
from cStringIO import StringIO

s=StringIO()
t=StringIO()

# Save fig a with one cmap to a StringIO instance. Need to explicitly define format
a=np.random.rand(20,20)
plt.imsave(s,a,cmap='jet',format='png')

# Save fig b with a different cmap
b=np.random.rand(20,20)
plt.imsave(t,b,cmap='copper',format='png')

# Return to beginning of string buffer
s.seek(0)
t.seek(0)

# Get the array of data
figa=plt.imread(s)
figb=plt.imread(t)

# Stitch the two arrays together
figc=np.concatenate((figa,figb),axis=1)

# Save without a cmap, to preserve the ones you saved earlier
plt.imsave('figc.png',figc,cmap=None)
tmdavison
  • 64,360
  • 12
  • 187
  • 165
  • Interesting! This is another solution! Though it's not ideal either, it is very handy if I don't want to deal with so much I/O to the hard drive. Thanks! (but I still wonder if there is a way using the "file-like object" instead of actually writing to the hard drive). – uhoh May 20 '15 at 12:21
  • Ah right, I think I understand now. Maybe you need something like this: http://stackoverflow.com/a/16165168/588071 – tmdavison May 20 '15 at 12:53
  • @uhoh Just added how to do this above – tmdavison May 20 '15 at 13:27
  • don't think cStringIO should need any installation, but if you're happy, I'm happy! – tmdavison May 20 '15 at 13:30
  • 1
    EDITED COMMENT: Oh, I see! Thanks @tom, now you have three distinct solutions! I'm a happy camper. Next frontier: manually generate the image (rgb-alpha) with the cmap and skip all the higher level methods. – uhoh May 20 '15 at 23:18
2

You can use imsave to save both images with the desired cmaps first, then reopen them with imread, concatenate the arrays, and then use imsave again to save the concatenated figure with cmap set to None

import matplotlib.pyplot as plt
import numpy as np

# Save fig a with one cmap
a=np.random.rand(200,200)
plt.imsave('figa.png',a,cmap='jet')

# Save fig b with a different cmap
b=np.random.rand(200,200)
plt.imsave('figb.png',a,cmap='copper')

# Reopen fig a and fig b
figa=plt.imread('figa.png')
figb=plt.imread('figb.png')

# Stitch the two figures together
figc=np.concatenate((figa,figb),axis=1)

# Save without a cmap, to preserve the ones you saved earlier
plt.imsave('figc.png',figc,cmap=None)

Figure a, with jet cmap

figa

Figure b, with copper cmap

figb

Figure c

figc

tmdavison
  • 64,360
  • 12
  • 187
  • 165
  • Sweet! Thanks @tom, it works! But now this has me thinking about how to avoid the hard drive. `help(plt.imsave)` says a "file-like object" can be used instead of a filename. – uhoh May 20 '15 at 11:25
  • There is a way, using imshow instead of imsave. I'll make a new answer with the alternate method – tmdavison May 20 '15 at 12:05