

新闻资讯
技术教程本文详解如何在 flask 中正确实现 opencv 摄像头实时视频流,解决因生成器逻辑错误导致的图像无法显示问题,并提供可直接运行的完整代码与关键注意事项。
在 Flask 中实现摄像头实时视频流(MJPEG over HTTP)是一个常见需求,但极易因生成器(generator)使用不当而失败。你遇到的问题核心在于:algorithms.raw_image() 是一个无限 while True 循环,本应持续产出帧数据,但原代码中 caller.output(frame) 是普通函数调用,未将其返回值(即 yield 的字节流)逐帧传递给 Flask 的 Response;同时,output() 方法内部也错误地使用了 yield,而 Flask 的 Response 期望接收一个可迭代的生成器对象,其每个元素必须是完整的、符合 multipart 格式的响应片段。
✅ 正确做法是:
以下是修正后的完整、健壮、可直接运行的代码:
# algorithms.py
import cv2
# 全局摄像头实例(单例,避免多线程冲突)
camera = cv2.VideoCapture(0)
if not camera.isOpened():
raise RuntimeError("无法打开默认摄像头,请检查设备连接")
class Algorithms:
@staticmethod
def raw_image(caller):
"""生成器:持续读取并产出处理后的帧"""
while True:
success, frame = camera.read()
if not success:
print("警告:摄像头读取失败,跳过此帧")
continue
frame = cv2.flip(frame, 1) # 水平翻转(镜像)
yield caller.output(frame) # ✅ 关键:yield output() 的返回值# server.py
from algorithms import Algorithms
from flask import Flask, Response
import cv2
import threading
app = Flask(__name__)
# 使用锁确保摄像头访问线程安全
camera_lock = threading.Lock()
class Server:
def output(self, frame):
"""将 OpenCV BGR 帧编码为 JPEG 并封装为 multipart 响应片段"""
ret, buffer = cv2.imencode('.jpg', frame)
if not ret:
return b''
frame_bytes = buffer.tobytes()
# ✅ 返回完整响应片段(不是 yield!)
return (
b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n'
)
def generate_frames(self):
"""封装生成器,加锁保护摄像头访问"""
while True:
with camera_lock:
# 注意:此处调用 Algorithms.raw_image(self) 返回生成器,
# 我们需遍历它——但更推荐将 raw_image 改为直接在此处读帧
# 为简化与健壮性,我们重构为本地读帧逻辑:
success, frame = camera.read()
if not success:
continue
frame = cv2.flip(frame, 1)
yield self.output(frame)
@app.route('/')
def index():
return '''
Flask 摄像头流
实时视频流
@@##@@
'''
@app.route('/video_feed')
def video_feed():
server_instance = Server()
return Response(
server_instance.generate_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame'
)
# 应用退出时释放摄像头
@app.tear
down_appcontext
def cleanup(exception):
if 'camera' in globals():
camera.release()
if __name__ == '__main__':
try:
app.run(host='0.0.0.0', port=5000, debug=False, threaded=True)
except KeyboardInterrupt:
print("\n正在关闭服务器...")
camera.release()? 关键注意事项总结:
按此结构实现后,浏览器即可稳定加载 MJPEG 流,图像实时可见。