This time I’ve been able to further test your case.
1. I think that the main issue you’re facing is the management of the queue. I’m not so familiar with python and its Threads/Queue modules, but just adapting your code to do that in the process_frames loop should convince you:
import cv2
from flask import Flask, Response
from imutils.video import VideoStream
import time
app = Flask(__name__)
# Colours, dimensions and other settings
RED = (0, 0, 255) # BGR
src_width = 1920
src_height = 1080
xstep = 5
xpos = range(0, src_width, xstep)
xnum = int(src_width / xstep)
ypos = src_height / 2
def process_frames():
src_stream = VideoStream(src=src_url).start()
while True:
frame = src_stream.read()
xposIdx = int(process_frames.frame_nb) % int(xnum)
x = xpos[xposIdx]
y = ypos
x_end = x + 100 # Adjust the width of the box
y_end = y + 100 # Adjust the height of the box
# Draw red box on the video
cv2.rectangle(frame, (int(x), int(y)), (int(x_end), int(y_end)), RED, 2)
# Encode the frame as JPEG
_, jpeg = cv2.imencode('.jpg', frame)
frame_bytes = jpeg.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n\r\n')
process_frames.frame_nb = process_frames.frame_nb + 1
#cv2.waitKey(1)
src_stream.stop()
process_frames.frame_nb = 0
@app.route('/video_feed')
def video_feed():
return Response(process_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
def main():
app.run(host='127.0.0.1', port=5560, debug=True)
cv2.destroyAllWindows()
if __name__ == '__main__':
src_url = "rtsp://127.0.0.1:8554/test"
main()
And you may use one of the following pipelines for testing from localhost:
gst-launch-1.0 souphttpsrc is-live=1 location=http://127.0.0.1:5560/video_feed ! queue ! multipartdemux single-stream=1 ! image/jpeg ! jpegdec ! videoconvert ! autovideosink
# Or using NVDEC
gst-launch-1.0 souphttpsrc is-live=1 location=http://127.0.0.1:5560/video_feed ! queue ! multipartdemux single-stream=1 ! image/jpeg ! nvv4l2decoder mjpeg=1 ! nvvidconv ! autovideosink
One part of the missed synchro issue may also come from imutils.
2. Using gstreamer backend allows a much more periodic frame acquisition and processing. Also try:
import cv2
from flask import Flask, Response
# Check that your opencv build for this python version has gstreamer videoio support
import re
print('GStreamer support: %s' % re.search(r'GStreamer\:\s+(.*)', cv2.getBuildInformation()).group(1))
app = Flask(__name__)
# Colours, dimensions and other settings
RED = (0, 0, 255) # BGR
def process_frames():
gst_cap_str = 'uridecodebin uri=' + src_url + ' ! nvvidconv ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=BGR ! queue ! appsink drop=1'
print(gst_cap_str)
cap = cv2.VideoCapture(gst_cap_str, cv2.CAP_GSTREAMER)
if not cap.isOpened():
print('Failed to open capture. Exiting')
quit()
fps = float(cap.get(cv2.CAP_PROP_FPS))
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (w, h)
print(f"Size: {(size)}, FPS: {fps}")
xstep = 5
xpos = range(0, w, xstep)
xnum = int(w / xstep)
ypos = h / 2
while True:
ret, frame = cap.read()
if not ret:
print('Failed to read from camera, aborting')
break
xposIdx = int(process_frames.frame_nb) % xnum
x = xpos[xposIdx]
y = ypos
x_end = x + 100 # Adjust the width of the box
y_end = y + 100 # Adjust the height of the box
# Draw red box on the video
cv2.rectangle(frame, (int(x), int(y)), (int(x_end), int(y_end)), RED, 2)
# Encode the frame as JPEG
_, jpeg = cv2.imencode('.jpg', frame)
frame_bytes = jpeg.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n\r\n')
process_frames.frame_nb = process_frames.frame_nb + 1
#cv2.waitKey(1)
cap.release()
process_frames.frame_nb = 0
@app.route('/video_feed')
def video_feed():
return Response(process_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
def main():
app.run(host='127.0.0.1', port=5560, debug=True)
cv2.destroyAllWindows()
if __name__ == '__main__':
src_url = "rtsp://127.0.0.1:8554/test"
main()
You can safely ignore the warnings about gstreamer backend unable to query duration and compute position.