Save matplotlib to final given size including titles [duplicate] - python

This question already has answers here:
How to adjust padding with cutoff or overlapping labels
(8 answers)
Closed 4 years ago.
I am making figures for publication. I need the final figure, including the titles and text to be 3 inches wide and saved as a .tiff file. I know I can specify the size of the plot via matplotlib using
matplotlib.pyplot.figure(figsize=(3,2), dpi=300)
However this only specifies the size of the actual plot, not including the titles and everything. Also, the titles get cut off of my saved .tiff file when I try this method. Of course the easy fix is to decrease the size of the plot and manually make the figure the correct size, but does anyone know of an automatic way to make the plot be a given final size with all the bells and whistles included?
Here is the output image which is saved (this one is a png so it can upload to stackoverflow). Note the plot titles are cut off for some reason.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from PIL import Image
from io import BytesIO
bent=[2.83263904,2.75860489,2.64546045,2.4949913,2.34923737,2.16430217,2.02562783,1.82478841,1.70324689,1.70315642,1.39739535,1.33945747,1.22295623,1.15726486,1.08449954,0.96155077,0.90325786,0.84547091]
bent=[i/27 for i in bent]
print(bent)
planar=[4.11233905,3.93027011,3.65135645,3.38525615,3.1130921,2.81042899,2.58995789,2.36159934,2.15981447,1.9964454,1.74375941,1.63263452,1.48503205,1.38596544,1.26501988,1.17391638,1.07490417,0.99369748]
planar=[i/27 for i in planar]
bi=[2.51027966,2.56495852,2.47033072,2.33008642,2.19395126,2.13732249,1.80922673,1.76037446,1.52930137,1.56732451,1.33905847,1.24952153,1.15699233,1.08251496,0.98449116,0.93838164,0.86542147,0.725736]
bi=[i/34 for i in bi]
T=[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
arr=[bi,bent,planar]
arr=np.array(arr)
arr=arr.T
font = {'family' : 'normal',
'weight' : 'normal',
'size' : 8}
matplotlib.rc('font', **font)
fig = plt.figure(figsize=(3,2), dpi=300)
plt.title("-ΔS$_m$ vs T") #for Fe$_4$Gd
plt.xlabel('Temperature (K)')
plt.ylabel("$-ΔS_m$ ($J.K^{-1}.mol^{-1}n (e^{-})^{-1}$)")
MMM=('Fe$_4$Gd$_2$','Fe$_4$Gd bent','Fe$_4$Gd planar')
plt.plot(T,arr,'o')
plt.legend(MMM, loc='best')
# save figure
# (1) save the image in memory in PNG format
png1 = BytesIO()
fig.savefig(png1, format='png')
# (2) load this image into PIL
png2 = Image.open(png1)
# (3) save as TIFF
png2.save('TESTTESTTEST.tiff')
png1.close()

Use plt.tight_layout(). You can find more details here.

Related

Image not updating in python plot during animation

The Problem:
I'm trying to simulate a live video by cycling through a series of still images I have saved in a directory, but when I add the animation and update functions my plot is displayed empty.
Background on why I'm doing this:
I believe its important for me to do it this way rather than a complete change of approach, say turning the images into a video first then displaying that, because what I really want to test is the image analysis I will be adding and then overlaying on each frame. The final application will be receiving frames one by one from a camera and will need to do some processing, display the image + annotations + output the data as .csv etc... I'm simulating this for now because I do not have any of the hardware to generate the images and will not have it for several months during which time I need to get the image processing set up, but I do have access to some sets of stills that are approximately what will be produced. In case its relevant my simulation images are 1680x1220 and are 1.88 Mb TIFFs, though I could covert and compress them if needed, and in the final form the resolution will be a bit higher and probably the image format could be adjusted if needed.
What I have tried:
I followed an example to list all files in a folder, and an example
to update a plot. However, the plot displays blank when I run the
code.
I added a line to print the current file name, and I can see this
cycling as expected.
I also made sure the images will display in the plot if I just create
a plot and add one image, and they do. But, when combined with the
animation function the plot is blank and I'm not sure what I've done
wrong/failed to include.
I also tried adding a plt.pause() in the update, but again this
didn't work.
I increased the interval up to 2000 to give it more time, but that didn't work. I believe 2000 is extreme, I'm expecting it should work with more like 20-30fps. Going to 0.5fps tells me the code is wrong or incomplete, rather than it just being a question of needing time to read the image file.
I appreciate no one else has my images, but they are nothing special. I'm using 60 images but I guess it could be tested with any 2 random images and setting range(60) to range(2) instead?
The example I copied originally demonstrated the animation function by making a random array, and if I do that it will show a plot that updates with random squares as expected.
Replacing:
A = np.random.randn(10,10)
im.set_array(A)
...with my image instead...
im = cv2.imread(files[i],0)
...and the plot remains empty/blank. I get a window shown called "Figure1" (like when using the random array), but unlike with the array there is nothing in this window.
Full code:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import os
import cv2
def update(i):
im = cv2.imread(files[i],0)
print(files[i])
#plt.pause(0.1)
return im
path = 'C:\\Test Images\\'
files = []
# r=root, d=directories, f = files
for r, d, f in os.walk(path):
for file in f:
if '.TIFF' in file:
files.append(os.path.join(r, file))
ani = FuncAnimation(plt.gcf(), update, frames=range(60), interval=50, blit=False)
plt.show()
I'm a python and a programming novice so have relied on adjusting examples others have given online but I have only a simplistic understanding of how they are working and end up with a lot of trial and error on the syntax. I just can't figure out anything to make this one work though.
Cheers for any help!
The main reason nothing is showing up is because you never add the images to the plot. I've provided some code below to do what you want, be sure to look up anything you are curious about or don't understand!
import glob
import os
from matplotlib import animation
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
IMG_DIRPATH = 'C:\\Test Images\\' # the folder with your images (be careful about
# putting spaces in directory names!)
IMG_EXT = '.TIFF' # the file extension of your images
# Create a figure, and set to the desired size.
fig = plt.figure(figsize=[5, 5])
# Create axes for the current figure so that images can be sized appropriately.
# Passing in [0, 0, 1, 1] makes the axes fill the whole figure.
# frame_on=False means we won't have a bounding box, and setting xticks=[] and
# yticks=[] means that we won't have pesky tick marks along our image.
ax_props = {'frame_on': False, 'xticks': [], 'yticks': []}
ax = plt.axes([0, 0, 1, 1], **ax_props)
# Get all image filenames.
img_filepaths = glob.glob(os.path.join(IMG_DIRPATH, '*' + IMG_EXT))
def update_image(img_filepath):
# Remove all existing images on the axes, and restore our settings.
ax.clear()
ax.update(ax_props)
# Read the current image.
img = mpimg.imread(img_filepath)
# Add the current image to the plot axes.
ax.imshow(img)
anim = animation.FuncAnimation(fig, update_image, frames=img_filepaths, interval=250)
plt.show()

Concatenated images are badly degraded

I am trying to display several pictures on my Jupyter notebook. However, the pixel is really rough like below.
The pixel of original picture is clear. How should I improve this issue ?
This is a certain point of process to have a classification whether the picture is dog or cat. I have a many pictures of dogs and cat in the folder located on same directory and just took them from there. The picture is I just tried to show on the Jupyter notebook with using matplotlib.
Thank you in advance.
To force the resolution of the matplotlib inline images:
import matplotlib as plt
dpi = 300 # Recommended to set between 150-300 for quality image preview
plt.rcParams['figure.dpi'] = dpi
I think it uses a very low setting around 80 dpi by default.
The image quality seems to be degraded in the example picture simply because you are trying to show a 64 pixel large image on 400 pixels or so on screen. Each original pixel thus comprises several pixels on screen.
It seems you do not necessarily want to use matplotlib at all if the aim is to simply show the image in its original size on screen.
%matplotlib inline
import numpy as np
from IPython import display
from PIL import Image
a = np.random.rand(64,64,3)
b = np.random.rand(64,64,3)
c = (np.concatenate((a,b), axis=1)*255).astype(np.uint8)
display.display(Image.fromarray(c))
To achieve a similar result with matplotlib, you need to crop the margin around the axes and make sure the figure size is exactly the size of the array to show.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
a = np.random.rand(64,64,3)
b = np.random.rand(64,64,3)
c = np.concatenate((a,b), axis=1)
fig, ax = plt.subplots(figsize=(c.shape[1]/100.,c.shape[0]/100.), dpi=100)
fig.subplots_adjust(0,0,1,1)
ax.axis("off")
_ = ax.imshow(c)

Reading matplotlib graphs from file

Is it possible to read (say) 4 .jpeg graphs produced by matplotlib into matplotlib again so that they can be replotted as subplots? If so, how would I do it?
If you really want to do it by reading jpeg files of existing plots (noting the comments), one way might be to read in the graphs in with scipy.misc.imread. I've set the axis labels off assuming you saved the original graphs with labels and everything.
import matplotlib.pyplot as plt
from scipy.misc import imread
# Create a figure with 2x2 arranged subplots
fig, ax = plt.subplots(2,2)
# Plot images one by one here
# (Just using the same jpeg file in this example...)
im1 = imread("graph1.jpg")
ax[0,0].imshow(im1)
ax[0,0].axis('off')
ax[0,1].imshow(im1)
ax[0,1].axis('off')
ax[1,0].imshow(im1)
ax[1,0].axis('off')
ax[1,1].imshow(im1)
ax[1,1].axis('off')
fig.show()

matplotlib setting pdf raster resolution using pdfpages savefig dpi

I have a project that generates a set of charts using matplotlib and exports each one as an individual PNG file. The client also wants the group of charts output as a single PDF file. We have successfully got this to work using PIL Image and matplotlib PdfPages to import each PNG on a separate page and export the multi-page PDF, but the PDF output quality is unacceptable. If I use anything other than dpi=100 with savefig, then output is blank. Any suggestions on how to export a PDF with higher raster resolution?
pp = PdfPages(filepath+'Treatment Zone Charts.pdf')
for file in os.listdir(filepath):
if "Treatment Zone Charts" in file and file.endswith(".png"):
print filepath+file
#read PNG image
im = Image.open(filepath+'/'+file)
im = im.resize((1100,850),Image.ANTIALIAS)
im = np.array(im).astype(np.float) / 255
# Create a figure with size w,h tuple in inches
fig = Figure(figsize=(11,8.5))
# Create a canvas and add the figure to it.
canvas = PDF_FigureCanvas(fig)
#add image to plot
fig.figimage(im, 0, -220, zorder = 1)
# Save the Plot to the PDF file
pp.savefig(fig, dpi=100)
#close the PDF file after the last plot is created
pp.close()
The output was blank because the image was positioned off of the canvas. For dpi=200 and no resizing of the original image, we needed to modify figimage to:
fig.figimage(im, 0, -1080, zorder = 1)
The output is very sensitive to the y-value, a small change from -1080 and the image was placed off the top or bottom of the page. I don't understand why the dpi setting has this effect on the figimage y parameter, but this now produces PDF output with good quality.

Change figure size and figure format in matplotlib [duplicate]

This question already has answers here:
How do I change the size of figures drawn with Matplotlib?
(14 answers)
Closed 1 year ago.
I want to obtain fig1 exactly of 4 by 3 inch sized, and in tiff format correcting the program below:
import matplotlib.pyplot as plt
list1 = [3,4,5,6,9,12]
list2 = [8,12,14,15,17,20]
plt.plot(list1, list2)
plt.savefig('fig1.png', dpi = 300)
plt.close()
You can set the figure size if you explicitly create the figure with
plt.figure(figsize=(3,4))
You need to set figure size before calling plt.plot()
To change the format of the saved figure just change the extension in the file name. However, I don't know if any of matplotlib backends support tiff
You can change the size of the plot by adding this before you create the figure.
plt.rcParams["figure.figsize"] = [16,9]
The first part (setting the output size explictly) isn't too hard:
import matplotlib.pyplot as plt
list1 = [3,4,5,6,9,12]
list2 = [8,12,14,15,17,20]
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
ax.plot(list1, list2)
fig.savefig('fig1.png', dpi = 300)
fig.close()
But after a quick google search on matplotlib + tiff, I'm not convinced that matplotlib can make tiff plots. There is some mention of the GDK backend being able to do it.
One option would be to convert the output with a tool like imagemagick's convert.
(Another option is to wait around here until a real matplotlib expert shows up and proves me wrong ;-)
If you need to change the figure size after you have created it, use the methods
fig = plt.figure()
fig.set_figheight(value_height)
fig.set_figwidth(value_width)
where value_height and value_width are in inches. For me this is the most practical way.

Categories

Resources