Flask+Opencv无法多人读取摄像头

我试图在Flask中把本机的摄像头返回到前端,并把Flask的host设置为0.0.0.0,当我和我的室友访问地址时,只有一个人可以享用我电脑的摄像头,其他先进的人会被后进的人卡掉(卡成一帧),我认为是多线程的问题,于是在Flask中做了相关配置,但似乎没什么用,如何可以让所有人都可以正常访问我的摄像头?

img

相关代码如下(前端部分没有截取)

img

以下内容部分参考ChatGPT模型:


这个问题可能是由于多个用户同时访问摄像头造成的资源竞争问题。为了解决这个问题,可以考虑使用多线程或多进程的方式来处理每个用户的请求。

具体来说,可以使用Flask的Blueprint来实现多线程或多进程。首先,在Flask中定义一个Blueprint:

from flask import Blueprint

camera_bp = Blueprint('camera', __name__)

然后,在Blueprint中定义一个路由来处理摄像头请求:

import cv2

from flask import Response

from . import camera_bp

def generate_frames():
    camera = cv2.VideoCapture(0)

    while True:
        success, frame = camera.read()

        if not success:
            break

        ret, buffer = cv2.imencode('.jpg', frame)

        if not ret:
            break

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

    camera.release()

@camera_bp.route('/video_feed')
def video_feed():
    return Response(generate_frames(),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

在这个路由中,我们通过cv2.VideoCapture(0)打开本机的摄像头,并使用一个while循环来不断读取摄像头的帧。对于每一帧,我们使用cv2.imencode()将其编码为JPEG格式,并通过yield语句将其发送到前端。最后,我们在路由的返回值中使用Flask的Response对象来包装这个生成器函数。

接下来,我们可以在Flask的主文件中将这个Blueprint注册到Flask应用中:

from flask import Flask

from .camera import camera_bp

app = Flask(__name__)
app.register_blueprint(camera_bp)

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

现在,我们就可以通过访问http://localhost:5000/video_feed%E6%9D%A5%E6%9F%A5%E7%9C%8B%E6%9C%AC%E6%9C%BA%E7%9A%84%E6%91%84%E5%83%8F%E5%A4%B4%E4%BA%86%E3%80%82%E5%A6%82%E6%9E%9C%E6%9C%89%E5%A4%9A%E4%B8%AA%E7%94%A8%E6%88%B7%E5%90%8C%E6%97%B6%E8%AE%BF%E9%97%AE%E8%BF%99%E4%B8%AAURL%EF%BC%8C%E6%AF%8F%E4%B8%AA%E7%94%A8%E6%88%B7%E9%83%BD%E4%BC%9A%E8%8E%B7%E5%BE%97%E8%87%AA%E5%B7%B1%E7%8B%AC%E7%AB%8B%E7%9A%84%E7%94%9F%E6%88%90%E5%99%A8%E5%87%BD%E6%95%B0%EF%BC%8C%E4%BB%8E%E8%80%8C%E9%81%BF%E5%85%8D%E4%BA%86%E8%B5%84%E6%BA%90%E7%AB%9E%E4%BA%89%E9%97%AE%E9%A2%98%E3%80%82


如果我的建议对您有帮助、请点击采纳、祝您生活愉快

在 Flask 中,处理请求和响应的过程是单线程的,默认情况下只能处理一个请求。如果多个客户端同时访问你的 Flask 应用,并向你的摄像头请求视频流数据,由于摄像头数据的获取和编码是阻塞操作,因此只能等待上一个请求完成后才能处理下一个请求。这就会导致某些客户端无法接收到足够的数据帧,从而卡成一帧。

为了解决这个问题,你可以使用 Flask-SocketIO 库实现基于 WebSocket 的实时通信,在客户端和服务器之间建立一个持久化的双向连接,这样每个客户端都可以独立地获取摄像头数据,并不影响其他客户端的体验。

下面是一个简单的示例代码,演示了如何在 Flask 中使用 Flask-SocketIO 库实现实时视频流传输:

from flask import Flask, render_template
from flask_socketio import SocketIO, emit
import cv2

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)


def gen_frames():
    camera = cv2.VideoCapture(0)
    while True:
        success, frame = camera.read()
        if not success:
            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')
    camera.release()


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


@socketio.on('connect', namespace='/video')
def connect():
    emit('response', {'data': 'Connected'})


@socketio.on('disconnect', namespace='/video')
def disconnect():
    print('Client disconnected')


@socketio.on('request', namespace='/video')
def request_frame():
    for frame in gen_frames():
        socketio.emit('frame', {'data': frame}, namespace='/video')


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

在这个示例代码中,我们使用 OpenCV 获取摄像头数据,并将每一帧编码为 JPEG 格式的字节流,然后通过 Flask-SocketIO 库实现实时传输。具体来说,在客户端连接到服务器时,我们向客户端发送一个连接成功的响应。当客户端需要请求新的视频帧时,我们从摄像头获取数据并将其作为“frame”事件的数据发送给客户端。客户端可以监听“frame”事件,并在收到新的数据时立即更新图像。使用 Flask-SocketIO 库可以避免多线程的问题,并允许多个客户端同时独立地接收视频流数据。