Convert pyplot figure into wand.image Image - python

Is there a way to convert a pyplot figure created with pyplot.Figure into a wand image? I have tried using the following to no avail:
image_data = BytesIO()
figure.savefig(image_data, format='png')
image_data.seek(0)
image = Image(file=image_data, resolution=250)
The end goal of this is to convert a list of figures into a long png. The only other method (which is ugly) is to convert to pdf and then concatenate the pages.

I was trying to figure out how to do this same thing. I went down a rabbit hole for a bit thinking I needed to also use PIL (Pillow) to accomplish this task. With the help of the previous answer I was able to come up with a complete example:
import matplotlib
from io import BytesIO
import numpy
import matplotlib.pyplot as plt
from wand.display import display
from wand.image import Image
plt.plot([1,5,3,2])
plt.ylabel('y axis numbers')
plt.xlabel('x axis numbers')
image_data = BytesIO() #Create empty in-memory file
plt.savefig(image_data, format='png') #Save pyplot figure to in-memory file
image_data.seek(0) #Move stream position back to beginning of file
img = Image(file=image_data) #Create wand.image
display(img) #Use wand to display the img

I believe you are on the right track. Without seeing the figure, I would assume the issue would be related to wand holding the C structure pointer using the with keyword.
image_data = BytesIO()
figure.savefig(image_data, dpi=250, format='png')
image_data.seek(0)
with Image(file=image_data) as img:
# ... do work
img.save(filename='/tmp/out.png')

I tried the recommended code above and had no luck. I posted the question to the WandB forum (here) and the following was recommended:
fig, ax1 = plt.subplots(...)
...
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
wandb.log(({"chart": wandb.Image(Image.open(buf)) }))
fig.show()
It seems that using the file parameter is no longer allowed.

Related

matplotlib image to base64 without saving

I am creating a wordcloud image with this code.
wordcloud = WordCloud(
background_color='white',
random_state=30,
width=1500,
height=1200,
font_path = font_path,
prefer_horizontal=1)
wordcloud.generate_from_frequencies(frequencies=d)
I show the image with matplotlib like this:
plt.imshow(wordcloud)
plt.axis('off')
plt.show()
I am using this as part of a web app. I want to convert this image to base64 and store as a string as a value in a dictionary key for a specific instance. I see a lot of posts about how to convert images to base64 but it looks like they involve saving the figure locally before encoding. How do I do this without saving anywhere so I can just go from image to string?
This code looks kind of like what I want.
import base64
from PIL import Image
from io import BytesIO
with open("image.jpg", "rb") as image_file:
data = base64.b64encode(image_file.read())
im = Image.open(BytesIO(base64.b64decode(data)))
im.save('image1.png', 'PNG')
If I just did this, would this accomplish my task?
data = base64.b64encode(wordcloud)
If I just did this, would this accomplish my task?
data = base64.b64encode(wordcloud)
No. You need to "save" the image, get that bytestream, and encode that to base64. You don't have to save the image to an actual file; you can actually use a buffer.
w = WordCloud().generate('Test')
buffer = io.BytesIO()
w.to_image().save(buffer, 'png')
b64 = base64.b64encode(buffer.getvalue())
And to convert that back and display the image
img = Image.open(io.BytesIO(base64.b64decode(b64)))
plt.imshow(img)
plt.show()

Possible to reconstruct audio only with spectrogram image?

So I'm creating some spectrograms with librosa to be saved as images, after which I intend to make modifications to the image directly (ie. add random noise, etc), then I would like to reconstruct the audio from that image.
Anyway, some research led me to examples of similar processes (see here or here) but nothing quite like I'm trying to do, which is take a png/jpg image of a spectrogram and convert it back to an usable audio file.
Here's the full code I'm using to generate the spec images:
import librosa
from librosa import display
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
filename = librosa.util.example_audio_file()
y, sr = librosa.load(filename)
window_size = 1024
window = np.hanning(window_size)
stft = librosa.core.spectrum.stft(y, n_fft=window_size, hop_length=512, window=window)
out = 2 * np.abs(stft) / np.sum(window)
fig = plt.Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
fig.subplots_adjust(left=0,right=1,bottom=0,top=1)
ax.axis('tight')
ax.axis('off')
p = librosa.display.specshow(librosa.amplitude_to_db(out, ref=np.max), ax=ax, y_axis='log', x_axis='time')
fig.savefig('spectrogram.png')
Which would produce this exact image:
spectrogram.png
But functions like librosa.istft or librosa.griffinlim expect the output of librosa.core.spectrum.stft, and I haven't been able to reverse that entire process coming from just the image file. Assuming I had this picture, is there any way to build the audio back again (even if it's lossy)? What kind of other information would be necessary, and how could I do it?
Thanks in advance.

Serving matplotlib graphs with django without saving

I am using a package called matplotlib to create some graphs, based on user input. Creating these graphs can be done like so
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
some_plot = plt.figure()
Moreover it is possible to then save these graphs as images,
some_plot.savefig(path, format='png')
The problem is that I don't really want to save every single user generated graph, but rather I would like to just display them. I have tried to look up some solutions for related problems. One such solution was to use IoBytes. Following along these answer I get something like
from io import BytesIO
some_plot_io = BytesIO()
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
some_plot = plt.figure()
some_plot.savefig(some_plot_io, format='png')
Is it possible to somehow pass on the BytesIO object to the template and serve the image or does this method not work? Would there be some other method to do this?
After searching for quite a while and fiddling with my code I managed to find an answer. I have included a complete example of my working code below as most of the answers I could find online were very short and cryptic.
Imports
from io import BytesIO
import base64
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
views
plt.plot(range(10))
buf = BytesIO()
plt.savefig(buf, format='png')
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '')
buf.close()
You can then pass on image_base64 to your template and display it using
template
<img src="data:image/png;base64, {{ image_base64 }}" alt="somealt" />
Thanks to comments by DavidG and swatchai for pointing me in the right direction to search for.

Image plotted with matplotlib not showing colorbar

I have a .fit file. I have read the file, displayed the image with scale. When I want to write this image in .png file, the .png file is displaying the image without scale. I am attaching the code that I have tried.
import pyfits
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
hdulist = pyfits.open('HMI20170425_134641_6173.fits')
image_data = hdulist[0].data
hdulist.close()
fig=plt.imshow(image_data, cmap='gray')
plt.colorbar()
fig.write_png('image.png')
It is showing output image with scale. However, the 'image.png' file showing image without scale.
Please help me in this regard.
I guess what you call the scale is actually the colorbar ? Which indeed is missing when you use fig.write_png because here you are saving only the image part of the plot. You should use plt.savefig instead:
# use astropy instead of pyfits which is no more maintained
import astropy.io.fits as pyfits
import matplotlib.pyplot as plt
%matplotlib inline
image_data = pyfits.getdata('HMI20170425_134641_6173.fits')
plt.imshow(image_data, cmap='gray')
plt.colorbar()
plt.savefig('image.png')

Writing pandas/matplotlib image directly into XLSX file

I am generating plots in pandas/matplotlib and wish to write them to an XLSX file. I am not looking to create native Excel charts; I am merely writing the plots as non-interactive images. I am using the XlsxWriter library/engine.
The closest solution I have found is the answer to this SO question, which suggests using the XlsxWriter.write_image() method. However, this method appears to take a filename as its input. I am trying to programmatically pass the direct output from a pandas/matplotlib plot() call, e.g. something like this:
h = results.resid.hist()
worksheet.insert_image(row, 0, h) # doesn't work
or this:
s = df.plot(kind="scatter", x="some_x_variable", y="resid")
worksheet.insert_image(row, 0, s) # doesn't work
Is there any way to accomplish this, short of the workaround of writing the image to a disk file first?
Update
Answer below got me on the right track and am accepting. I needed to make a few changes, mainly (I think) because I am using Python 3 and perhaps some API changes. Here is the solution:
from io import BytesIO
import matplotlib.pyplot as plt
imgdata = BytesIO()
fig, ax = plt.subplots()
results.resid.hist(ax=ax)
fig.savefig(imgdata, format="png")
imgdata.seek(0)
worksheet.insert_image(
row, 0, "",
{'image_data': imgdata}
)
The "" in the insert_image() code is to trick Excel, which is still expecting a filename/URL/etc.
You can save the image to memory as a file object (not to disk) and then use that when inserting to Excel file:
import matplotlib.pyplot as plt
from cStringIO import StringIO
imgdata = StringIO()
fig, ax = plt.subplots()
# Make your plot here referencing ax created before
results.resid.hist(ax=ax)
fig.savefig(imgdata)
worksheet.insert_image(row, 0, imgdata)

Categories

Resources