I am setting up a webserver with flask and seaborn/matplotlib. There I am creating and assigning a variable in Python img_buffer of type IOBytes to represent a graph.
Then I am transforming this data into an str img_tag:
...
import base64
str_equivalent_image = base64.b64encode(img_buffer.getvalue()).decode()
img_tag = "<img src='data:image/png;base64," + str_equivalent_image + "'/>"
Now I want to use this img_tag in the corresponding index.html to display the image. How to I do that? The following did not work:
...
<body>
<h1>Graph</h1>
{img_tag}
</body>
</html>
This is the code I used. It is reduced as much as possible. The page is updated every 0,5s. Later the graphic should be updated as well on the basis of dynamic data I read out from a URL, but this is not yet implemented:
server.py:
from gevent import monkey; monkey.patch_all()
from flask import Flask, Response, render_template, stream_with_context, url_for, send_file, render_template
from gevent.pywsgi import WSGIServer
import json
import time
import io
import base64
from requests.exceptions import HTTPError
import matplotlib as mpl
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import matplotlib.pyplot as plt
import seaborn as sns
fig,ax=plt.subplots(figsize=(6,6))
ax=sns.set(style="darkgrid")
x=[i for i in range(100)]
y=[i for i in range(100)]
app = Flask(__name__)
counter = 100
#app.route("/")
def render_index():
return render_template("index.html")
#app.route('/getVisualizeData')
def getVisualizeData():
sns.lineplot(x,y)
canvas=FigureCanvas(fig)
img = io.BytesIO()
fig.savefig(img)
img.seek(0)
return img
#app.route("/listen")
def listen():
def respond_to_client():
while True:
img_buffer=getVisualizeData()
str_equivalent_image = base64.b64encode(img_buffer.getvalue()).decode()
img_tag = "<img src='data:image/png;base64," + str_equivalent_image + "'/>"
_data = json.dumps({"img_tag":img_tag})
yield f"id: 1\ndata: {_data}\nevent: online\n\n"
time.sleep(0.5)
return Response(respond_to_client(), mimetype='text/event-stream')
if __name__ == "__main__":
http_server = WSGIServer(("localhost", 8080), app)
http_server.serve_forever()
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>APP</title>
</head>
<body>
<h1>Graph</h1>
{img_tag}
<script>
var eventSource = new EventSource("/listen")
eventSource.addEventListener("message", function(e) {
console.log(e.data)
}, false)
eventSource.addEventListener("online", function(e) {
data = JSON.parse(e.data)
document.querySelector("#img_tag").innerText = data.img_tag
}, true)
</script>
</body>
</html>
Try using double brackets
<body>
<h1>Graph<h1>
{{img_tag}}
</body>
Related
I am trying to generate a map with data to be displayed in a flask application. All that is displayed when I run the app is Figure(id='1021', ...). I also get this error in console: [Error] Failed to load resource: the server responded with a status of 403 () (bokeh.min.js.map, line 0). Any Ideas?
app.py
from flask import Flask, render_template
import geoviews as gv
import project_4 as map
import holoviews as hv
import geoviews.tile_sources as gvts
import pandas as pd
from bokeh.models import HoverTool
from geoviews import dim, opts
import numpy as np
app = Flask(__name__)
#app.route("/")
def index():
# Load the data and create the GeoViews points
college_data = pd.read_csv('updated_locations.csv')
college_gv_points = gv.Points(college_data, ['longitude', 'latitude'], ['university', 'num_students_attended'])
# Create the hover tool
tooltips = [('Unviversity', '#university'),
('Attendees', '#num_students_attended'),]
hover = HoverTool(tooltips=tooltips)
# Create the light and dark plots
light_plot = (gvts.CartoLight * college_gv_points).opts(
opts.Points(alpha=0.3,
hover_line_color='black',
color = 'blue', line_color='black', xaxis=None, yaxis=None,
tools=[hover],size=np.sqrt(dim('num_students_attended'))*2,
hover_fill_color='blue', hover_fill_alpha=0.2))
dark_plot = (gvts.CartoDark.options(alpha=0.8) * college_gv_points).opts(
opts.Points(alpha=0.6,
hover_line_color='black',
color = 'orange', line_color=None, xaxis=None, yaxis=None,
tools=[hover],size=np.sqrt(dim('num_students_attended'))*2,
hover_fill_color='orange', hover_fill_alpha=0.4))
# Render the map using Holoviews
hv_points = hv.render(dark_plot)
return render_template('index.html', plot=hv_points)
if __name__ == '__main__':
app.run(port=8000, debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<link
href="http://cdn.pydata.org/bokeh/release/bokeh-1.3.4.min.css"
rel="stylesheet"
type="text/css"
/>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.3.4.min.js"></script>
</head>
<body>
{{ plot | safe }}
</body>
</html>
I am attempting to make an app where a user can upload a CSV file of data and view the data thru the browser on a different flask route which would also a show the plot of the data.
I having issues in my code trying to show the plot of the data. (referencing the static file .png) I can get the pandas dataframe to HTML to work, but in my table.html file I am trying to reference a png plot created with matplot lib and saved to a static directory.
<img src="{{url_for('static', filename=filename)}}" />
All of this experimenting is due to cache issues with the browser so I am creating a unique filename with next_file_name function, and I think this is where I am getting screwed up in the HTML & Jinja trying to reference this unique file name.. I am hoping that a unique filename may be a fix for the cache issues I am observing. The png files are saving properly with the function plot0, plot1, plot2, plot3, etc...
I was hoping to be able to create something where I can repeat the process over & over of analyzing new data and getting retrieving a fresh new plot of the data. Any tips help, thanks
from flask import Flask, make_response, request, render_template
from werkzeug.utils import secure_filename
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import time
app = Flask(__name__, static_url_path='/static')
num = 0
def next_file_name(num):
return 'static/plot%d.png' % num
#app.route('/')
def form():
return render_template('form.html')
#app.route('/transform', methods=["POST"])
def transform_view():
global num
f = request.files['data_file']
filename = secure_filename(f.filename)
f.save(filename)
df = pd.read_csv(filename, index_col='Date', parse_dates=True)
OAT = pd.Series(df['OAT'])
RAT = pd.Series(df['RAT'])
MAT = pd.Series(df['MAT'])
df_OATrat = (OAT - RAT)
df_MATrat = (MAT - RAT)
plt.scatter(df_OATrat,df_MATrat, color='grey', marker='+')
plt.xlabel('OAT-RAT')
plt.ylabel('MAT-RAT')
plt.title('Economizer Diagnostics')
plt.plot([0,-18],[0,-18], color='green', label='100% OSA during ideal conditions')
plt.plot([0,20],[0,5], color='red', label='Minimum OSA in cooling mode')
plt.plot([0,-38],[0,-9.5], color='blue', label='Minimum OSA in heating mode')
plt.plot([0,0],[-20,10], color='black')
plt.plot([-30,20],[0,0], color='black')
#plt.legend()
plt.text(-3, -28, time.ctime(), fontsize=9)
pic = next_file_name(num)
plt.savefig(pic)
num+=1
resp = make_response(render_template('table.html', tables=[df.to_html(classes='data')], titles=df.columns.values, filename='pic'))
resp.cache_control.no_cache = True
return resp
if __name__ == '__main__':
app.run(debug=True)
table.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
<title>Title</title>
</head>
<body>
<h1>Economizer Data Plot</h1>
<img src="{{url_for('static', filename=filename)}}" />
</form>
{% for table in tables %}
{{ table|safe }}
{% endfor %}
</body>
</html>
You made pic a string instead of using it's value
... filename=pic
If it's a caching issue, it would be easier to either add automatic cache busting to your image files or disable cache with an after-request callback, which triggers the execution of a function at the end of a request.
#app.after_request
def add_header(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
response.headers['Cache-Control'] = 'public, max-age=0'
return response
See this solution for more information.
I am trying to display the plotted figure using the web browser. But the render template in return statement is throwing error. Below is the code used and the error received.
The python code:
from flask import Flask, render_template, Response
import io
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
app = Flask(__name__,template_folder = 'D:/DATA_SETS')
#app.route('/html_example')
def myplot():
with app.app_context():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig('D:/DATA_SETS/new_plot.png')
return Response(render_template('template_1.html', name = 'new_plot', url ='D:/DATA_SETS/new_plot.png'))
f = myplot()
if 1 == 1:
app.run(debug=True)
The template template_1.html
<!doctype html>
<html>
<body>
<h1>Performance</h1>
<p>{{ name }}</p>
<img src={{ url}} alt="Chart" height="42" width="42">
</body>
</html>
Expected : The image return from function should be displayed in the HTML format written.
Current : The webpage is displaying 'Not Found Error'.
Webpage looking at is 'http://localhost:5000/html_example'
You need to wrap your code into with app.app_context function. Your code will be similar to this:
app = Flask(__name__,template_folder = 'templates')
#app.route('/')
def myplot():
with app.app_context():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig('D:/DATA_SETS/new_plot.png')
return Response(render_template('template_1.html', name = 'new_plot',
url ='D:/DATA_SETS/new_plot.png'))
if __name__ == '__main__':
app.run(debug=True)
The problem is in the return statement. It should be
return render_template('template_1.html', name = 'new_plot', url ='D:/DATA_SETS/new_plot.png'))
and you should not be doing
f= myplot()
for producing the image.
when you will hit the URL localhost:5000/ it will automatically do the process of producing image and serve it to frontend.
Update:
Directory Structure:
main.py
from flask import Flask, render_template, Response
import io
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
app = Flask(__name__,static_url_path="/folder/",static_folder="folder")
#app.route('/')
def myplot():
plt.figure()
t = range(0,6,1)
x= np.sin(t)
plt.plot(t,x)
plt.savefig(app.static_folder+'/'+'new_plot.png')
return render_template('template_1.html', name = 'new_plot', filename ='new_plot.png')
if __name__ == "__main__":
app.run(debug=True)
template_1.html
<!doctype html>
<html>
<body>
<h1>Performance</h1>
<p>{{ name }}</p>
<img src="{{ url_for('static', filename=filename) }}" alt="Chart" height="100" width="100">
</body>
</html>
Output:
I have created a face recognition model using keras and tensorflow, and now I am trying to convert it as a web application using flask and python. My requirement is that, I need a live webcam displayed on the webpage and by clicking on a button it should take the picture and save it to a specified directory, and using that picture the application should recognize the person. if the person is not found in the dataset then a message should be displayed over the webpage that unknown identity is been found. To do this job I have started learning flask and after that when it comes to the requirement it was very difficult for me. somebody help me out to solve this situation.
What you want to do is streaming with Flask by using the webcam Stream and handle it with Machine Learning. Your main script for the web server in flask will allow you to load your index.html file and then Stream each frame through the /video_feed path:
from flask import Flask, render_template, Response, jsonify
from camera import VideoCamera
import cv2
app = Flask(__name__)
video_stream = VideoCamera()
#app.route('/')
def index():
return render_template('index.html')
def gen(camera):
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
#app.route('/video_feed')
def video_feed():
return Response(gen(video_stream),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='127.0.0.1', debug=True,port="5000")
Then you need the VideoCamera class in wich you will handle each frame and where you can make every prediction or processing you want on the frames. The camera.py file :
class VideoCamera(object):
def __init__(self):
self.video = cv2.VideoCapture(0)
def __del__(self):
self.video.release()
def get_frame(self):
ret, frame = self.video.read()
# DO WHAT YOU WANT WITH TENSORFLOW / KERAS AND OPENCV
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
And finally the page showing the video Stream in the html file index.html (in the templates/ folder, if not exist generate it) :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Video Stream</title>
</head>
<body>
<img src="{{ url_for('video_feed') }}" />
</body>
</html>
from flask import Flask,request,jsonify
import numpy as np
import cv2
import tensorflow as tf
import base64
app = Flask(__name__)
graph = tf.get_default_graph()
#app.route('/')
def hello_world():
return """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<video id="video" width="640" height="480" autoplay></video>
<button id="snap">Snap Photo</button>
<canvas id="canvas" width="640" height="480"></canvas>
</body>
<script>
var video = document.getElementById('video');
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true }).then(function(stream) {
//video.src = window.URL.createObjectURL(stream);
video.srcObject = stream;
video.play();
});
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var video = document.getElementById('video');
// Trigger photo take
document.getElementById("snap").addEventListener("click", function() {
context.drawImage(video, 0, 0, 640, 480);
var request = new XMLHttpRequest();
request.open('POST', '/submit?image=' + video.toString('base64'), true);
request.send();
});
</script>
</html>
"""
# HtmlVideoElement
#app.route('/test',methods=['GET'])
def test():
return "hello world!"
#app.route('/submit',methods=['POST'])
def submit():
image = request.args.get('image')
print(type(image))
return ""`
i have done like this, but the problem is that, when calling the API /submit in decorator, i get my image stored as HTMLVideoElement when print the type of image variable, I dont know how to convert it into Jpeg format and use it for further purpose.
Following this tutorial, I am trying to visualise a dataset using Holoviews instead of Bokeh (sample data available here as a CSV file), serving the results using Flask. I decided to use Flask and not Bokeh Server because I am building a larger workflow using the former.
My code is the following:
from flask import Flask, render_template, request
import numpy as np
import pandas as pd
from datetime import datetime
from bokeh.embed import components
from bokeh.io import curdoc
import holoviews as hv
hv.extension("bokeh")
app = Flask(__name__)
renderer = hv.renderer('bokeh')
infile = "./uploads/test.csv"
def loadRegionData(regionProperty, **kwargs):
df = pd.read_csv(infile, parse_dates=['Datetime'])
df1 = df[regionProperty]
df = pd.concat([df['Datetime'],df1], axis=1)
return hv.Curve(df)
colNames = ((pd.read_csv(infile, nrows=1)).drop(['Datetime'], axis=1)).columns.values
dmap = hv.DynamicMap(loadRegionData, kdims='RegionProperty').redim.values(RegionProperty=colNames)
hvplot = renderer.get_plot(dmap)
plot = hvplot.state
plot.name = 'plot'
curdoc().add_root(plot)
#app.route("/")
def index():
# Embed plot into HTML via Flask Render
script, div = components(plot)
return render_template("index.html", script=script, div=div)
if __name__ == '__main__':
app.run(port=5000, debug=True)
I am running into the following (unrelated issues)
When I deploy using Flask, the dropdowns to select the columns do not appear. I suspect that is because I am not returning/referring to the correct variables from the index() function into my index.html:
<html>
<head>
<link
href="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.2.min.css"
rel="stylesheet" type="text/css">
<link
href="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.2.min.css"
rel="stylesheet" type="text/css">
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-1.0.2.min.js"></script>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-widgets-1.0.2.min.js"></script>
</head>
<body>
<h1>Holoview test</h1>
{{ script|safe }}
{{ div|safe }}
</body>
</html>
How can I get Flask to also show the dropdown selector?
An unrelated issue which I found when I tested this app using Bokeh Server, and which could also arise in the Flask implementation, is that the scales do not adjust dynamically based on my column selection. Perhaps this can go as a separate question on SO, but I thought to include it here for now to keep things together.