欢迎关注『OpenCV DNN @ Youcans』系列,持续更新中
【OpenCV DNN】Flask 视频监控目标检测教程 04
- 3.4 用Flask构建流媒体服务器
- 3.4.1 流媒体服务器基本知识
- 3.4.2 用Flask搭建流媒体服务器
- Flask04 完整例程
- cvFlask04 项目的文件树
- cvFlask04.py
- index1.html
本系列从零开始,详细讲解使用 Flask 框架构建 OpenCV DNN 模型的 Web 应用程序。
本节介绍用Flask构建流媒体服务器,向服务器发送请求可以获取模拟视频源产生的视频图像。
3.4 用Flask构建流媒体服务器
我们的第四个例程,使用Flask框架构建一个视频流服务器,向服务器发送请求可以获取模拟视频源产生的视频图像。本例使用模拟视频源,是为了避免硬件配置和视频延迟的影响,构建一个极简的视频流服务器。
3.4.1 流媒体服务器基本知识
我们首先介绍流媒体服务器的基本知识。
流是一种让服务器在响应请求时将响应数据分块的技术。针对视频数据量大、实时性要求高的特点,把应答数据分成小块数据,服务器以数据块的形式响应请求,分块传输给客户端来实现流媒体服务器,因此响应返回请求就不会随视频数据而变大
另外,在视频流的返回播放时,视频流可以让每个块替换页面中的前一个块,来实现在浏览器窗口中的“播放”。
Multipart响应包含一个multipart媒体类型的标头,后面跟着多块独立的数据,每块数据有自己的Content-Type。Multipart有多种不同类型,针对流媒体我们使用multipart/x-mixed-replace。以下是Multipart视频流的结构:
HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame
--frame
Content-Type: image/jpeg
<jpeg data here>
--frame
Content-Type: image/jpeg
<jpeg data here>
...
3.4.2 用Flask搭建流媒体服务器
下面我们使用Flask搭建一个简单的流媒体服务器。
Flask使用生成器(generator function)原生支持流式响应,生成器可以中断和恢复,可以一次返回多个值。一个返回流式响应的路由,需要返回参数为生成器的Response对象。Flask负责调用生成器,将Response对象分成数据块,使用Multipart Responses来组装一个HTTP应答,将数据块发送给客户端。
使用Motion JPEG方法,将视频画面逐帧发送给浏览器,浏览器逐帧替换来实现视频的播放功能。这也是很多IP Camera的流媒体播放方式,实时性很好,但视频质量不是很理想。
新建一个Flask项目。cvFlask04项目的文件树如下。
---项目文件名\
|---templates\
| |---index1.html
|--- cvFlask04.py
任务逻辑由Python程序文件cvFlask04.py实现,完整代码如下。
# cvFlask04.py
# OpenCV+Flask 图像处理例程 04
# 在网页上显示模拟视频源的图像
# Copyright 2023 Youcans, XUPT
# Crated:2023-4-30
from flask import Flask, Response, render_template, request
import cv2
import numpy as np
import time
app = Flask(__name__)
# 定义视频流类
class VideoStream(object):
def get_frame(self): # 模拟视频源
time.sleep(0.1)
ct = time.time()
sec_str = str(round(ct - int(ct), 3))
img = np.ones((400, 600, 3), np.uint8) * 128
cv2.putText(img, sec_str, (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255,255,0), 1)
ret, buffer = cv2.imencode('.jpg', img) # 编码为 jpg 格式
img_byte = buffer.tobytes() # 转换为 bytes 类型
return img_byte
# 视频流的网页 HTML 模板
@app.route('/')
def index():
return render_template('index1.html')
# 生成视频流的帧
def gen_frames(camera):
while True:
frame = camera.get_frame() # 获取视频帧
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'
+ frame + b'\r\n') # 生成视频流的帧
# 视频流的传输路由:从网页获取视频源,返回视频流
@app.route('/video_feed')
def video_feed():
return Response(gene_frames(VideoStream()),
content_type='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
# 启动本地视频流服务器,激活该网页
app.run(host='0.0.0.0', port=5000, debug=True, threaded=True) # 绑定 IP 地址和端口号
程序cvFlask04.py定义了一个视频流VideoStream类,用于持续地提供视频帧的数据。在本例中使用模拟视频源,将系统时间写在空白画布上来生成模拟视频图像。
程序的index()中指定了视频流的网页模板index1.html。网页index1.html位于templates文件夹,具体内容如下。
<!DOCTYPE html>
<html>
<head>
<title>Video Streaming Demonstration</title>
</head>
<body>
<h1>Video Streaming Demonstration</h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
其中,img标签定义了图片使用的url,是由url_for()函数返回的、利用视图函数的名字’video_feed’获取的动态url。
/video_feed路径由video_feed()方法提供服务,返回一个multipart应答。生成器函数gen_frames()不断地从VideoStream逐帧获取图片,通过生成器返回给客户端。客户端浏览器收到流媒体时,在img标签定义的图片中逐帧显示,从而实现视频播放。
下面运行cvFlask03的项目脚本。进入cvFlask04项目的根目录,运行程序cvFlask04.py,启动流媒体服务器。
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.3.249:5000
在本地浏览器打开http://127.0.0.1:5000,或在局域网内设备(包括移动手机)浏览器打开http://192.168.3.249:5000,就可以看到模拟视频源生成的视频流。
Flask04 完整例程
cvFlask04 项目的文件树
---项目文件名\
|---templates\
| |---index1.html
|--- cvFlask04.py
cvFlask04.py
# cvFlask04.py
# OpenCV+Flask 图像处理例程 04
# 在网页上显示模拟视频源的图像
# Copyright 2023 Youcans, XUPT
# Crated:2023-4-30
from flask import Flask, Response, render_template, request
import cv2
import numpy as np
import time
app = Flask(__name__)
# 定义视频流类
class VideoStream(object):
def get_frame(self): # 模拟视频源
time.sleep(0.1)
ct = time.time()
sec_str = str(round(ct - int(ct), 3))
img = np.ones((400, 600, 3), np.uint8) * 128
cv2.putText(img, sec_str, (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255,255,0), 1)
ret, buffer = cv2.imencode('.jpg', img) # 编码为 jpg 格式
img_byte = buffer.tobytes() # 转换为 bytes 类型
return img_byte
# 视频流的网页 HTML 模板
@app.route('/')
def index():
return render_template('index1.html')
# 生成视频流的帧
def gen_frames(camera):
while True:
frame = camera.get_frame() # 获取视频帧
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'
+ frame + b'\r\n') # 生成视频流的帧
# 视频流的传输路由:从网页获取视频源,返回视频流
@app.route('/video_feed')
def video_feed():
return Response(gene_frames(VideoStream()),
content_type='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
# 启动本地视频流服务器,激活该网页
app.run(host='0.0.0.0', port=5000, debug=True, threaded=True) # 绑定 IP 地址和端口号
index1.html
<!DOCTYPE html>
<html>
<head>
<title>Video Streaming Demonstration</title>
</head>
<body>
<h1>Video Streaming Demonstration</h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
【本节完】
下节我们将讨论:使用Flask框架构建一个视频流服务器。
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:
【OpenCV DNN】Flask 视频监控目标检测教程 04(https://blog.csdn.net/youcans/article/details/130865522)
Copyright 2023 youcans, XUPT
Crated:2023-05-25
欢迎关注『OpenCV DNN @ Youcans』系列,持续更新中
【OpenCV DNN】Flask 视频监控目标检测教程 01
【OpenCV DNN】Flask 视频监控目标检测教程 02
【OpenCV DNN】Flask 视频监控目标检测教程 03