Wrong order of frames creating GIF animation from PNG files - python

I'm saving a GIF file from multiple PNG files I create inside a loop. I'm not able to save the PNG's with the right name, because when translated to GIF I get the wrong order of the frames.
The following code is exactly what I need but with the order of the frames messed up. Thanks a lot, I'm new to python.
# basic animated mod 39 wheel in python
import glob
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
plt.close()
plt.rcParams.update({
"lines.color": "white",
"patch.edgecolor": "white",
"text.color": "lightgray",
"axes.facecolor": "black",
"axes.edgecolor": "lightgray",
"axes.labelcolor": "white",
"xtick.color": "white",
"ytick.color": "white",
"grid.color": "lightgray",
"figure.facecolor": "black",
"figure.edgecolor": "black",
"savefig.facecolor": "black",
"savefig.edgecolor": "black"})
plt.xlabel('real axis')
plt.ylabel('imaginary axis')
plt.title('events constellation')
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.gca().set_aspect('equal', adjustable='box')
#for fullscreen plt.draw just reinforces de rendering?
#plt.draw()
#mng = plt.get_current_fig_manager()
#mng.full_screen_toggle()
for n in range(1,40):
cnums = 3 * np.exp(1j * 2 * np.pi * (1/39) * n)
x = cnums.real
y = cnums.imag
plt.scatter(x, y , label="event", marker="o", color="blue", s=250)
#plt.pause(1)
plt.savefig(f'/outpng/img{n}.png',dpi=100)
# filepaths
fp_in = "/outpng/*.png"
fp_out = "/outpng/image.gif"
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
img, *imgs = [Image.open(f) for f in sorted(glob.glob(fp_in))]
img.save(fp=fp_out, format='GIF', append_images=imgs,
save_all=True, duration=600, loop=0)

If I understand correctly, you have:
lst_files = glob.glob(fp_in)
>>> ['/outpng/img28.png', '/outpng/img4.png', '/outpng/img20.png', '/outpng/img32.png', '/outpng/img36.png', '/outpng/img11.png', '/outpng/img1.png', '/outpng/img9.png', '/outpng/img24.png', '/outpng/img35.png', '/outpng/img7.png', '/outpng/img12.png',]
And you want to have the files 'in order' (presumable the number is the order of the images). You can do this using:
import re
def get_number(file_name):
m = re.findall('img([0-9]*)\.', file_name)[0]
return int(m)
lst_files = ['/outpng/img28.png', '/outpng/img4.png', '/outpng/img20.png', '/outpng/img32.png', '/outpng/img36.png', '/outpng/img11.png', '/outpng/img1.png', '/outpng/img9.png', '/outpng/img24.png', '/outpng/img35.png', '/outpng/img7.png', '/outpng/img12.png']
lst_numbers = [get_number(i) for i in lst_files]
lst_number_files = sorted(list(zip(lst_numbers, lst_files)))
lst_files_sorted = [i[1] for i in lst_number_files]
How this works:
you find the number in the string (using re.findall) which fits 'imgX.' where X is your number
you match the numbers to the file names
you sort the files based on the number
you flatten the list to only contain the file names
Now you can use this list to make your GIF:
img, *imgs = [Image.open(f) for f in lst_files_sorted]

Related

Extract sagittal and coronal cuts from axial view using pydicom

I am trying to read a series of .dcm files which are by default show axial view. Below is the code:
import os
import numpy as np
import pydicom as dicom
from matplotlib import pyplot as plt
root_dir = 'mydcomDir'
def sortDcm():
print('Given Path to the .dcm directory is: {}'.format(root_dir))
slices = [dicom.read_file(root_dir + '/' + s) for s in os.listdir(root_dir)]
slices.sort(key = lambda x: float(x.ImagePositionPatient[2]))
pos1 = slices[int(len(slices)/2)].ImagePositionPatient[2]
pos2 = slices[(int(len(slices)/2)) + 1].ImagePositionPatient[2]
diff = pos2 - pos1
# if diff > 0:
# slices = np.flipud(slices)
try:
slice_thickness = np.abs(slices[0].ImagePositionPatient[2] - slices[1].ImagePositionPatient[2])
except:
slice_thickness = np.abs(slices[0].SliceLocation - slices[1].SliceLocation)
for s in slices:
s.SliceThickness = slice_thickness
# print("from sorted dicom",len(slices))
return slices
dcms = sortDcm()
ref_dicom = dcms[0]
d_array = np.zeros((ref_dicom.Columns,ref_dicom.Rows, len(dcms)), dtype=ref_dicom.pixel_array.dtype)
for dcm in dcms:
d_array[:, :, dcms.index(dcm)] = dcm.pixel_array
# fig = plt.figure(figsize=(12,12))
# plt.subplot(1, 3, 1)
# plt.title("Coronal")
# plt.imshow(np.flipud(d_array[idx , :, :].T))
# plt.subplot(1, 3, 2)
# plt.title("Sagital")
# plt.imshow(np.flipud(d_array[:, idy, :].T))
# plt.subplot(1, 3, 3)
plt.title("axial")
plt.imshow(d_array[:, :, dcms.index(dcm)])
plt.pause(0.001)
As you can see from the code I could not figure out the relevant idx and idy for particular dcm file.
So my question is how to get sagittal and coronal cuts and plot them, given the axial cuts?
Thanks in advance.
Edit:
As #ColonelFazackerley answered perfectly. I am adding below line just to show how I used it.
# fill 3D array with the images from the files
for i, s in enumerate(slices):
img2d = s.pixel_array
img3d[:,:,i] = img2d
#then to view sagittal and coronal slices for each of the axial slice
for i, s in enumerate(slices):
img2d = s.pixel_array
img3d[:,:,i] = img2d
corId = corId-1
sagId = sagId-1
# plot 3 orthogonal slices
a1 = plt.subplot(1,3,1)
plt.title('Axial')
plt.imshow(img3d[:,:,i],'gray')
a1.set_aspect(ax_aspect)
a2 = plt.subplot(1,3,2)
plt.title('Sagittal')
plt.imshow(np.flipud(img3d[:,sagId,:].T),'gray')
a2.set_aspect(sag_aspect)
a3 = plt.subplot(1,3,3)
plt.imshow(np.flipud(img3d[corId,:,:].T),'gray')
a3.set_aspect(cor_aspect)
plt.title('Coronal')
plt.show()
plt.pause(0.001)
"""usage: reslice.py <glob>
where <glob> refers to a set of DICOM image files.
Example: python reslice.py "*.dcm". The quotes are needed to protect the glob
from your system and leave it for the script."""
import pydicom
import numpy as np
import matplotlib.pyplot as plt
import sys
import glob
# load the DICOM files
files=[]
print('glob: {}'.format(sys.argv[1]))
for fname in glob.glob(sys.argv[1], recursive=False):
print("loading: {}".format(fname))
files.append(pydicom.read_file(fname))
print("file count: {}".format(len(files)))
# skip files with no SliceLocation (eg scout views)
slices=[]
skipcount=0
for f in files:
if hasattr(f, 'SliceLocation'):
slices.append(f)
else:
skipcount = skipcount + 1
print("skipped, no SliceLocation: {}".format(skipcount))
# ensure they are in the correct order
slices = sorted(slices, key=lambda s: s.SliceLocation)
# pixel aspects, assuming all slices are the same
ps = slices[0].PixelSpacing
ss = slices[0].SliceThickness
ax_aspect = ps[1]/ps[0]
sag_aspect = ps[1]/ss
cor_aspect = ss/ps[0]
# create 3D array
img_shape = list(slices[0].pixel_array.shape)
img_shape.append(len(slices))
img3d=np.zeros(img_shape)
# fill 3D array with the images from the files
for i, s in enumerate(slices):
img2d = s.pixel_array
img3d[:,:,i] = img2d
# plot 3 orthogonal slices
a1 = plt.subplot(2,2,1)
plt.imshow(img3d[:,:,img_shape[2]//2])
a1.set_aspect(ax_aspect)
a2 = plt.subplot(2,2,2)
plt.imshow(img3d[:,img_shape[1]//2,:])
a2.set_aspect(sag_aspect)
a3 = plt.subplot(2,2,3)
plt.imshow(img3d[img_shape[0]//2,:,:].T)
a3.set_aspect(cor_aspect)
plt.show()
tested against series 2 from this example 3D CT data
http://www.pcir.org/researchers/54879843_20060101.html
edit note: accepted as an example into the pydicom project
https://github.com/pydicom/pydicom/blob/master/examples/image_processing/reslice.py

Obtain plot data from JPEG or png file

I'm writing a program that obtains plot data from a graph(JPEG).
Vertical axis is logarithmic.
I successfully made a program that understands horizontal and vertical axes as linear (not logarithmic), see the code below:
%matplotlib inline
from PIL import Image
from scipy import *
from pylab import *
im = array(Image.open('fig1.jpg'))
hh = im.shape[0]
ww = im.shape[2]
imshow(im)
print(im[100,0,:])
Col = array([255,0,0])#赤
bnd = 30
yax = linspace(0.5,2e-4,hh)
xax = linspace(1,14,ww)
for i in range(hh):
for j in range(ww):
im[i,j,:] = 255*(any(im[i,j,:]>Col+bnd) or any(im[i,j,:]<Col-bnd))
mapim = abs(im[:,:,0]/255-1).astype(bool)
yval = array([average(yax[mapim[:,t]]) for t in range(ww)])
rlt = interp(range(100),xax,yval)
I have no idea how to modify it to make it understand logarithmic axis.
Please help me.
You just need to convert the y max and min to log scale:
ymax_lin = log10(0.5)
ymin_lin = log10(2e-4)
yax = linspace(ymax_lin,ymin_lin,hh)
and convert back to linear the yval values at the end:
yval = 10**yval
The full working code is here:
%matplotlib inline
from PIL import Image
from scipy import *
from pylab import *
im = array(Image.open('fig1.jpg'))
hh = im.shape[0]
ww = im.shape[1]
imshow(im)
print(im[100,0,:])
Col = array([255,0,0])
bnd = 30
ymax_lin = log10(0.5)
ymin_lin = log10(2e-4)
yax = linspace(ymax_lin,ymin_lin,hh)
xax = linspace(1,14,ww)
for i in range(hh):
for j in range(ww):
im[i,j,:] = 255*(any(im[i,j,:]>Col+bnd) or any(im[i,j,:]<Col-bnd))
mapim = abs(im[:,:,0]/255-1).astype(bool)
yval = array([average(yax[mapim[:,t]]) for t in range(ww)])
yval = 10**yval
rlt = interp(range(100),xax,yval)

Crop a pattern-based area of multiple images in python

I have a big number of screenshots that need to be cropped. All the images look similar - there is a rectangular window with blue border, containing some graphical elements inside. This window is contained inside another one but I need to crop only the inner window. Across all images the dimensions of the inner window are different and so is the content. The content in most cases includes elements with rectangular form and sometimes - blue border, the same border as the inner window. I am mentioning this because I am thinking of the following flow:
A script that goes through all images in the target directory. For each of them:
Find the area to be cropped (inner window)
Crop the area
Save the file
How can this be done? Python is not compulsory, can be any other too also.
It's not straightforward but this is a possible recipe:
import matplotlib.pyplot as plt
import numpy as np
def synthimage():
w,h = 300,200
im = np.random.randint(0,255,(w,h,3))/255
xa = np.random.randint(50,w-60)
xb = xa + np.random.randint(50,90)
ya = np.random.randint(50,h-60)
yb = ya + np.random.randint(20,50)
im[xa:xb,ya] = np.array([1,0,0])
im[xa:xb,yb] = np.array([1,0,0])
im[xa,ya:yb] = np.array([1,0,0])
im[xb,ya:yb] = np.array([1,0,0])
return im
def getRectPoints(im):
x,y = [],[]
for i in range(im.shape[0]):
for j in range(im.shape[1]):
if (im[i,j]-np.array([1,0,0])).sum()==0:
x.append(i)
y.append(j)
return np.array(x),np.array(y)
def denoise(x,y):
nx,ny = [],[]
for i in range(x.shape[0]):
d = np.sqrt((x[i]-x)**2+(y[i]-y)**2)
m = d<2
if len(m.nonzero()[0])>2:
nx.append(x[i])
ny.append(y[i])
return np.array(nx),np.array(ny)
im = synthimage()
plt.imshow(np.swapaxes(im,0,1),origin='lower',interpolation='nearest')
plt.show()
x,y = getRectPoints(im)
plt.scatter(x,y,c='red')
plt.xlim(0,300)
plt.ylim(0,200)
plt.show()
nx,ny = denoise(x,y)
plt.scatter(nx,ny,c='red')
plt.xlim(0,300)
plt.ylim(0,200)
plt.show()
#Assuming rectangle has no rotation (otherwise check Scipy ConveHull)
xmi = nx.min()
xma = nx.max()
ymi = ny.min()
yma = ny.max()
new = np.ones(im.shape)
new[xmi:xma,ymi:yma] = im[xmi:xma,ymi:yma]
plt.imshow(np.swapaxes(new,0,1),origin='lower',interpolation='nearest')
plt.show()
, the name of the functions should be self-explaining. Synthetic data was generated for the purpose of this exercise. The results are (in order):
Obviously each one of this steps can be changed depending on the requirements but this would be a functional solution for the majority of case-studies.

matplotlib: how to define lines with text and mark lines joints?

I am trying to recreate the following:
Any comments will be appreciated. I want to imitate this picture actually, but I have 3 problems:
How to get known the joints of two lines and the turning points of a line? Could these specific points be calculated from analytical calculations? or matplotlib could find out them?
How can I draw the dashed vertical line segment just below the line joint?
How to paste text to the segments of the lines? Could matplotlib determine the convenient location to write text attached to the lines ? or I should determine the location myself ?
For example, I can only draw such kind as below, far less than required. Please help me to improve my picture.
My own picture, which need improvements:
Code so far with detail code as below:
# -*- coding: utf-8 -*
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import math
from pylab import *
c = 2.998*10**10
hp = 6.626*10**-27
hb = 1.055*10**-27
kb = 1.381*10**-16
g = 6.673*10**-8
me = 9.109*10**-28
mp = 1.673*10**-24
q = 4.803*10**-10
sigT = 6.652*10**-25
p = 2.5
E52 = 1000
epsB_r = 0.01
epse_r = 0.1
D28 = 1
n1 = 1.0
nu15 = 2*10**(-3)
r014 = 1
g42 = 5
delt12 =1
g4 = g42*10**2
E0 = E52*10**52
eta = g4
N0 = E0/(g4*mp*c**2)
p_td = 24*3600
p_txd = 3**(1./3)*2**(-4./3)*10**(52./3)*pi**(-1./3)*mp**(-1./3)*c**(-5./3)/p_td
txd = p_txd*n1**(-1./3)*eta**(-8./3)*E52**(1./3)
p_Fmax_r1 = 2**(1./2)*3**(-1)*pi**(-1./2)*me*mp**(1./2)*c**3*sigT*q**(-1)*p_txd**(-3./2)*10**(-56)
Fmax_r1 = lambda t : p_Fmax_r1*N0*eta**6*E52**(-1./2)*n1*epsB_r**(1./2)*D28**(-2)*t**(3./2)
p_Fmax_r2 = 2**(1./2)*3**(-1)*pi**(-1./2)*me*mp**(1./2)*c**3*sigT*q**(-1)*p_txd**(34./35)*10**(-56)
Fmax_r2 = lambda t : p_Fmax_r2*N0*epsB_r**(1./2)*D28**(-2)*t**(-34./35)*eta**(-62./105)*n1**(37./210)*E52**(34./105)
p_nuc_r1 = 2**(-13./2)*3**2*me*mp**(-3./2)*c**(-2)*sigT**(-2)*pi**(-1./2)*q*p_td**(-2)
p_nuc_r2 = 2**(-13./2)*3**2*pi**(-1./2)*me*mp**(-3./2)*c**(-2)*sigT**(-2)*q*p_txd**(-74./35)*p_td**(-2)
nuc_r1 = lambda t : p_nuc_r1*eta**(-4)*epsB_r**(-3./2)*n1**(-3./2)*t**(-2)
nuc_r2 = lambda t : p_nuc_r2*eta**(172./105)*t**(4./35)*n1**(-167./210)*epsB_r**(-3./2)*E52**(-74./105)
p_num_r1 = 2**(11./2)*7**(-2)*mp**(5./2)*me**(-3)*pi**(-1./2)*q*p_txd**(-6)
p_num_r2 = 2**(11./2)*7**(-2)*mp**(5./2)*me**(-3)*pi**(-1./2)*q*p_txd**(54./35)
num_r1 = lambda t : p_num_r1*eta**18*((p-2)/(p-1))**2*epse_r**2*epsB_r**(1./2)*n1**(5./2)*t**6*E52**(-2)
num_r2 = lambda t : p_num_r2*((p-2)/(p-1))**2*n1**(-1./70)*eta**(-74./35)*E52**(18./35)*t**(-54./35)*epse_r**2*epsB_r**(1./2)
def num_r_(t):
return num_r1(t) if t<txd else num_r2(t)
num_r = np.vectorize(num_r_)
def nuc_r_(t):
return nuc_r1(t) if t<txd else nuc_r2(t)
nuc_r = np.vectorize(nuc_r_)
def Fmax_r_(t):
return Fmax_r1(t) if t<txd else Fmax_r2(t)
Fmax_r = np.vectorize(Fmax_r_)
i= np.arange(-5,-2,0.05)
t = 10**i
dnum = [math.log10(mmm) for mmm in num_r(t)]
dnuc = [math.log10(j) for j in nuc_r(t)]
nu_obs = [math.log(nu15*10**15,10) for a in i]
plt.figure('God Bless: Observable Limit')
plt.title(r'$\nu_{obs}$ and $\nu_c$ and $\nu_m$''\nComparation')
plt.xlabel('Time: log t')
plt.ylabel(r'log $\nu$')
plt.axvline(math.log10(txd))
plt.plot(i,nu_obs,'--',linewidth=2,label=r'$\nu_{obs}$')
plt.plot(i,dnum,'-.',linewidth=2,label=r'$\nu_m$')
plt.plot(i,dnuc,linewidth=2,label=r'$\nu_c$')
plt.savefig("test4.eps", dpi=120,bbox_inches='tight')
plt.legend()
plt.show()
I just find a solution, not certain whether there would be some better solution.
I took reference here: Annotate some points
I assumed the solution like this :
1, We can calculate the joint point coordination of lines.
2, If we want to plot a segment of a vertical line, i.e. the segment below the joint point, we can choose two points to draw a short line. That does work!
3, Maybe we can only to find a location of the illustrative text, and attach the text to that place.
I add such phrases :
plot([math.log10(txd),math.log10(txd)],[4,math.log10(nuc_r(txd))], color ='blue', linewidth=2.5, linestyle="--")
scatter([math.log10(txd),],[math.log10(nuc_r(txd))], 50, color ='blue')
annotate(r'$sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$',
xy=(math.log10(txd), math.log10(nuc_r(txd))), xycoords='data',
xytext=(+10, +30), textcoords='offset points', fontsize=16,
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2"))
And got the result as :
A Better One

Multiple plots on pdf with matplotlib

I've been fighting with pyplot for few days now. I want to return a pdf report with 4 samples on each page. 4 inline subplots for each: text with the name and some statistics, and 3 graphs of values vs time. I found a tutorial online and tried it (see below) but it gives nothing. the pdf is empty. I can't find where it is wrong.
Thank you in advance !
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.pyplot as plt
t=[n*5 for n in range(len(ratio))]
y_list_ratio=[[x*100/l[3]for x in l[2]]for l in hit_ratio]
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
pp = PdfPages('multipage.pdf')
# Generate the pages
nb_plots = len(hit_ratio)
nb_plots_per_page = 4
nb_pages = int(numpy.ceil(nb_plots / float(nb_plots_per_page)))
grid_size = (nb_plots_per_page, 4)
for i, elt in enumerate(hit_ratio):
# Create a figure instance (ie. a new page) if needed
if i % nb_plots_per_page == 0:
plt = plt.figure(figsize=(8.27, 11.69), dpi=100)
# Plot stuffs !
plt.subplot2grid(grid_size, (i % nb_plots_per_page, 0))
plt.text(0.5,0.5,"Puit Hauteur_pic Digitonine\n"+ \
str(elt[-1])+" "+str(elt[5])+" "+str(elt[6]),horizontalalignment='center',verticalalignment='center', bbox=props)
plt.subplot2grid(grid_size, (i % nb_plots_per_page, 1))
plt.plot(t,hit_norm[i][0])
plt.subplot2grid(grid_size, (i % nb_plots_per_page, 2))
plt.plot(t,y_list_ratio[i])
plt.subplot2grid(grid_size, (i % nb_plots_per_page, 3))
plt.plot(t,elt[7])
plt.plot(t,elt[8])
# Close the page if needed
if (i + 1) % nb_plots_per_page == 0 or (i + 1) == nb_plots:
fig2.tight_layout()
pp.savefig(fig2)
# Write the PDF document to the disk
pp.close()
Since you don't have any answers yet, I have an alternate suggestion:
Try ReportLab.
from reportlab.lib import colors, utils
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter, landscape
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Image, PageBreak, KeepTogether
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.lib.enums import TA_CENTER
from reportlab.platypus.paragraph import Paragraph
landscape_pic_width = 8
letter_pic_width = 6
....
def get_image(path, width=1*inch):
#'''returns an image for adding to report'''
img = utils.ImageReader(path)
iw, ih = img.getSize()
aspect = ih / float(iw)
return Image(path, width=width, height=(width * aspect))
def plot_stuff():
#whatever you want to plot, finish with saving the figure
elements = [] #this is a list that will contain the items to be included in the report
report_title = str(report_title)
c_table = Table(Conditions_Table_data)
c_table.setStyle(TableStyle([('ALIGN', (0,0),(-1,-1),'CENTER'), ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), ('BOX', (0,0),(-1,-1), 2, colors.blueviolet), ('SIZE', (0,0),(-1,-1), 10), ('SPAN',(-3,3),(-1,3))]))
#tells it how to format the table
#puts in the logo, the assigned report title, the entered report title, the conditions table, and the setup picture
elements.append(get_image(logo_picture, width = 9*inch))
elements.append(Paragraph(document_title, PS(name='Heading1', spaceAfter = 22, fontName = 'Times-Bold', fontSize = 18, alignment = TA_CENTER)))
#you can append items to the list "elements" with a for loop.
doc = SimpleDocTemplate(path_plus_title + ".pdf", pagesize=landscape(letter))
#creates the report. Will throw an error if the report exists and is already open. Otherwise, will generate a report
#this WILL overwrite an existing report with the same name. Timestamps being forced into the data file names help.
doc.build(elements)
There's definitely sections missing from this code...but these are the items I import ("inch", for instance, is a value for sizing that you multiply by the number of inches you want for that dimension)
You basically build a list of the items that go into your report pdf in the order they go in. For each element, there's some style setting that takes place. You can include text (Paragraphs), tables (it's a "list of lists" with each list being a row), and images.

Categories

Resources