Flask 实现实时摄像头视频流的正确方法
技术百科
心靈之曲
发布时间:2026-01-17
浏览: 次 本文详解如何在 flask 中正确实现 opencv 摄像头实时视频流,解决因生成器逻辑错误导致的图像无法显示问题,并提供可直接运行的完整代码与关键注意事项。
在 Flask 中实现摄像头实时视频流(MJPEG over HTTP)是一个常见需求,但极易因生成器(generator)使用不当而失败。你遇到的问题核心在于:algorithms.raw_image() 是一个无限 while True 循环,本应持续产出帧数据,但原代码中 caller.output(frame) 是普通函数调用,未将其返回值(即 yiel 
✅ 正确做法是:
- raw_image() 必须是生成器函数(含 yield),且每次循环中 yield caller.output(frame) —— 将处理后的帧数据逐帧产出;
- output() 方法应返回单帧的完整 multipart 响应片段(return),而非 yield(否则外层生成器会产出 generator 对象,而非 bytes);
- 需确保 OpenCV 资源(如 VideoCapture)在应用结束时被释放,避免端口/设备占用;
- 推荐使用 threading.Lock 或全局状态管理摄像头实例,防止多请求并发导致 cv2.VideoCapture 冲突(Flask 默认多线程模式下,多个请求可能同时调用 raw_image)。
以下是修正后的完整、健壮、可直接运行的代码:
# 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.teardown_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()? 关键注意事项总结:
- ❌ 错误模式:output() 内 yield → 导致外层生成器产出 generator 对象,Flask 无法序列化;
- ✅ 正确模式:output() return bytes,raw_image() 或 generate_frames() 中 yield self.output(frame);
- ? 不要在 @app.route 内创建新 VideoCapture 实例,务必复用全局单例并加锁;
- ⚠️ 生产环境请改用 gunicorn + gevent 替代 app.run(),并添加超时、错误重连、帧率控制(如 time.sleep(0.033) 限 30 FPS);
- ? 浏览器访问地址为 http://
:5000(非 localhost,因 host='0.0.0.0' 绑定所有接口)。
按此结构实现后,浏览器即可稳定加载 MJPEG 流,图像实时可见。
# ai
# 是一个
# 多个
# 推荐使用
# 而非
# 可直接
# 浏览器
# app
# 端口
# http
# go
# 循环
# 并发
# 对象
# html
# 编码
# 字节
# 接口
# 线程
# 多线程
# while
# 返回值
# 遍历
# 加锁
# flask
# 帧数
# opencv
# gunicorn
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- SAX解析器是什么,它与DOM在处理大型XML文件
- Win11开机Logo怎么换_Win11自定义启动
- 怎么将XML数据可视化 D3.js加载XML
- Python项目维护经验_长期演进说明【指导】
- Go语言中正确反序列化多个同级XML元素为结构体切
- windows系统找不到无线网络怎么办_windo
- Windows10电脑怎么设置虚拟光驱_Win10
- C++如何使用std::async进行异步编程?(
- Win11怎么修复系统文件_使用sfc命令修复Wi
- Win11怎么自动隐藏任务栏_Win11全屏显示设
- Win11怎么开启远程桌面_Win11系统远程桌面
- Win11如何设置系统语言_Win11系统语言切换
- Windows电脑如何截屏?(四种快捷方法)
- 如何使用Golang实现容器健康检查_监控和自动重
- php后缀怎么变mp4能播放_让php伪装mp4正
- C++中的协变与逆变是什么?C++函数指针与返回类
- Win11怎么关闭搜索历史_Win11清除任务栏搜
- 如何在 Go 中正确反序列化多个同级 XML 元素
- php与c语言在嵌入式中有何区别_对比两者在硬件控
- VSC怎样用终端运行PHP_命令行执行脚本的步骤【
- Windows10系统怎么查看CPU核心数_Win
- PHP主流架构怎么集成Redis缓存_配置步骤【方
- Win11怎么关闭自动调节亮度_Windows11
- Python音视频处理高级项目教程_FFmpegP
- Windows服务无法启动错误1067是什么_进程
- Mac如何使用听写功能_Mac语音输入打字【效率技
- Win10怎么设置开机密码_Windows10账户
- 如何使用Golang log记录不同级别日志_Go
- Win11怎么设置开机问候语_自定义Win11锁屏
- Win11输入法切换快捷键怎么改_Windows
- 如何在Golang中处理模块包路径变化_Golan
- php订单日志怎么在swoole写_php协程sw
- php删除数据怎么清空表_truncate与del
- Win11怎么更改电脑密码_Windows 11修
- VSC怎样在VSC中调试PHPAPI_接口调试技巧
- Win11怎么设置ipv4地址_Windows 1
- mac怎么分屏_MAC双屏显示与分屏操作技巧【指南
- Mac怎么给文件夹加密_Mac创建加密磁盘映像教程
- Windows10如何查看保存的WiFi密码_Wi
- Win11怎么关闭边缘滑动手势_Windows11
- php文件怎么变mp4保存_php输出视频流保存为
- 如何在Golang中使用time处理时间_Gola
- Win11更新后变慢怎么办_Win11系统更新后卡
- Mac的Time Machine怎么用_Mac系统
- c++中如何使用auto关键字_c++11类型推导
- c++ std::future和std::prom
- php下载安装选zip还是msi格式_两种安装包对
- MAC的“接续互通”功能无法使用怎么办_MAC检查
- 如何在 IIS 上为 ASP.NET 6 应用排除
- Windows10怎么备份注册表_Windows1

QQ客服