[Python] Flask Multiple Camera Replay with OpenCV

Discussão em 'Python' iniciado por Stack, Março 9, 2025.

  1. Stack

    Stack Membro Participativo

    I looking to see if it is possible to use Flask and OpenCV to create an application to take feeds from 4 Camera simultaneously and on demand play the last 30secs back. Sort of easy VAR replay. (Note: Currently faking cameras 2,3,4 will get more cameras if I prove this works) I have managed to connect to cameras and see content when using basic python app no Flask. But when I run in Flask The feed is black with occasional coloured lines.

    I am running this on Windows.

    Thanks for your wisdom :D

    Darran

    Code: Python Main app

    import cv2
    import threading
    import time
    import numpy as np
    from collections import deque
    from flask import Flask, Response, render_template, request, jsonify

    app = Flask(__name__)

    # Parameters
    FPS = 30 # Frames per second
    BUFFER_SIZE = 30 * FPS # Store last 30 sec of frames
    NUM_CAMERAS = 4 # Number of cameras
    buffers = [deque(maxlen=BUFFER_SIZE) for _ in range(NUM_CAMERAS)]
    #caps = [cv2.VideoCapture(i) for i in range(NUM_CAMERAS)] # Open all cameras

    caps = [
    cv2.VideoCapture(0, cv2.CAP_DSHOW), # Camera 0
    cv2.VideoCapture(0, cv2.CAP_DSHOW), # Fake Camera 2 (copy of 0)
    cv2.VideoCapture(0, cv2.CAP_DSHOW), # Fake Camera 2 (copy of 0)
    cv2.VideoCapture(0, cv2.CAP_DSHOW) # Fake Camera 3 (copy of 0)
    ]


    # Capture video from ca
    # meras
    def capture_video(cam_index):
    while True:
    ret, frame = caps[cam_index].read()
    if ret:
    buffers[cam_index].append(frame)
    time.sleep(1 / FPS)

    # Start threads for each camera
    for i in range(NUM_CAMERAS):
    threading.Thread(target=capture_video, args=(i,), daemon=True).start()


    @app.route('/video_feed/0')
    def video_feed_0():
    return Response(generate_feed(0), mimetype='multipart/x-mixed-replace; boundary=frame')

    @app.route('/video_feed/1')
    def video_feed_1():
    return Response(generate_feed(1), mimetype='multipart/x-mixed-replace; boundary=frame')

    @app.route('/video_feed/<int:cam_index>')
    def video_feed(cam_index):
    return Response(generate_feed(cam_index),
    mimetype='multipart/x-mixed-replace; boundary=frame')


    # Stream live video
    def generate_feed(cam_index):
    while True:
    success, frame = caps[cam_index].read()

    if not success:
    print(f"❌ Failed to capture frame from Camera {cam_index}")
    continue

    cv2.imshow(f"Camera {cam_index}", frame) # Display the frame in OpenCV window

    ret, buffer = cv2.imencode(".jpg", frame)
    if not ret:
    print(f"❌ Failed to encode frame from Camera {cam_index}")
    continue

    yield (b"--frame\r\n"
    b"Content-Type: image/jpeg\r\n\r\n" + buffer.tobytes() + b"\r\n")




    # Replay last 30 sec
    @app.route('/replay/<int:speed>')
    def replay(speed):
    replay_buffers = [list(buffers) for i in range(NUM_CAMERAS)]
    min_frames = min(len(frames) for frames in replay_buffers)

    for i in range(min_frames):
    frames = [cv2.resize(replay_buffers[c], (320, 240)) for c in range(NUM_CAMERAS)]
    combined_frame = np.vstack([np.hstack(frames[:2]), np.hstack(frames[2:])])

    _, buffer = cv2.imencode(".jpg", combined_frame)
    yield (b"--frame\r\n"
    b"Content-Type: image/jpeg\r\n\r\n" + buffer.tobytes() + b"\r\n")
    time.sleep((1 / FPS) / speed)

    @app.route('/replay_feed/<int:speed>')
    def replay_feed(speed):
    return Response(replay(speed), mimetype="multipart/x-mixed-replace; boundary=frame")

    @app.route('/')
    def index():
    return render_template("index.html")

    if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)


    HTML:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multi-Camera Replay</title>
    <style>
    body { font-family: Arial, sans-serif; text-align: center; }
    .grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
    img { width: 100%; height: auto; }
    button { margin: 10px; padding: 10px; font-size: 16px; cursor: pointer; }
    </style>
    </head>
    <body>

    <h1>Live Camera Feeds</h1>
    <div class="grid">
    <img src="/video_feed/0" alt="Camera 0">
    <img src="/video_feed/1" alt="Camera 1">
    <img src="/video_feed/2" alt="Camera 2">
    <img src="/video_feed/3" alt="Camera 3">
    </div>

    <h1>Replay Last 30 Sec</h1>
    <button onclick="startReplay(1)">Replay Normal</button>
    <button onclick="startReplay(2)">Replay 2x Speed</button>
    <button onclick="startReplay(0.5)">Replay Slow Motion</button>

    <h1>Replay View</h1>
    <img id="replayFeed" src="" alt="Replay Feed" style="width: 80%; border: 2px solid black;">

    <script>
    function startReplay(speed) {
    document.getElementById("replayFeed").src = "/replay_feed/" + speed;
    }
    </script>

    </body>
    </html>

    Continue reading...

Compartilhe esta Página