How to animate plt.imshow with text? - python-3.9

I am trying to animate a simulation. I want to include the time of the simulation. I have written the following code:
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims = []
for i in range(40):
im=plt.imshow(np.log10(D[0,i,:,:]),cmap=plt.get_cmap("Spectral"),extent=[0,28,0,14],animated=True)
plt.text(10,2,"t="+str(t[i])+"Myr",c='w',fontsize='large')
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
ani.save("rhdjet1.mp4")
plt.show()
But all the text is getting dumped at once in the beginning.This is a still from the animation. The gibberish in white is the text getting overlayed.
How to correct this?

This can be done using the celluloid package in python:
from celluloid import Camera
#Creating matplotlib figure and camera object
fig = plt.figure()
camera = Camera(fig)
#Looping the data and capturing frame at each iteration
for i in range(0,90):
plt.imshow(K[i])
plt.text(str(t[i]))
camera.snap()
#Creating the animation from captured frames
animation = camera.animate(interval = 200, repeat = True,
repeat_delay = 500)
animation.save("restart.mp4")

Related

PySimpleGUI drawimage not displaying image

I have an RGB image, which I immediately take the red component. I then convert the resulting grayscale into bytes and display it in Graph using draw_image. However, only the background is shown and the red component image is not displayed. Let img be my RGB image. Here is my code:
import cv2
import PySimpleGUI as sg
from PIL import Image, ImageTk
r,g,b = cv2.split(img)
data = bytes(Image.fromarray(r).tobytes())
width = len(b)
length = len(b[0])
layout = [[sg.Graph(
canvas_size=(length, width),
graph_bottom_left=(0, 0),
graph_top_right=(length, width),
key="-GRAPH-",
change_submits=True,
background_color='black',
drag_submits=True) ]]
window = sg.Window(layout, finalize=True)
window.Maximize()
graph = window["-GRAPH-"]
graph.draw_image(data = data, location=(0,width))
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
The result is nothing but black background. I have checked that the image img and the red component r are both correct (i.e. statements like imshow will give the right image). The problem therefore lies in either the line data = bytes(Image.fromarray(r).tobytes()) or graph.draw_image(data = data, location=(0,width)). However, both seem correct to me. What am I missing? Is there any workarounds? As a side note, I am not allowed to save any images.
Image.tobytes(encoder_name='raw', *args)
This method returns the raw image data from the internal storage. For compressed image data (e.g. PNG, JPEG) use save(), with a BytesIO parameter for in-memory data.
import io
import cv2
import PySimpleGUI as sg
from PIL import Image, ImageTk
img = cv2.imread('D:/images.jpg')
r,g,b = cv2.split(img)
im = Image.fromarray(r)
width, height = im.size
buffer = io.BytesIO()
im.save(buffer, format='PNG')
data = buffer.getvalue()
layout = [[sg.Graph(
canvas_size=(width, height),
graph_bottom_left=(0, 0),
graph_top_right=(width, height),
key="-GRAPH-",
change_submits=True,
background_color='black',
drag_submits=True) ]]
window = sg.Window('Title', layout, finalize=True)
# window.Maximize()
graph = window["-GRAPH-"]
graph.draw_image(data = data, location=(0, height))
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
window.close()

How to generate a matplotlib animation using an image array?

So my problem is generating an animation from the list img_array. The code above that is basically used to get an image from the folder, annotate it and then save it into the array. Was wondering if anyone would have any suggestions on how to convert the images in the image array into an animation. Any help is appreciated! TIA.
I tried FFmepg and what not but none of them seem to work. I also tried videowriter in OpenCV but when I tried to open the file I get that this file type is not supported or corrupt.
import cv2
import numpy as np
import glob
import matplotlib.pyplot as plt
from skimage import io
import trackpy as tp
import pims
import pylab as pl
##########
pixel_min=23
min_mass=5000
Selector1=[1,2,3,4,5,6,7,11]
##########
frames = pims.ImageSequence('/Users/User/Desktop/eleventh_trial_2/*.tif', as_grey=True)
f1 = tp.locate(frames[0], pixel_min,minmass=min_mass)
plt.figure(1)
ax3=tp.annotate(f1,frames[0])
ax = plt.subplot()
ax.hist(f1['mass'], bins=20)
ax.set(xlabel='mass', ylabel='count');
f = tp.batch(frames[:], pixel_min, minmass=min_mass);
#f = tp.batch(frames[lower_frame:upper_frame], pixel, minmass=min_mass);
t=tp.link_df(f,10,memory=3)
##############
min_mass=8000#12000 #3000#2000 #6000#3000
pixel_min=23;
count=0
img_array = []
for filename in glob.glob('/Users/User/Desktop/eleventh_trial_2/*.tif'):
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
img2 = io.imread(filename, as_gray=True)
fig, ax = plt.subplots()
ax.imshow(img)
#ax=pl.text(T1[i,1]+13,T1[i,0],str(int(T1[i,9])),color="red",fontsize=18)
T1=t.loc[t['frame']==count]
T1=np.array(T1.sort_values(by='particle'))
for i in Selector1:
pl.text(T1[i,1]+13,T1[i,0],str(int(T1[i,9])),color="red",fontsize=18)
circle2 = plt.Circle((T1[i,1], T1[i,0]), 5, color='r', fill=False)
ax.add_artist(circle2)
count=count+1
img_array.append(fig)
ani = animation.ArtistAnimation(fig, img_array, interval=50, blit=True,repeat_delay=1000)
When I run this I don't get an an error however I can't save the ani as tried in the past either using OpenCV videoWriter.
I found a work around although not the most efficient one. I saved the figures in a separate directory using os and plt.savefig() and then use ImageJ to automatically convert the sequentially numbered and saved figures into an animation. It ain't efficient but gets the job done. I am still open to more efficient answers. Thanks

Overlaying a box on label image using Tkinter

I am using Tkinter and the grid() layout manager to create a GUI. I am showing the image in my GUI using a label, on a tabbed window:
label2 = ttk.Label(tab2)
image2 = PhotoImage(file="lizard.gif")
label2['image'] = image2
label2.grid(column=0, row=0, columnspan=3)
For illustration, let's say the image is 300 x 900. If I know a set of coordinates within the image, how can I overlay a shaded box on the image, defined by the known (A,B,C,D which are shown just for the illustration purpose) coordinates?
Let me give you a step by step solution.
You can use a tkinter.Label() to display your image as you did, you can also choose other widgets. But for situation, let's choose tkinter.Canvas() widget instead (but same reasoning is valid if you choose to use tkinter.Label())
Technical issues:
Your problem contains 2 main sub-problems to resolve:
How to overlay 2 images the way you want.
How to display an image using tkinter.Canvas()
To be able to read an image of jpg format , you need to use a specific PIL (or its Pillow fork) method and a class:
PIL.Image.open()
PIL.ImageTk.PhotoImage()
This is done by 3 lines in the below program:
self.im = Image.open(self.saved_image)
self.photo = ImageTk.PhotoImage(self.im)
And then display self.photo in the self.canvas widget we opted for:
self.canvas.create_image(0,0, anchor=tk.N+tk.W, image = self.photo)
Second, to reproduce the effect you desire, use cv2.addWeighted() OpenCV method. But I feel you have already done that. So I just show you the portion of code of the program that does it:
self.img = cv2.imread(self.image_to_read)
self.overlay = self.img.copy()
cv2.rectangle(self.overlay, (500,50), (400,100), (0, 255, 0), -1)
self.opacity = 0.4
cv2.addWeighted(self.overlay, self.opacity, self.img, 1 - self.opacity, 0, self.img)
cv2.imwrite( self.saved_image, self.img)
Program design:
I use 2 methods:
- __init__(): Prepare the frame and call the GUI initialization method.
- initialize_user_interface(): Draw the GUI and perform the previous operations.
But for scalability reasons, it is better to create a separate method to handle the different operations of the image.
Full program (OpenCV + tkinter)
Here is the source code (I used Python 3.4):
'''
Created on Apr 05, 2016
#author: Bill Begueradj
'''
import tkinter as tk
from PIL import Image, ImageTk
import cv2
import numpy as np
import PIL
class Begueradj(tk.Frame):
'''
classdocs
'''
def __init__(self, parent):
'''
Prepare the frame and call the GUI initialization method.
'''
tk.Frame.__init__(self, parent)
self.parent=parent
self.initialize_user_interface()
def initialize_user_interface(self):
"""Draw a user interface allowing the user to type
"""
self.parent.title("Bill BEGUERADJ: Image overlay with OpenCV + Tkinter")
self.parent.grid_rowconfigure(0,weight=1)
self.parent.grid_columnconfigure(0,weight=1)
self.image_to_read = 'begueradj.jpg'
self.saved_image = 'bill_begueradj.jpg'
self.img = cv2.imread(self.image_to_read)
self.overlay = self.img.copy()
cv2.rectangle(self.overlay, (500,50), (400,100), (0, 255, 0), -1)
self.opacity = 0.4
cv2.addWeighted(self.overlay, self.opacity, self.img, 1 - self.opacity, 0, self.img)
cv2.imwrite( self.saved_image, self.img)
self.im = Image.open(self.saved_image)
self.photo = ImageTk.PhotoImage(self.im)
self.canvas = tk.Canvas(self.parent, width = 580, height = 360)
self.canvas.grid(row = 0, column = 0)
self.canvas.create_image(0,0, anchor=tk.N+tk.W, image = self.photo)
def main():
root=tk.Tk()
d=Begueradj(root)
root.mainloop()
if __name__=="__main__":
main()
Demo:
This is a screenshot of the running program:
You will need to use a canvas widget. That will allow you to draw an image, and then overlay a rectangle on it.
Although the above answers were wonderfully in depth, they did not fit my exact situation (Specifically use of Python 2.7, etc.). However, this solution gave me exactly what I was looking for:
canvas = Canvas(tab2, width=875, height=400)
image2=PhotoImage(file='lizard.gif')
canvas.create_image(440,180,image=image2)
canvas.grid(column=0, row=0, columnspan=3)
The rectangle is added over the canvas using:
x1 = 3, y1 = 10, x2 = 30, y2 = 20
canvas.create_rectangle(x1, y1, x2, y2, fill="blue", stipple="gray12")
stipple comes from this example, to help add transparency to the rectangle.

Matplotlib Subplot into Tkinter GUI window

I'm working on a Tkinter GUI and I was wondering if it was possible to put a Subplot into a Tkinter GUI. Any help would be appreciated as I currently have no idea.
import pandas.io.data as web
import matplotlib.pyplot as plt
import datetime
start = datetime.datetime(2010, 1, 1)
end = datetime.datetime(2014, 8, 3)
google = web.DataReader("GOOG", 'yahoo', start, end )
ax1 = plt.subplot2grid((4,4), (0,0), colspan=4)
ax2 = plt.subplot2grid((4,4), (1,0), colspan=2)
top = plt.subplot2grid((4,4), (0, 0), rowspan=3, colspan=4)
top.plot(google.index, google["Close"])
plt.title('Google Stock Price from 2007 - 2012')
bottom = plt.subplot2grid((4,4), (3,0), rowspan=1, colspan=4)
bottom.bar(google.index, google['Volume'])
plt.title('Google Trading Volume in Millions')
plt.gcf().set_size_inches(15,8)
plt.show()
I'm working with something around this, but I haven't been able to place it into the GUI without it being an entirely separate window.
self.root2= Tk()
self.root2.geometry("600x400")
self.root2.title("Stock Visualization")
frame = Frame(self.root2)
frame.grid(row=2,column=0, sticky="s")
frame2 = Frame(self.root2)
frame2.grid(row=0,column=0, sticky = "n")
## self.canvas=Canvas(self.root2, width=300, height=300, background='white')
## self.canvas.grid(row=1,column=0, columnspan = 4)
This is part of the frame, without all the labels and such around. I have that Canvas commented out where I would want the Subplot to go.
This might be a little late, but you should try something along the lines of this
fig = Figure(figsize=(5,4), dpi = 100)
ax = fig.add_subplot(111)
figcanvas = FigureCanvasTkAgg(fig, master = root)
plotthis(figcanvas, ax)#in this example plotthis is a function that plots the figure, ax being a graph, and figcanvas being the canvas of which the graph is being plotted to.
figcanvas.get_tk_widget().grid()
Just as a little heads up, I don't think this method of doing this works in 3 yet, so as of now, I would just do it in 2 if at all possible. No clue as to why it doesn't work in 3. Hope this helps :)

Setting correct limits with imshow if image data shape changes

I have a 3D array, of which the first two dimensions are spatial, so say (x,y). The third dimension contains point-specific information.
print H.shape # --> (200, 480, 640) spatial extents (200,480)
Now, by selecting a certain plane in the third dimension, I can display an image with
imdat = H[:,:,100] # shape (200, 480)
img = ax.imshow(imdat, cmap='jet',vmin=imdat.min(),vmax=imdat.max(), animated=True, aspect='equal')
I want to now rotate the cube, so that I switch from (x,y) to (y,x).
H = np.rot90(H) # could also use H.swapaxes(0,1) or H.transpose((1,0,2))
print H.shape # --> (480, 200, 640)
Now, when I call:
imdat = H[:,:,100] # shape (480,200)
img.set_data(imdat)
ax.relim()
ax.autoscale_view(tight=True)
I get weird behavior. The image along the rows displays the data till 200th row, and then it is black until the end of the y-axis (480). The x-axis extends from 0 to 200 and shows the rotated data. Now on, another rotation by 90-degrees, the image displays correctly (just rotated 180 degrees of course)
It seems to me like after rotating the data, the axis limits, (or image extents?) or something is not refreshing correctly. Can somebody help?
PS: to indulge in bad hacking, I also tried to regenerate a new image (by calling ax.imshow) after each rotation, but I still get the same behavior.
Below I include a solution to your problem. The method resetExtent uses the data and the image to explicitly set the extent to the desired values. Hopefully I correctly emulated the intended outcome.
import matplotlib.pyplot as plt
import numpy as np
def resetExtent(data,im):
"""
Using the data and axes from an AxesImage, im, force the extent and
axis values to match shape of data.
"""
ax = im.get_axes()
dataShape = data.shape
if im.origin == 'upper':
im.set_extent((-0.5,dataShape[0]-.5,dataShape[1]-.5,-.5))
ax.set_xlim((-0.5,dataShape[0]-.5))
ax.set_ylim((dataShape[1]-.5,-.5))
else:
im.set_extent((-0.5,dataShape[0]-.5,-.5,dataShape[1]-.5))
ax.set_xlim((-0.5,dataShape[0]-.5))
ax.set_ylim((-.5,dataShape[1]-.5))
def main():
fig = plt.gcf()
ax = fig.gca()
H = np.zeros((200,480,10))
# make distinguishing corner of data
H[100:,...] = 1
H[100:,240:,:] = 2
imdat = H[:,:,5]
datShape = imdat.shape
im = ax.imshow(imdat,cmap='jet',vmin=imdat.min(),
vmax=imdat.max(),animated=True,
aspect='equal',
# origin='lower'
)
resetExtent(imdat,im)
fig.savefig("img1.png")
H = np.rot90(H)
imdat = H[:,:,0]
im.set_data(imdat)
resetExtent(imdat,im)
fig.savefig("img2.png")
if __name__ == '__main__':
main()
This script produces two images:
First un-rotated:
Then rotated:
I thought just explicitly calling set_extent would do everything resetExtent does, because it should adjust the axes limits if 'autoscle' is True. But for some unknown reason, calling set_extent alone does not do the job.

Resources