Opencv+flask to process more than 5 cameras

hello, OpenCV teams,I want to control more than 5 cameras,and I use python==3.6.9 opencv==4.1.1(jetson tx2 default)
with flask to create a web program

#app.py for flask
import os
import time
import cv2
from flask import Flask, render_template, request, jsonify, Response
from threading import Thread, Lock
import subprocess
import base64

app = Flask(__name__)


camera_config = {
    'roix': 0,
    'roiy': 0,
    'width': 256,
    'height': 256,
    'fps': 30
}


devices = ['/dev/video0', '/dev/video1', '/dev/video2', '/dev/video3', '/dev/video4', '/dev/video5']


storage_path = './raw'


class CameraThread(Thread):
    def __init__(self, device, config, storage_path):
        super().__init__()
        self.device = device
        self.config = config
        self.storage_path = storage_path
        self.cap = None
        self.running = False
        self.lock = Lock()

    def setup_camera(self):
      
        v4l2_cmds = [
            f"v4l2-ctl -d {self.device} --set-ctrl roi_x={self.config['roix']}",
            f"v4l2-ctl -d {self.device} --set-ctrl roi_y={self.config['roiy']}",
            f"v4l2-ctl -d {self.device} --set-fmt-video=width={self.config['width']},height={self.config['height']},pixelformat=GREY",
            f"v4l2-ctl -d {self.device} --set-ctrl frame_rate={self.config['fps']}",
        ]
        for cmd in v4l2_cmds:
            subprocess.call(cmd, shell=True)

    def open_camera(self):
        self.cap = cv2.VideoCapture(self.device)
        if self.cap.isOpened():
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.config['width'])
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.config['height'])
            self.running = True
        else:
            print(f"Failed to open {self.device}")

    def capture_frame(self):
        with self.lock:
            if self.cap and self.running:
                ret, frame = self.cap.read()
                if ret:
                    timestamp = time.strftime("%Y%m%d-%H%M%S", time.localtime())
                    filename = os.path.join(self.storage_path, self.device.split('/')[-1], f'{timestamp}.raw')
                    os.makedirs(os.path.dirname(filename), exist_ok=True)
                    with open(filename, 'wb') as f:
                        f.write(frame.tobytes())

    def run(self):
        self.setup_camera()
        self.open_camera()
        while self.running:
            time.sleep(1 / self.config['fps'])

    def stop(self):
        with self.lock:
            self.running = False
            if self.cap:
                self.cap.release()
                self.cap = None


class CameraManager:
    def __init__(self, devices, config, storage_path):
        self.devices = devices
        self.config = config
        self.storage_path = storage_path
        self.cameras = {}

    def start_all(self):
        
        self.cameras = {device: CameraThread(device, self.config, self.storage_path) for device in self.devices}
        for camera in self.cameras.values():
            camera.start()

    def capture_all(self):
   
        for camera in self.cameras.values():
            if camera.running:
                camera.capture_frame()

    def stop_all(self):
        
        for camera in self.cameras.values():
            camera.stop()
            camera.join()
        self.cameras.clear()  

    def set_storage_path(self, path):
        global storage_path
        storage_path = path
        for camera in self.cameras.values():
            camera.storage_path = path

camera_manager = CameraManager(devices, camera_config, storage_path)

@app.route('/')
def index():
    return render_template('index.html',my_list=[0, 6])

@app.route('/start', methods=['POST'])
def start():
    camera_manager.start_all()
    return jsonify({'status': 'started'})

@app.route('/capture', methods=['POST'])
def capture():
    camera_manager.capture_all()
    return jsonify({'status': 'captured'})

@app.route('/stop', methods=['POST'])
def stop():
    camera_manager.stop_all()
    return jsonify({'status': 'stopped'})

@app.route('/set_storage', methods=['POST'])
def set_storage():
    path = request.form.get('path', './raw')
    camera_manager.set_storage_path(path)
    return jsonify({'status': 'storage set', 'path': path})

@app.route('/video_feed/<string:device>')
def video_feed(device):
    def generate():
        while True:
            frames = {}
            for camera_device, camera in camera_manager.cameras.items():
                if camera.device.split('/')[-1] == device:
                    with camera.lock:
                        if camera.cap and camera.running:
                            ret, frame = camera.cap.read()
                            if ret:
                                _, jpeg = cv2.imencode('.jpg', frame)
                                yield (b'--frame\r\n'
                                       b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n\r\n')
            time.sleep(1 / camera_config['fps'])
    return Response(generate(), mimetype='multipart/x-mixed-replace; boundary=frame')

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


and

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multi-Camera Control</title>
    <style>
        .camera-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-around;
            margin-top: 20px;
        }
        .camera {
            border: 1px solid #ccc;
            padding: 10px;
            width: 256px;
            height: 256px;
            overflow: hidden;
        }
        .camera img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    </style>
</head>
<body>
    <h1>Multi-Camera Control</h1>
    <button id="start">Start Cameras</button>
    <button id="capture">Capture Frames</button>
    <button id="stop">Stop Cameras</button>
    <form id="storage-form">
        <label for="storage-path">Storage Path:</label>
        <input type="text" id="storage-path" name="path" value="./raw">
        <button type="submit">Set Storage Path</button>
    </form>
    <div id="status"></div>
    <div class="camera-container">
        <div class="camera" id="video0">
            <img src="" alt="Video0">
        </div>
        <div class="camera" id="video1">
            <img src="" alt="Video1">
        </div>
        <div class="camera" id="video2">
            <img src="" alt="Video2">
        </div>
        <div class="camera" id="video3">
            <img src="" alt="Video3">
        </div>
        <div class="camera" id="video4">
            <img src="" alt="Video4">
        </div>
<!--        <div class="camera" id="video5">-->
<!--            <img src="" alt="Video5">-->
<!--        </div>-->
    </div>

    <script>
        document.getElementById('start').addEventListener('click', function() {
            fetch('/start', { method: 'POST' })
                .then(response => response.json())
                .then(data => {
                    document.getElementById('status').innerText = data.status;
                    // Start video feeds
                    document.querySelectorAll('.camera img').forEach(img => {
                        img.src = `/video_feed/${img.parentElement.id}`;
                    });
                });
        });

        document.getElementById('capture').addEventListener('click', function() {
            fetch('/capture', { method: 'POST' })
                .then(response => response.json())
                .then(data => {
                    document.getElementById('status').innerText = data.status;
                });
        });

        document.getElementById('stop').addEventListener('click', function() {
            fetch('/stop', { method: 'POST' })
                .then(response => response.json())
                .then(data => {
                    document.getElementById('status').innerText = data.status;
                    // Stop video feeds
                    document.querySelectorAll('.camera img').forEach(img => {
                        img.src = '';
                    });
                });
        });

        document.getElementById('storage-form').addEventListener('submit', function(event) {
            event.preventDefault();
            const path = document.getElementById('storage-path').value;
            fetch('/set_storage', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: `path=${path}`
            })
            .then(response => response.json())
            .then(data => {
                document.getElementById('status').innerText = `Storage set to ${data.path}`;
            });
        });
    </script>
</body>
</html>

when I open 5 cameras at the same time, my program works. but more than 5 cameras ,it not work.
Is this an opencv limitation of only having a maximum of 5 cameras open at the same time? If so, where do I change to remove this restriction?

Try implementing multi-threading and create a separate thread for each camera to handle frame capture independently:

import cv2
import threading
import queue

class CameraThread(threading.Thread):
def init(self, camera_id):
threading.Thread.init(self)
self.camera_id = camera_id
self.queue = queue.Queue(maxsize=10)
self.stopped = False

def run(self):
    cap = cv2.VideoCapture(self.camera_id)
    while not self.stopped:
        ret, frame = cap.read()
        if not ret:
            break
        if not self.queue.full():
            self.queue.put(frame)
    cap.release()

def stop(self):
    self.stopped = True

def get_frame(self):
    return self.queue.get()

Usage

camera_threads =
for i in range(10): # Adjust the number of cameras
thread = CameraThread(i)
thread.start()
camera_threads.append(thread)

Main processing loop

while True:
for thread in camera_threads:
frame = thread.get_frame()
# Process frame here