I am creating a matplotlib animation to be displayed on a flask app based on user input. The matplotlib script is similar to this:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# Horizontal bar plot with gaps
fig, ax = plt.subplots()
ax.get_yaxis().set_visible(False)
ax.spines[['top', 'bottom','left','right']].set_visible(False)
y2=[20,20,20,20,20,20,20]
y3=np.array(y2) #convert to array wont work with list
x2 =[20,15,14,13, 12,11,10]
x3=np.array(x2)
year =["2014","2015","2016","2017","2018","2019","2020"]
yr2 =np.array(year)
def animate(i):
ax.clear()
ax.set_ylim(16, 24)
ax.barh(20, 60, 4 )
ax.plot(60, 18, marker=6, markersize=18, clip_on=False,)
ax.annotate(r"$\bf" + str(2013) +"$" + f" ({60})", (60 , 18),xytext=(0, -25), size= 8, textcoords='offset points', ha='center', va='bottom')
ax.barh(y3[i], x3[i], 4,color='c')
ax.plot(x3[i], y3[i]+2, color = 'c', marker=7, markersize=18, clip_on=False,)
ax.annotate(r"$\bf" + str(yr2[i]) +"$" + f" ({x3[i]})", (x3[i] , y3[i]+2),xytext=(0, 15), size= 8, color = 'c', textcoords='offset points', ha='center', va='bottom')
ani = animation.FuncAnimation(fig, animate, repeat=False,
frames=len(x3), interval=100000)
# To save the animation using Pillow as a gif
writer = animation.PillowWriter(fps=1,
metadata=dict(artist='Me'),
bitrate=1800)
ani.save('scatter.gif', writer=writer)
Is it possible to save gif into an in-memory file rather than saving as a gif?
tempfile
, load it into memory with io.BytesIO
, and delete the file.buf = io.BytesIO()
and ani.save(buf, writer=writer)
, because ani.save
does not accept BytesIO
as the path.python 3.9.18
, flask 2.2.2
, matplotlib 3.7.2
, numpy 1.21.5
.from flask import Flask, Response
import io
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import os
import tempfile
app = Flask(__name__)
@app.route("/")
def index():
return """
<html>
<body>
<img src="/gif" alt="animation">
</body>
</html>
"""
@app.route("/gif")
def gif():
# Generate the GIF data and store it in a BytesIO object (buf)
# Here you should call the function or code that generates the GIF and returns the BytesIO object.
# For example, you can call a function that generates the matplotlib animation and returns buf.
buf = generate_gif() # replace this with your code that generates the GIF
# Return the GIF data as a response with the correct content type
return Response(buf.getvalue(), content_type="image/gif")
def generate_gif():
# Horizontal bar plot with gaps
fig, ax = plt.subplots()
ax.get_yaxis().set_visible(False)
ax.spines[["top", "bottom", "left", "right"]].set_visible(False)
y2 = [20, 20, 20, 20, 20, 20, 20]
y3 = np.array(y2) # convert to array won't work with list
x2 = [20, 15, 14, 13, 12, 11, 10]
x3 = np.array(x2)
year = ["2014", "2015", "2016", "2017", "2018", "2019", "2020"]
yr2 = np.array(year)
def animate(i):
ax.clear()
ax.set_ylim(16, 24)
ax.barh(20, 60, 4)
ax.plot(60, 18, marker=6, markersize=18, clip_on=False,)
ax.annotate(r"$\bf" + str(2013) + "$" + f" ({60})", (60, 18), xytext=(0, -25), size=8, textcoords="offset points", ha="center", va="bottom",)
ax.barh(y3[i], x3[i], 4, color="c")
ax.plot(x3[i], y3[i] + 2, color="c", marker=7, markersize=18, clip_on=False,)
ax.annotate(r"$\bf" + str(yr2[i]) + "$" + f" ({x3[i]})", (x3[i], y3[i] + 2), xytext=(0, 15), size=8, color="c", textcoords="offset points", ha="center", va="bottom",)
ani = animation.FuncAnimation(fig, animate, repeat=False, frames=len(x3), interval=100000)
# Save the animation to a temporary file with a '.gif' suffix
with tempfile.NamedTemporaryFile(delete=False, suffix=".gif") as temp_file:
writer = animation.PillowWriter(fps=1, metadata=dict(artist="Me"), bitrate=1800)
ani.save(temp_file.name, writer=writer)
# Read the file contents into a BytesIO object
temp_file.seek(0)
buf = io.BytesIO(temp_file.read())
# Now buf contains the gif data, and you can use buf.getvalue() to access it.
# Don't forget to delete the temporary file
os.remove(temp_file.name)
return buf
if __name__ == "__main__":
app.run(debug=True)
Updates per comments from OP based on Given a BytesIO buffer, generate img tag in html.
temp_file.seek(0)
buf = io.BytesIO(temp_file.read())
gif = base64.b64encode(buf.getbuffer()).decode("ascii") # return gif
# on the html side:
<img src='data:image/png;base64,{{buf}}' class="responsiveImage"/>