System setup:
IP Camera —> Processing Server (Ubuntu 20.04) —> Display Unit (browser as client)
My processing server works as an inference server and processes the images for face classification using OpenCV and Dlib with Python 3.8 and CUDA. Finally, streams the output using Flask so that I can see it in the browser from anywhere.
Everything works as expected, no lags in the capture or processing. Output video is smooth if I see it from the browser on the same server. However, the output (video) lags and freezes if I open it from other devices (in the same network).
I’m really stuck, tried many diffent things but nothing really helped. Will be grateful if some kind hearted helped to get out of the situation.
My Python coding is as follow:
import cv2
import numpy as np
from flask import Flask
import threading
...
import psycopg2
from psycopg2.pool import ThreadedConnectionPool
...
thread_lock = threading.Lock()
out_cam1 = out_cam2 = None
# create db-pool
db_pool = create_pool_connection_pgsql()
# ----------------------------------------------------------
class FrameCapture:
def __init__(self, source=0, backend=1800):
if backend and isinstance(backend, int):
self.stream = cv2.VideoCapture(source, backend)
else:
self.stream = cv2.VideoCapture(source)
(self.grabbed, self.frame) = self.stream.read()
self.stopped = False
# thread initialization
self.thread = None
def start(self):
self.thread = Thread(target=self.update, args=())
self.thread.daemon = True
self.thread.start()
return self
def update(self):
while True:
# if the thread indicator variable is set, stop the thread
if self.stopped:
return
# otherwise, read the next frame from the stream
(self.grabbed, self.frame) = self.stream.read()
def read(self):
# return the frame most recently read
return self.frame
def stop(self):
# indicate that the thread should be stopped
self.stopped = True
# ----------------------------------------------------------
def get_camera_id():
global db_pool
# get list of camera from db
...
# ----------------------------------------------------------
def camera_source(cam_id):
global db_pool
# get camera source from db
...
# ----------------------------------------------------------
def process_frames(cam_id):
global thread_lock
global out_cam1, out_cam2, ...
...
cam_cap = FrameCapture(source=camera_source(cam_id)).start()
while True:
frame = cam_cap.read()
# processing frames here
...
...
with thread_lock:
if cam_id == "cam_1":
out_cam1 = frame.copy()
elif cam_id == "cam_2":
out_cam2 = frame.copy()
...
# ----------------------------------------------------------
def generate_stream(cam_id):
global thread_lock
global out_cam1, out_cam2, ...
# loop over frames from the output stream
while True:
with thread_lock:
if cam_id == "cam_1":
if out_cam1 is None:
continue
# encode the frame in JPEG format
(flag, encoded_image) = cv2.imencode(".jpg", out_cam1)
if not flag:
continue
elif cam_id == "cam_2":
if out_cam2 is None:
continue
# encode the frame in JPEG format
(flag, encoded_image) = cv2.imencode(".jpg", out_cam2)
if not flag:
continue
...
# yield the output frame in the byte format
yield b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + encoded_image.tobytes() + b'\r\n'
# ----------------------------------------------------------
# feed generator for cam display
@app.route('/cam_feed/', methods=['GET'])
def camera_feed():
return Response(generate_stream(cam_id=request.args.get('cam_id')),
mimetype="multipart/x-mixed-replace; boundary=frame")
# ----------------------------------------------------------
# display for camera
@app.route('/<cam_id>/', methods=['GET'])
def camera_display(cam_id):
return render_template("cam_feed.html", cam_id=cam_id)
# ----------------------------------------------------------
# check if this is the main thread
if __name__ == '__main__':
# get list of cam-id's
cam_active = get_camera_id()
for cam in cam_active:
cam_thread = threading.Thread(target=process_frames, name=cam, args=(cam,))
cam_thread.daemon = True
cam_thread.start()
sleep(3)
# start the app
app.run(host='0.0.0.0',
port=5000,
# debug=True,
debug=False,
threaded=True,
use_reloader=False
)
...
# close db pool connections
if db_pool:
db_pool.closeall()