With the current code, I can only send the video frame to the webpage. How can I also send some text along with each frame and have it displayed.
FastAPI code
def generate_frames(cap,i):
while True:
success,frame = cap.read()
if not success:
break
else:
# Reshape image
im_arr = cv2.imencode('.jpg', frame)[1]
cv2.waitKey(50)
print(loa[i]) //text to be displayed along with image
i = i + 1
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + bytearray(im_arr) + b'\r\n')
#app.get('/video')
def video():
i = 0
cap = cv2.VideoCapture('C:\\Users\\ryanv_k78mbsh\\Desktop\\FINAL\\MovenetTest\\animation.gif')
return StreamingResponse(generate_frames(cap,i),media_type = 'multipart/x-mixed-replace; boundary=frame')
HTML code that receives and displays the video frame
<div style= "height:50px"></div>
<img src ="{{ url_for('video') }}" width="50%" />
</div>
You could use WebSockets instead, as described in this answer (Option 2), and send both the text and image bytes to the frontend. Example below:
app.py
from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from fastapi.templating import Jinja2Templates
import uvicorn
import cv2
app = FastAPI()
camera = cv2.VideoCapture(0,cv2.CAP_DSHOW)
templates = Jinja2Templates(directory="templates")
#app.get('/')
def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
#app.websocket("/ws")
async def get_stream(websocket: WebSocket):
await websocket.accept()
try:
while True:
success, frame = camera.read()
if not success:
break
else:
ret, buffer = cv2.imencode('.jpg', frame)
await websocket.send_text("some text")
await websocket.send_bytes(buffer.tobytes())
except WebSocketDisconnect:
print("Client disconnected")
if __name__ == '__main__':
uvicorn.run(app, host='127.0.0.1', port=8000)
templates/index.html
<!DOCTYPE html>
<html>
<head>
<title>Live Streaming</title>
</head>
<body>
<img id="frame" src="">
<div id="textArea"></div>
<script>
let ws = new WebSocket("ws://localhost:8000/ws");
let image = document.getElementById("frame");
image.onload = function(){
URL.revokeObjectURL(this.src); // release the blob URL once the image is loaded
}
ws.onmessage = function(event) {
if (typeof event.data === 'string')
document.getElementById("textArea").innerHTML = event.data;
else
image.src = URL.createObjectURL(event.data);
};
</script>
</body>
</html>
Related
I am working on an University project about UAV's. I need to send the live webcam video over a web server and display that video on a HTML page. I choose Flask since i use python a lot. I am struggling with this error on SERVER SIDE. Even ChatGPT couldn't help me. Open to all suggestions. Thank you for your time.
Error:
img = cv2.imdecode(np.frombuffer(request.data, np.uint8), cv2.IMREAD_COLOR)
cv2.error: OpenCV(4.7.0) /io/opencv/modules/imgcodecs/src/loadsave.cpp:798: error:
(-215:Assertion failed) !buf.empty() in function 'imdecode_'
Server side code:
from flask import Flask, request, render_template
import cv2
import numpy as np
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed', methods=['POST'])
def video_feed():
img = cv2.imdecode(np.frombuffer(request.data, np.uint8), cv2.IMREAD_COLOR)
# process the video frame here
# ...
ret, jpeg = cv2.imencode('.jpg', img)
return jpeg.tobytes()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
HTML Page:
<!DOCTYPE html>
<html>
<head>
<title>Video Streaming Demonstration</title>
</head>
<body>
<h1>Video Streaming Demonstration</h1>
<img id="video" src="{{ url_for('video_feed') }}" width="640" height="480">
<script>
function updateVideo() {
var xhr = new XMLHttpRequest();
xhr.open('POST', '{{ url_for("video_feed") }}');
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status === 200) {
var objectURL = URL.createObjectURL(this.response);
document.getElementById('video').src = objectURL;
}
updateVideo();
};
xhr.send();
}
updateVideo();
</script>
</body>
</html>
Client side code:
import cv2
import requests
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if ret:
# Check if the frame has non-zero width and height
if frame.shape[0] > 0 and frame.shape[1] > 0:
# Send the frame to the server as a POST request
response = requests.post('http://localhost:5000/video_feed', data=frame.tobytes())
else:
print("Invalid frame size: ", frame.shape)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I am using opencv and pyzbar to make a barcode scanner that can work in a flask app.
There is a while True loop which constantly outputs the current camera frame to 127.0.0.1:5000/scanner, whilst simultaneously attempting to decode a barcode from the image. If it decodes a barcode, the loop breaks and the program redirects to 127.0.0.1:5000/output/, where the barcode number is displayed - except it doesn't for some reason. 127.0.0.1:5000/output/ displays correctly if it is entered manually.
Python:
from flask import Flask, render_template, Response, redirect, url_for
import io
import cv2
import sys
from pyzbar import pyzbar
app = Flask(__name__)
#app.route('/output/')
def output():
return str(output)
#app.route('/scanner')
def index():
return render_template('scanner.html')
def gen():
vc = cv2.VideoCapture(0)
while True:
read_return_code, frame = vc.read()
og = frame
cv2.rectangle(frame,(200,170),(450,330),(0,255,100),7)
frame = cv2.flip(frame, 1)
decode = pyzbar.decode(og)
if len(str(decode)) > 10:
global output
output = str(decode).split("'")
output = output[1]
vc.release()
return redirect('/output/') #THE PROBLEM
break
if cv2.waitKey(1) & 0xFF == ord('q'):
break
encode_return_code, image_buffer = cv2.imencode('.jpg', frame)
io_buf = io.BytesIO(image_buffer)
print("a", file=sys.stderr)
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + io_buf.read() + b'\r\n')
#app.route('/video_feed')
def video_feed():
return Response(
gen(),
mimetype='multipart/x-mixed-replace; boundary=frame'
)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, threaded=False)
HTML:
<html>
<head>
<style>
body{
font-family: Verdana, sans-serif;
}
</style>
<title>Scanner</title>
</head>
<body>
<h1>Scanner</h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
you return redirect without parameters, this is equal to just requesting it via URL.
your 'output' route doesn't take any input params.
You should change your code to something like this, where 'data' is the output of your cv:
app.route('/output/<data>')
def output(data):
return str(data)
app.route('/scanner')
def index():
data='whatever'
return redirect(url_for('output', data=data))
I'm trying to display 4 streams (2 streams works) in my opencv flask app but after displaying a frame or two, all 4 streams gets stuck, I can't figure out what I'm doing wrong and it's been couple of days. I have provided the necessary code below, Please have a look:
Python OpenCV Flask App:
cap1 = cv2.VideoCapture('1.avi')
cap2 = cv2.VideoCapture('2.avi')
cap3 = cv2.VideoCapture('3.avi')
cap4 = cv2.VideoCapture('4.avi')
def cap1_gen():
while True:
ret, frame = cap1.read()
if not ret:
break
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n'+frame+b'\r\n')
def cap2_gen():
while True:
ret, frame = cap1.read()
if not ret:
break
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n'+frame+b'\r\n')
def cap3_gen():
while True:
ret, frame = cap1.read()
if not ret:
break
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n'+frame+b'\r\n')
def cap4_gen():
while True:
ret, frame = cap1.read()
if not ret:
break
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n'+frame+b'\r\n')
#app.route('/stream1')
def stream_cap1():
return Response(cap1_gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/stream2')
def stream_cap2():
return Response(cap2_gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/stream3')
def stream_cap3():
return Response(cap3_gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/stream4')
def stream_cap4():
return Response(cap4_gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/', methods=["GET"])
def get_mongo_stream():
if request.method == "GET":
data = [x for x in mongo.db.plates.find({})]
return render_template('index.html', data=data)
if __name__ == "__main__":
app.run(debug=True)
In index.html:
<h1>Display Streams</h1>
<div id="cam_feed_container">
<section>
<div><img class="cam_feed" src="{{ url_for('stream_cap1') }}" width="100%" height="100%" /></div>
<div><img class="cam_feed" src="{{ url_for('stream_cap2') }}" width="100%" height="100%" /></div>
<div><img class="cam_feed" src="{{ url_for('stream_cap3') }}" width="100%" height="100%" /></div>
<div><img class="cam_feed" src="{{ url_for('stream_cap4') }}" width="100%" height="100%" /></div>
</section>
When I display only 1 or 2 streams, it shows the video running smooth but when I add more, like total 3 or 4 then the video get stuck. Can anyone help provide some solution to my problem!
I'm currently writing a program that uses webRTC, Flask, and openCV etc... to analyze a webcam video in real time.
I succeeded in sending the webcam video from JavaScript to the Flask server, but I cannot display the image after analysis from Flask to HTML.
For example, I tried to save the image file once.
#app.route("/img", methods=["POST"])
def img():
img = cv2.imdecode(np.fromstring(request.files['video'].read(), np.uint8), cv2.IMREAD_UNCHANGED)
#Processing by openCV...
cv2.imwrite("test.jpg", img)
return "success"
#app.route('/feed')
def feed():
return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
def gen():
while True:
with open('test.jpg', 'rb') as f:
img = f.read()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + img + b'\r\n')
Next is the HTML code
<img id="cap-src" src="{{ url_for('feed') }}">
However, this code curiously didn't update the image displayed in HTML, so I gave up.
Then I tried the method of not saving the image.
#app.route("/img", methods=["POST"])
def img():
global img
img = cv2.imdecode(np.fromstring(request.files['video'].read(), np.uint8), cv2.IMREAD_UNCHANGED)
def generate_img():
#Processing by openCV...
#app.route('/feed')
def feed():
return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
def gen():
while True:
img = generate_img()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + img + b'\r\n')
The HTML code does not change.
However, this method did not update the image displayed in HTML.
Is there any good way?
And I want to use a method that doesn't save the image if possible.Thank you.
Finally, I put the summarized code.
This is the HTML code
<html>
<head>
</head>
<body>
<img id="img">
<video id="myvideo" width="720px" autoplay></video>
<button id="start"></button>
<canvas id="videocanvas"></canvas>
<img id="cap-src" src="">
<script src="https://code.jquery.com/jquery-1.12.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script>
$(function(){
const constraints = window.constraints = {
audio: false,
video: {
facingMode: "environment"
}
};
async function init() {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const video = document.querySelector('#myvideo');
const videoTracks = stream.getVideoTracks();
window.stream = stream;
video.srcObject = stream;
e.target.disabled = true;
} catch{
$('#errorMsg').text('error');
}
}
$('#start').click(init);
var canvas = $('#videocanvas')[0];
$('#myvideo').on('loadedmetadata', function(){
var video = $('#myvideo')[0];
var width = canvas.width = video.videoWidth;
var height = canvas.height = video.videoHeight;
var ctx = canvas.getContext("2d");
var fd = new FormData();
fd.append('video', null);
setInterval(function(){
ctx.drawImage(video, 0, 0, width, height);
canvas.toBlob(function(blob){
fd.set('video', blob);
$.ajax({
url: "/img",
type : "POST",
processData: false,
contentType: false,
data : fd,
dataType: "text",
})
.done(function(data){
console.log(data);
})
.fail(function(data){
console.log(data);
});
}, 'image/jpeg');
},100);
});
});
document.getElementById("start").onclick = function() {
document.getElementById("cap-src").src = "{{ url_for('feed') }}";
}
</script>
</body>
</html>
And the python code.
from flask import Flask, render_template, Response, request, jsonify
import cv2
import numpy as np
from PIL import Image
#app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
response.headers.add('Access-Control-Allow-Methods', 'GET,POST') # Put any other methods you need here
return response
#app.route('/')
def index():
return render_template('index.html')
#app.route("/img", methods=["POST"])
def img():
img = cv2.imdecode(np.fromstring(request.files['video'].read(), np.uint8), cv2.IMREAD_UNCHANGED)
image = cv2.resize(img, (480, 300))
cv2.imwrite("test.jpg", image)
ret, jpeg = cv2.imencode('.jpg', image)
jpeg = jpeg.tobytes()
return "success"
#api.route('/feed')
def feed():
return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
def gen():
while True:
with open('./templates/dst/test.jpg', 'rb') as f:
img = f.read()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + img + b'\r\n')
Since the issue you mentioned is about "displaying the image after analysis from Flask to HTML", so here is the solution to your problem, it can send video or camera output images to the html. For more details follow this tutorial
The main python app (which will run on the server) file is here:
app.py
from flask import Flask, render_template, request,Response
import cv2,imutils,time
import pyshine as ps
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
def changeBrightness(img,value):
""" This function will take an image (img) and the brightness
value. It will perform the brightness change using OpenCv
and after split, will merge the img and return it.
"""
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
lim = 255 - value
v[v>lim] = 255
v[v<=lim] += value
final_hsv = cv2.merge((h,s,v))
img = cv2.cvtColor(final_hsv,cv2.COLOR_HSV2BGR)
return img
def changeBlur(img,value):
""" This function will take the img image and blur values as inputs.
After perform blur operation using opencv function, it returns
the image img.
"""
kernel_size = (value+1,value+1) # +1 is to avoid 0
img = cv2.blur(img,kernel_size)
return img
def pyshine_process(params):
print("Parameters:",params)
"""Video streaming generator function."""
CAMERA=True
if CAMERA:
cap = cv2.VideoCapture(0)
else:
cap = cv2.VideoCapture('videos/mario.mp4')
print('FUNCTION DONE')
# Read until video is completed
fps=0
st=0
frames_to_count=20
cnt=0
while(cap.isOpened()):
ret, img = cap.read()
brightness_value_now = int(params['brightness'])
blur_value_now = int(params['blur'])
img = changeBrightness(img,brightness_value_now)
img = changeBlur(img,blur_value_now)
if ret == True:
if cnt == frames_to_count:
try: # To avoid divide by 0 we put it in try except
fps = round(frames_to_count/(time.time()-st))
st = time.time()
cnt=0
except:
pass
cnt+=1
img = imutils.resize(img, width=640)
text = 'FPS: '+str(fps)
img = ps.putBText(img,text,text_offset_x=20,text_offset_y=30,background_RGB=(10,20,222))
text = str(time.strftime("%d %b %Y %H.%M.%S %p"))
img = ps.putBText(img,text,text_offset_x=190,text_offset_y=30,background_RGB=(228,20,222))
text = f"Brightness: {brightness_value_now}"
img = ps.putBText(img,text,text_offset_x=20,text_offset_y=300,background_RGB=(20,210,4))
text = f'Blur: {blur_value_now}'
img = ps.putBText(img,text,text_offset_x=490,text_offset_y=300,background_RGB=(210,20,4))
frame = cv2.imencode('.JPEG', img,[cv2.IMWRITE_JPEG_QUALITY,20])[1].tobytes()
time.sleep(0.016)
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
else:
break
#app.route('/res',methods = ['POST','GET'])
def res():
global result
if request.method == 'POST':
result = request.form.to_dict()
return render_template("results.html",result = result)
#app.route('/results')
def video_feed():
global result
params= result
return Response(pyshine_process(params),mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == "__main__":
app.run(debug=True, host='192.168.1.104',port=9999,threaded=True)
Here are the files for the templates folder
index.html
<html>
<head>
<title>PyShine streaming video application </title>
</head>
<body>
<h1>Please input the parameters (0 to 100): </h1>
<form action = "http://192.168.1.104:9999/res" method = "POST">
<table>
<tr>
<td align="right">Brightness:</td>
<td align="left"><input type="text" name="brightness" value="1" /></td>
</tr>
<tr>
<td align="right">Blur:</td>
<td align="left"><input type="text" name="blur" value="0" /></td>
</tr>
<tr>
<td></td>
<td align="right"><input type = "submit" value = "submit" /></td>
</tr>
</table>
</form>
</body>
</html>
results.html
<!DOCTYPE html>
<html>
<head>
<title>PyShine Streaming Video Application </title>
</head>
<body>
<h1>PyShine streaming processed video... </h1>
<br>
<img src="">
<br>
<input type="button" value="Go back!" onclick="history.back()">
</body>
</html>
Important: change the host IP address according to your specifications.
I am working on an application in which as soon as I run the flask server my webcam should start(which it does!) and show the yolo model on the webpage itself(the one that I have runs independently, which I don't want).
I'm not sure how do I tackle this. Maybe by looking at the code you would be able to help.
main.py
from flask import Flask, render_template, Response
from camera import VideoCamera
import tablib
import os
app = Flask(__name__)
dataset = tablib.Dataset()
with open(os.path.join(os.path.dirname(__file__), 'object.csv')) as f:
dataset.csv = f.read()
#app.route('/')
def index():
data = dataset.html
return render_template('index.html', data=data)
def gen(camera):
while True:
count = 0
frame = camera.get_frame()
yield (b'--frame+str("%d"%count)\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(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
#app.route('/table')
def display_table():
#do something to create a pandas datatable
df = pd.DataFrame(data=[[person], [Timestamps]])
df_html = df.to_html() #using pandas to autogenerate html
return render_template('index.html', 'table_html=df_html')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
camera.py
import datetime
import cv2
import time
import numpy as np
from keras import backend as K
from keras.models import load_model
from yad2k.models.keras_yolo import yolo_head, yolo_eval
from yad2k.yolo_utils import read_classes, read_anchors, preprocess_webcam_image, draw_boxes, generate_colors
import pandas as pd
class VideoCamera(object):
def __init__(self):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
self.video = cv2.VideoCapture(0)
self.class_names = read_classes("model_data/coco_classes.txt")
anchors = read_anchors("model_data/yolo_anchors.txt")
image_shape = (480., 640.)
self.yolo_model = load_model("model_data/yolo.h5")
#print(self.yolo_model.summary(), "fndkjcndkn")
yolo_outputs = yolo_head(self.yolo_model.output, anchors, len(self.class_names))
self.scores, self.boxes, self.classes = yolo_eval(yolo_outputs, image_shape)
def predict(self,sess,frame):
# Preprocess your image
image, image_data = preprocess_webcam_image(frame, model_image_size=(608, 608))
out_scores, out_boxes, out_classes = sess.run([self.scores, self.boxes, self.classes], feed_dict={self.yolo_model.input: image_data,
K.learning_phase(): 0})
# Print predictions info
#print('Found {} boxes'.format(len(out_boxes))) #here it prints object names!
# Generate colors for drawing bounding boxes.
colors = generate_colors(self.class_names)
# Draw bounding boxes on the image file
draw_boxes(image, out_scores, out_boxes, out_classes, self.class_names, colors)
return np.array(image), out_boxes
def __del__(self):
self.video.release()
cv2.destroyAllWindows()
def get_frame(self):
sess = K.get_session()
while True:
# Capture frame-by-frame
grabbed, frame = self.video.read()
if not grabbed:
break
#Run detection
start = time.time()
output_image,image = self.predict(sess,frame)
#df = pd.DataFrame({'Object': [image], 'Timestamp': [datetime.datetime.now().strftime("%A, %d. %B %Y %I:%M%p")]}, index=[0])
#df.to_csv('objects3.csv', sep=';', header = 2, index=False, encoding='utf-8', mode = 'a', columns=['Object', 'Timestamp'])
#df.to_csv('object2.csv', header= 2, mode = 'a')
end = time.time()
cv2.imshow('', output_image)
if(cv2.waitKey(1) & 0xFF == ord('q')):
return 0
#dt = datetime.datetime.now().strftime("%A, %d. %B %Y %I:%M%p")
#print (dt)
#print("Inference time: {:.2f}s".format(end - start))
success, image = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
ret, jpeg = cv2.imencode('.jpg', image)
# Display the resulting frame
cv2.imshow('', output_image)
#
# When everything done, release the capture
stream.release()
cv2.destroyAllWindows()
index.html
<html>
<head>
<title>Video Streaming Demonstration</title>
<link rel=stylesheet type="text/css" href="{{ url_for('static', filename='css/style.css')}}"/>
</head>
<body>
<h1>Keep Looking...</h1>
<img id="bg" src="{{ url_for('video_feed') }}">
{{ table_html | safe }}
<table border="1" class="dataframe">
<thead>
<tr style="text-align: left;">
<th>Objects</th>
<th>Timestamps</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ output_image }} </td>
<td>{{ dt }}</td>
</tr>
</tbody>
</table>
<!-- new code -->
<div class="table">
{% block body %}
{{ data|safe }}
{% endblock %}
</div>
</body>
</html>
This example could work for you. He is not streaming from a camera but from a file. You could write this file in Yolo from one script and read it from Flask. You just need to remove the OpenCV camera code.
test.py
from flask import Flask, render_template, Response
import cv2
import socket
import io
app = Flask(__name__)
vc = cv2.VideoCapture(0)
#app.route('/')
def index():
"""Video streaming ."""
return render_template('index.html')
def gen():
"""Video streaming generator function."""
while True:
rval, frame = vc.read()
cv2.imwrite('pic.jpg', frame)
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + open('pic.jpg', 'rb').read() + b'\r\n')
#app.route('/video_feed')
def video_feed():
"""Video streaming route. Put this in the src attribute of an img tag."""
return Response(gen(),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(debug=True, threaded=True)
/templates/index.html
<html>
<head>
<title>Video Streaming </title>
</head>
<body>
<h1> Live Video Streaming </h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>