How to retrieve the raw figure data from matplotlib? - python

I am using matplotlib to generate matrices I can train on. I need to get to the raw figure data.
Saving and reading the .png works fine, but my code runs 10x longer. Another stack overflow asked a similar question and the solution was to grab the canvas, but that related logic generated a numpy error. Here is my mwe.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import IdentityTransform
px = 1/plt.rcParams['figure.dpi'] # pixel in inches
fig, ax = plt.subplots(figsize=(384*px, 128*px))
i = 756
plt.text(70, 95, "value {:04d}".format(i), color="black", fontsize=30, transform=IdentityTransform())
plt.axis('off')
plt.savefig("xrtv.png") # I dont want to do this ...
rtv = plt.imread("xrtv.png") # or this, but I want access to what imread returns.
gray = lambda rgb: np.dot(rgb[..., :3], [0.299, 0.587, 0.114])
gray = gray(rtv)

Disabling rendering was a good hint. Consider using a memory buffer to I/O rather than playing with strings. Here is a full example:
import numpy as np
import io
import matplotlib.pyplot as plt
from PIL import Image
# disable rendering and dump to buffer
plt.ioff()
fig,ax = plt.subplots()
ax.plot(np.sin(np.arange(100)))
buf = io.BytesIO()
fig.savefig(buf,format='RGBA')
# plot from buffer
shape = (int(fig.bbox.bounds[-1]),int(fig.bbox.bounds[-2]),-1)
img_array = np.frombuffer(buf.getvalue(),dtype=np.uint8).reshape(shape)
Image.fromarray(img_array)

Related

How to fix BytesIO numpy image array returning blank?

I'm trying to save a Matplotlib plot to an array using BytesIO as suggested here: Matplotlib save plot to NumPy array. Here is my code
import lightkurve
import matplotlib.pyplot as plt
import numpy as np
import io
def download(search):
lc = search.download() # downloads lightcurve as lightcurve object
if lc is not None:
fig,ax = plt.subplots()
ax.scatter(lc.time.value.tolist(), lc.flux.value.tolist(), color='k')
ax.autoscale()
ax.set_xlabel('Time (BTJD)')
ax.set_ylabel('Flux')
fig.show()
io_buf = io.BytesIO()
fig.savefig(io_buf,format="raw")
io_buf.seek(0)
img_arr = np.frombuffer(io_buf.getvalue(),dtype=np.uint8)
io_buf.close()
return img_arr
For some reason, the returned image array only contains the repeated value 255 like so: [255 255 255 ... 255 255 255] suggesting a blank image. I've tried using plt instead of fig, autoscaling the axes in case they weren't showing, and plotting instead with the Lightkurve built-in plotting function lc.plot(ax=ax) but nothing has changed. Does anyone know how to fix this?
I couldn't reproduce your bug. In fact, I ran your code (with some modifications) and the resulting image was exactly like the original image. Did you thoroughly check if your img_arr had only 255s? (e.g., np.unique(img_arr), in my case, len(np.unique(imgarr)) == 231)
import lightkurve
import matplotlib.pyplot as plt
import numpy as np
import io
def download(search):
lc = search.download() # downloads lightcurve as lightcurve object
if lc is not None:
fig,ax = plt.subplots()
ax.scatter(lc.time.value.tolist(), lc.flux.value.tolist(), color='k')
ax.autoscale()
ax.set_xlabel('Time (BTJD)')
ax.set_ylabel('Flux')
fig.show()
io_buf = io.BytesIO()
fig.savefig(io_buf,format="raw")
fig.savefig('test.png') # So I could see the dimensions of the array
io_buf.seek(0)
img_arr = np.frombuffer(io_buf.getvalue(),dtype=np.uint8)
io_buf.close()
return img_arr
# I put something random -- Next time, provide this step so others can more easily debug your code. Never touched lightkurve before
search = lightkurve.search_lightcurve('KIC 757076', author="Kepler", quarter=3)
imgarr = download(search)
fig, ax = plt.subplots()
ax.imshow(imgarr.reshape(288, -1), aspect=4, cmap='gray') # Visualizing the image from the array. Got '288' from the dimensions of the png.
Original plot:
Reconstructed plot:

Showing FITS image with 4-D array shape (python)

I downloaded a VLASS fits file and i'm trying to plot it without success.
The problem is, i think, the shape
print(image_data.shape)
which gives me (1, 1, 3722, 3722).
My code is:
import matplotlib.pyplot as plt
import numpy as np
from astropy.io import fits
from astropy.visualization import astropy_mpl_style
plt.style.use(astropy_mpl_style)
hdulist = fits.open("vla_test.fits")
hdu = hdulist[0]
plt.figure()
plt.imshow(hdu.data[0,0,:,:], cmap='Greys')
plt.colorbar()
I don't get any error but image doesn't show off and i get white (void) image.
I tried to download other files from the survey but i get same result.

Image data in mha brain tumor file

I have an MHA file and when I write
from medpy.io import load
image_data, image_header = load("HG/0001/VSD.Brain.XX.O.MR_Flair/VSD.Brain.XX.O.MR_Flair.684.mha")
print(image_data.shape)
I get a tuple (160, 216, 176). What do these dimensions represent (for reference these are brain tumor images from BRATS 2013)? Your help is appreciated.
Edit: on Jupyter for the slider to work I did
import matplotlib.pyplot as plt
from ipywidgets import interact
import numpy as np
%matplotlib inline
#interact(x=(0, image_data.shape[2]))
def update(x):
plt.imshow(np.flip(image_data[x].T, 0))
but of course your code probably works on other editors
According to the documentation, load(image) "Loads the image and returns a ndarray with the image’s pixel content as well as a header object."
Further down in medpy.io.load it says that image_data is "The image data as numpy array with order x,y,z,c.".
Edit: Because I was kind of curious to see what is actually in this file, I put together a quick script (heavily based on the slider demo) to take a look. I'll leave it here just in case it may be useful to someone. (Click on the "Layer" slider to select the z-coordinate to be drawn.)
from medpy.io import load
image_data, image_header = load("/tmp/VSD.Brain.XX.O.MR_Flair.684.mha")
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
axlayer = plt.axes([0.25, 0.1, 0.65, 0.03])
slider_layer = Slider(axlayer, 'Layer', 1, image_data.shape[2], valinit=1, valstep=1)
def update(val):
layer = slider_layer.val
ax.imshow(image_data[:,:,layer])
fig.canvas.draw_idle()
slider_layer.on_changed(update)
ax.imshow(image_data[:,:,0])
plt.show()
(This indirectly confirms that image_data holds a 3-D voxel image.)
Just to add on top the accepted answer, we can visualize the slices with subplots and animation too:
from medpy.io import load
image_data, image_header = load("VSD.Brain.XX.O.MR_Flair.684.mha")
image_data = image_data / image_data.max()
plt.figure(figsize=(20,32))
plt.gray()
plt.subplots_adjust(0,0,1,0.95,0.01,0.01)
for i in range(ct.shape[0]):
plt.subplot(16,10,i+1), plt.imshow(image_data[i]), plt.axis('off')
plt.suptitle('Brain-Tumor CT-scan mha (raw) files', size=15)
plt.show()

Transform numpy array to RGB image array

Consider the following code:
import numpy as np
rand_matrix = np.random.rand(10,10)
which generates a 10x10 random matrix.
Following code to display as colour map:
import matplotlib.pyplot as plt
plt.imshow(rand_matrix)
plt.show()
I would like to get the RGB numpy array (no axis) from the object obtained from plt.imshow
In other words, if I save the image generated from plt.show, I would like to get the 3D RGB numpy array obtained from:
import matplotlib.image as mpimg
img=mpimg.imread('rand_matrix.png')
But without the need to save and load the image, which is computationally very expensive.
Thank you.
You can save time by saving to a io.BytesIO instead of to a file:
import io
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from PIL import Image
def ax_to_array(ax, **kwargs):
fig = ax.figure
frameon = ax.get_frame_on()
ax.set_frame_on(False)
with io.BytesIO() as memf:
extent = ax.get_window_extent()
extent = extent.transformed(fig.dpi_scale_trans.inverted())
plt.axis('off')
fig.savefig(memf, format='PNG', bbox_inches=extent, **kwargs)
memf.seek(0)
arr = mpimg.imread(memf)[::-1,...]
ax.set_frame_on(frameon)
return arr.copy()
rand_matrix = np.random.rand(10,10)
fig, ax = plt.subplots()
ax.imshow(rand_matrix)
result = ax_to_array(ax)
# view using matplotlib
plt.show()
# view using PIL
result = (result * 255).astype('uint8')
img = Image.fromarray(result)
img.show()

Imaging simulation slice and adding a shape

I have been making slice images of a simulation, now I need to add a shape to the image, the slice has a colour map, I add a circle to the slice, I need help with making the circle colour be adjustable by values, and share the same colormap as the slice.The code I use is:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import csv
def readslice(ndim):
shape = (ndim,ndim,ndim)
fd = open("delta_T_v3_no_halos_z013.40_nf0.898994_useTs0_zetaX-1.0e+00_alphaX-1.0_TvirminX-1.0e+00_aveTb027.03_Pop-1_300_200Mpc", 'rb')
data = np.fromfile(file=fd, dtype= np.dtype('f4')).reshape(shape)
fd.close()
print data
return data
ff = readslice(300)
circle1=plt.Circle((150.0,150.0),50.0)
fig = plt.gcf()
fig.gca().add_artist(circle1)
plt.imshow(ff[0,:,:],cmap = cm.jet)
plt.colorbar()
plt.savefig('picwithcircle.png')
plt.show()

Categories