I like to produce high quality plots and therefore avoid rasterized graphics as much as possible.
I am trying to import an svg file on to a matplotlib figure:
import matplotlib.pyplot as plt
earth = plt.imread('./gfx/earth.svg')
fig, ax = plt.subplots()
im = ax.imshow(earth)
plt.show()
This works with png perfectly. Can somebody tell me how to do it with svg or at least point my to proper documentation.
I know that a similar question has been asked (but not answered): here. Has anything changed since?
P.S. I know that I could just export a high resolution png and achieve a similar effect. This is not the solution I am looking for.
Here is the image I would like to import:
.
Maybe what you are looking for is svgutils
import svgutils.compose as sc
from IPython.display import SVG # /!\ note the 'SVG' function also in svgutils.compose
import numpy as np
# drawing a random figure on top of your SVG
fig, ax = plt.subplots(1, figsize=(4,4))
ax.plot(np.sin(np.linspace(0,2.*np.pi)), np.cos(np.linspace(0,2.*np.pi)), 'k--', lw=2.)
ax.plot(np.random.randn(20)*.3, np.random.randn(20)*.3, 'ro', label='random sampling')
ax.legend()
ax2 = plt.axes([.2, .2, .2, .2])
ax2.bar([0,1], [70,30])
plt.xticks([0.5,1.5], ['water ', ' ground'])
plt.yticks([0,50])
plt.title('ratio (%)')
fig.savefig('cover.svg', transparent=True)
# here starts the assembling using svgutils
sc.Figure("8cm", "8cm",
sc.Panel(sc.SVG("./Worldmap_northern.svg").scale(0.405).move(36,29)),
sc.Panel(sc.SVG("cover.svg"))
).save("compose.svg")
SVG('compose.svg')
Output:
to anyone ending up here in 2021...
I'd suggest having a look at the cairosvg package
(conda install -c conda-forge cairosvg or pip3 install cairosvg)
https://cairosvg.org/
import cairosvg
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
img_png = cairosvg.svg2png("... the content of the svg file ...")
img = Image.open(BytesIO(img_png))
plt.imshow(img)
SVG (Scalable Vector Graphics) is a vectorial format, which means the image is not composed of pixels, but instead of relative paths that can be scaled arbitrarily.
NumPy/Matplotlib, as numerics software, only really works with pixel graphics and cannot handle svg. I would suggest first converting the svg file to e.g. a png file by opening and saving it in software such as Inkscape (which is free). Then, open the exported png in Python.
Alternatively, use the wikimedia provided versions of the file in png format on the picture's information page (click on the download button to the right of the picture).
If you really believe you need the vectorial form, well, there is no way to do that. You can always superpose the matplotlib figure on to the figure manually (using the matplotlib Artist to draw on the plot canvas), or through some pycairo magic, and save that. But Matplotlib cannot work directly with svg content.
Related
I'm trying to show a figure, in PyCharm, that is in my working directory, but it either doesn't work or only works with this code:
img = mpimg.imread('Figure_1.png')
plt.imshow(img)
plt.show()
with this result:
I just want the picture as it is, not inside another figure. This is the original picture for reference:
Some quick workarounds: to remove the axes, do plt.axes('off'). To make the picture fit the frame, set the aspect ratio to 'auto' and create a figure with the same aspect ratio as your original image (i'd say it's roughly 4:1?). Use tight_layoutto make sure all of your image is visible. I don't know if that's the official way, but that's how i do it, and it kinda works ;-)
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread('Figure_1.png')
f, a = plt.subplots(figsize=(16, 4))
plt.tight_layout()
plt.axis('off')
a.imshow(img)
plt.show()
I'm trying to use astropy 2.0.11 with python 2.7.15 to edit a fits image by applying a log stretch to it and change the contrast, and I have't been able to figure it out.
I've been trying to follow the tutorials on the astropy website for opening and manipulating fits files, but I'm wondering if the tutorials will only work for the latest version of astropy and on python 3?
Sorry about the organization of my code. This is prototype code and I'm just trying to test a few things and get this to work.
import time
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
import astropy.visualization
from astropy.io import fits
from astropy.utils.data import download_file
from astropy.visualization import astropy_mpl_style
plt.style.use(astropy_mpl_style)
from astropy.utils.data import get_pkg_data_filename
def main():
#My own fits file
#fitsImage = get_pkg_data_filename("C:\\20180807T000456.fits")
fitsImage = download_file('http://data.astropy.org/tutorials/FITS-images/HorseHead.fits', cache=True )
hdu_list = fits.open(fitsImage)
hdu_list.info()
#norm = ImageNormalize(stretch=LogStretch())
image_data = fits.getdata(fitsImage)
print(type(image_data))
print(image_data.shape)
hdu_list.close()
plt.figure()
plt.imshow(image_data, cmap='gray', norm=LogNorm())
plt.colorbar()
# I chose the tick marks based on the histogram above
cbar = plt.colorbar(ticks=[5.e3,1.e4,2.e4])
cbar.ax.set_yticklabels(['5,000','10,000','20,000'])
time.sleep(10)
I am also unable to get the image to display with the plt.imshow()
Any insight would be helpful
You're so close! I ran your code in Python 2.7 and all you need to do is add
plt.show()
before time.sleep(10) (any reason you're including this?) and you get
Also, I don't think you need to include the colorbar and yticklabels, plt.imshow automatically adds the colorbar with the lognorm scale (I commented that section out when I got the image).
I have a 2D array that I need to save as a png. I also need to add a text label to the image. So far, I have tried two approaches, none of which is optimal:
I use the matplotlib.image module to save the array directly as an image:
matplotlib.image.imsave(FILENAME, ARRAY, cmap=plt.cm.binary)
However I am unable to add text using that command. I could use PIL to read and edit after saving the raw images, but the I/O cost on a large data set would be unacceptable.
I use the pyplot interface to convert the array to a figure and then add a legend. However when I save it as a file, there is unnecessary whitespace. I have tried turning axes off, setting padding to 0 etc., but there is always some whitespace margin I cannot get rid of:
import matplotlib.pyplot as plt
plt.imshow(ARRAY, cmap=plt.cm.binary)
plt.axis('off')
plt.savefig(FILENAME, dpi=100, pad_inches=0.0, bbox_inches='tight')
Is there a way to generate an image from a 2D array, overlay text, and save as .png speedily with no whitespace? Preferably a solution using matplotlib/PIL, but if there's anything better out there, I can look into it.
I was able to solve my problem by using an object oriented approach from the start:
import matplotlib.pyplot as plt
fig = plt.figure(dpi=100, tight_layout=True, frameon=False, figsize=(resolution/100.,resolution/100.)) # dpi & figsize of my choosing
fig.figimage(ARRAY, cmap=plt.cm.binary)
fig.text(X,Y,TEXT, size='medium', backgroundcolor='white', alpha=0.5)
plt.savefig(FILENAME)
plt.close(fig)
Additional documentation for the figure class can be found here.
Note: For sizing figures, I found this relationship useful:
size in inches = resolution in pixels / DPI
This question is related to a comment on another question.
In matplotlib/python, I am trying to rasterize a specific element in my image and save it to eps. The issue is that when saving to EPS (but not SVG or PDF), a black background appears behind the rasterized element.
Saving to PDF and converting to EPS does not seem to be a reasonable solution, as there are weird pdf2ps and pdftops conversion issues that make understanding bounding boxes very ... scary (or worse, seemingly inconsistent). My current work around involves a convoluted process of saving in svg and export in Inkscape, but this should also not be required.
Here is the sample code needed to reproduce the problem. Matplotlib and Numpy will be needed. If the file is saved to mpl-issue.py, then it can be run with:
python mpl-issue.py
#!/usr/bin/env python
# mpl-issue.py
import numpy as np
import matplotlib as mpl
# change backend to agg
# must be done prior to importing pyplot
mpl.use('agg')
import matplotlib.pyplot as plt
def transparencytest():
# create a figure and some axes
f = plt.figure()
a = {
'top': f.add_subplot(211),
'bottom': f.add_subplot(212),
}
# create some test data
# obviously different data on the subfigures
# just for demonstration
x = np.arange(100)
y = np.random.rand(len(x))
y_lower = y - 0.1
y_upper = y + 0.1
# a rasterized version with alpha
a['top'].fill_between(x, y_lower, y_upper, facecolor='yellow', alpha=0.5, rasterized=True)
# a rasterized whole axis, just for comparison
a['bottom'].set_rasterized(True)
a['bottom'].plot(x, y)
# save the figure, with the rasterized part at 300 dpi
f.savefig('testing.eps', dpi=300)
f.savefig('testing.png', dpi=300)
plt.close(f)
if __name__ == '__main__':
print plt.get_backend()
transparencytest()
The testing.png image looks like this:
The testing.eps image ends up looks like this (in converted pdf versions and the figure-rasterized png):
The black backgrounds behind the rasterized elements are not supposed to be there. How can I remove the black backgrounds when saving an eps figure with rasterized elements in it?
This has been tested with a bunch of other mpl backends, so it does not appear to be a specific problem with agg. Mac OS X 10.9.4, Python 2.7.8 built from MacPorts, Matplotlib 1.3.1.
This was a known bug which has been fixed.
This is due to the fact that eps does not know about transparency and the default background color for the rasterization was (0, 0, 0, 0) (black which is fully transparent).
I also had this problem (https://github.com/matplotlib/matplotlib/issues/2473) and it is fixed (https://github.com/matplotlib/matplotlib/pull/2479) in matplotlib 1.4.0 which was released last night.
I'm trying to use matplotlib to generate 3D figures where the xy plane is an image, and then some 3D tracks are drawn on top (that part works just fine). The problem is, even though my imported PNG shows just fine with imshow, and even though I can plot an image on a 3D axis if I just use an example from the cookbook, my image just shows up as a featureless black box. I'm sure I'm missing something small- thanks in advance!
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D, art3d
from pylab import ogrid
import matplotlib.pyplot as plt
plt.ioff()
fig = plt.figure()
ay=fig.add_subplot(2,1,1)
rawim=plt.imread(r'G:\Path\myimage.png')
ay.imshow(rawim,cmap='gray')
ax=fig.add_subplot(2,1,2,projection='3d')
x,y= ogrid[0:rawim.shape[0],0:rawim.shape[1]]
ax.plot_surface(x,y,0,rstride=5,cstride=5,facecolors=rawim,cmap='gray')
ax.view_init(elev=45, azim=12)
plt.show()
The output comes out as this (edited to include image).
PS Running Matplotlib 1.2.1 in Spyder for Python 2.75
Edited to add- I was largely modeling my approach from this post, so if instead of
rawim=plt.imread(r'G:\Path\myimage.png')
I use
from matplotlib.cbook import get_sample_data
fn = get_sample_data("lena.png", asfileobj=False)
rawim=read_png(fn)
it works perfectly. I've tried several of my PNG outputs, produced a couple of different ways, and no love. And yes, they're greyscale between 0-1.
You should use an explicit color array for facecolors.
You want something having shape (Nx, Ny, 4) where the "4" dimension holds RGBA values.
Also, get rid of the cmap='gray' in the plot_surface invocation.