Matplotlib svg as string and not a file - python

I'd like to use Matplotlib and pyplot to generate an svg image to be used in a Django framework. as of now I have it generating image files that are link to by the page, but is there a way to directly get with the svg image as a unicode string without having to write to the file system?

Try using StringIO to avoid writing any file-like object to disk.
import matplotlib.pyplot as plt
import StringIO
from matplotlib import numpy as np
x = np.arange(0,np.pi*3,.1)
y = np.sin(x)
fig = plt.figure()
plt.plot(x,y)
imgdata = StringIO.StringIO()
fig.savefig(imgdata, format='svg')
imgdata.seek(0) # rewind the data
svg_dta = imgdata.buf # this is svg data
file('test.htm', 'w').write(svg_dta) # test it

Here is python3 version
import matplotlib.pyplot as plt
import numpy as np
import io
f = io.BytesIO()
a = np.random.rand(10)
plt.bar(range(len(a)), a)
plt.savefig(f, format = "svg")
print(f.getvalue()) # svg string

Related

How to retrieve the raw figure data from matplotlib?

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)

How to render a matplotlib plot with hatch pattern to pdf with reportlab?

I am unable to render correctly a matplotlib plot that contains hatch pattern to a pdf via reportlab.
Here is below a sample code that highlights the problem. The example is a simple but reportlab is needed in the project I am working on, so I cannot output straight to pdf from matplotlib.
from io import BytesIO
import matplotlib.pyplot as plt
import numpy as np
from reportlab.graphics import renderPDF
from reportlab.pdfgen import canvas
from svglib.svglib import svg2rlg
x = np.arange(100)
y = np.random.randint(50, 90, 100)
fig = plt.figure()
plt.plot(x, y, "o", color="b")
plt.axhspan(ymin=40, ymax=55, hatch="/", facecolor="none")
plt.show()
imgdata = BytesIO()
fig.savefig(imgdata, format="svg")
imgdata.seek(0)
drawing = svg2rlg(imgdata)
drawing.scale(0.5, 0.5)
c = canvas.Canvas("test.pdf")
renderPDF.draw(drawing, c, 10, 200)
c.drawString(100, 100, "No hatch pattern rendered")
c.showPage()
c.save()
The expected plot output is (as it is from matplotlib):
however, when exporting to pdf, I'm getting:
The culprit here seems to be svg2rlg that does not the following from the svg output of matplotlib:
Can't handle color: url(#h06c0184332)
Is there a way to circumvent this problem while keeping the output from matplotlib as svg?

Jupiter saves an empty photo, although it shows it before. How to fix that?

I'm reading and output the picture as a plot:
import numpy as np
import os
import matplotlib.pyplot as plt
import matplotlib.ticker as tkr
fig, ax = plt.subplots()
path = 'photo.jpg'
im = plt.imread(path)
ax.imshow(im)
Output
After I want to save it and check(see) the image before saving, but my code generate empty image:(
plt.axis("off")
plt.gca().xaxis.set_major_locator(tkr.NullLocator())
plt.gca().yaxis.set_major_locator(tkr.NullLocator())
filename = os.path.basename(path).split(".")[0]
output_path = os.path.join("test", filename+".png").replace("/", "")
plt.savefig(output_path, bbox_inches="tight", pad_inches=0.0)
plt.show()
plt.close()
Output
Is it necessary to read the image as a graph?
If not, just use PIL library. It is a way more simpler to use:
You can check the code there: https://colab.research.google.com/drive/1Dmhb4aiERRym9bNx5HgCQPOr9iANHMic?usp=sharing
PIL documentation: https://pillow.readthedocs.io/en/stable/handbook/tutorial.html

execute .py file, call python function in for loop

I have a function 'plot_rdm', which creates a plot and saves it as 'rdm.png'. I want several of these plots to be formed, each using a different .json file - so I have the function plot_rdm saved in 'plotrdm.py'.
In the saverdm.py file - I defined the filepath of the .json file I want to create a plot from and then called the plot_rdm function, looping over all of the files I want to create a plot from:
#import libraries
import numpy as np
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt
import scipy
import scipy.spatial
import scipy.spatial.distance as sd
from scipy.spatial.distance import squareform
import os
import json
# define fpath
#i.e. fpath[0] will be the first filepath...
path = './RDM_researchproject'
rootdir = path
filepath = []
for subdir, dirs, files in os.walk(rootdir):
for file in files:
if file.startswith('Meadows'):
count=0 # count default
filepath.append(os.path.join(subdir, file))
fpath = filepath[count]
os.system("/home/taran/RDM_researchproject/AVIMA/plotrdm.py")
count +=1
The plotrdm.py file with the plot_rdm function is as follows:
def plot_rdm(fpath):
import numpy as np
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt
import scipy
import scipy.spatial
import scipy.spatial.distance as sd
from scipy.spatial.distance import squareform
import json
with open(fpath) as fhandle:
data = json.load(fhandle)
#inspect rdm stimuli labels
stim = data['stimuli']
#contain all labels for y axis and x axis seperately
y_names = []
for i in stim:
y_names.append(i['name'])
x_names = []
for i in stim:
x_names.append(i['name'])
#create rdm array and squareform
rdm_array = np.array(data['rdm'])
srdm = squareform(rdm_array)
#label x and y axis on rdm
fig, ax = plt.subplots()
rdm = ax.imshow(srdm)
ax.set_xticks(np.arange(len(x_names)))
ax.set_yticks(np.arange(len(y_names)))
ax.set_xticklabels(x_names)
ax.set_yticklabels(y_names)
plt.setp(ax.get_xticklabels(), rotation=90, ha="right", rotation_mode="anchor")
plt.plot(srdm)
plt.imshow(srdm)
plt.colorbar(mappable = None, cax = None, ax = None)
fig.subplots_adjust(bottom=0.23)
import matplotlib.pyplot as plt
plt.savefig('rdm.png')
I am able to create the plots individually (i.e. when I don't call the plot_rdm function and loop over the files but I specify the filepath each time). But when I use the following code, I get an empty plot forming in the AVIMA folder. I'm not sure what's wrong in the saverdm file making this happen?
https://github.com/Taranks7/RDM_researchproject If I haven't explained what's going on well, this is the project I'm working on.
Thank you
When you want to call a python function from another file, you should not try to run another python process by calling os.system. Just import that function:
from plotrdm import plot_rdm
Instead of using os.filewalk and a file.startswith check, we can cleanup the code a lot by using the nice python library glob. I throw in a enumerate for good measure.
Your new rdmsave.py
import glob
from plotrdm import plot_rdm
basedir = "."
if __name__ == "__main__":
count = 0
for count, path in enumerate(sorted(glob.glob(f'{basedir}/**/Meadow*.json', recursive=True)), start=1):
print(f"processing {path}")
output_image = f'rdm_{count - 1:02}.png'
print(f"output image will be {output_image}")
plot_rdm(path, output_image)
print(f"processed {count} files")
Note that you may need to change basedir back to your local path.
And your plotrdm.py becomes:
import numpy as np
import matplotlib
import matplotlib.pyplot
import matplotlib.pyplot as plt
import scipy
import scipy.spatial
import scipy.spatial.distance as sd
from scipy.spatial.distance import squareform
import json
import matplotlib.pyplot as plt
def plot_rdm(fpath, output_filename):
with open(fpath) as fhandle:
data = json.load(fhandle)
# inspect rdm stimuli labels
stim = data['stimuli']
# contain all labels for y axis and x axis seperately
y_names = []
for i in stim:
y_names.append(i['name'])
x_names = []
for i in stim:
x_names.append(i['name'])
# create rdm array and squareform
rdm_array = np.array(data['rdm'])
srdm = squareform(rdm_array)
# label x and y axis on rdm
fig, ax = plt.subplots()
rdm = ax.imshow(srdm)
ax.set_xticks(np.arange(len(x_names)))
ax.set_yticks(np.arange(len(y_names)))
ax.set_xticklabels(x_names)
ax.set_yticklabels(y_names)
plt.setp(ax.get_xticklabels(), rotation=90, ha="right", rotation_mode="anchor")
plt.plot(srdm)
plt.imshow(srdm)
plt.colorbar(mappable=None, cax=None, ax=None)
fig.subplots_adjust(bottom=0.23)
plt.savefig(output_filename)
I added the second argument output_filename to the plot_rdm function to make it possible to store each image in a new file.
The output on my machine reads
processing ./5/Meadows_avima-image-version1_v_v2_vital-macaw_2_tree.json
output image will be rdm_00.png
processing ./4/Meadows_avima-image-version1_v_v2_quick-louse_2_tree.json
output image will be rdm_01.png
processing ./1/Meadows_avima-image-version1_v_v2_better-hound_2_tree.json
output image will be rdm_02.png
processing ./3/Meadows_avima-image-version1_v_v2_huge-falcon_2_tree.json
output image will be rdm_03.png
processing ./2/Meadows_avima-image-version1_v_v2_guided-koi_2_tree.json
output image will be rdm_04.png
processed 4 files
And 4 png files are created in the current folder.

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()

Categories