我有一个 flask 应用程序,它从相机读取帧并将其流式传输到网站。
相机.py
from threading import Thread
from copy import deepcopy
import queue
import cv2
class Camera(Thread):
def __init__(self, cam, normalQue, detectedQue):
Thread.__init__(self)
self.__cam = cam
self.__normalQue = normalQue
self.__detectedQue = detectedQue
self.__shouldStop = False
def __del__(self):
self.__cam.release()
print('Camera released')
def run(self):
while True:
rval, frame = self.__cam.read()
if rval:
frame = cv2.resize(frame, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
_, jpeg = cv2.imencode('.jpg', frame)
self.__normalQue.put(jpeg.tobytes())
self.__detectedQue.put(deepcopy(jpeg.tobytes()))
if self.__shouldStop:
break
def stopCamera(self):
self.__shouldStop = True
从你所看到的,我只是在读取框架,调整它的大小并存储在两个不同的问题中。没什么太复杂的。 我还有两个负责 mjpeg 流的两个类:
普通视频流.py
from threading import Thread
import traceback
import cv2
class NormalVideoStream(Thread):
def __init__(self, framesQue):
Thread.__init__(self)
self.__frames = framesQue
self.__img = None
def run(self):
while True:
if self.__frames.empty():
continue
self.__img = self.__frames.get()
def gen(self):
while True:
try:
if self.__img is None:
print('Normal stream frame is none')
continue
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + self.__img + b'\r\n')
except:
traceback.print_exc()
print('Normal video stream genenation exception')
和
检测视频流.py
from threading import Thread
import cv2
import traceback
class DetectionVideoStream(Thread):
def __init__(self, framesQue):
Thread.__init__(self)
self.__frames = framesQue
self.__img = None
self.__faceCascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
def run(self):
while True:
if self.__frames.empty():
continue
self.__img = self.__detectFace()
def gen(self):
while True:
try:
if self.__img is None:
print('Detected stream frame is none')
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + self.__img + b'\r\n')
except:
traceback.print_exc()
print('Detection video stream genenation exception')
def __detectFace(self):
retImg = None
try:
img = self.__frames.get()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = self.__faceCascade.detectMultiScale(gray, 1.1, 4)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
(_, encodedImage) = cv2.imencode('.jpg', img)
retImg = encodedImage.tobytes()
except:
traceback.print_exc()
print('Face detection exception')
return retImg
从您在两个流中看到的内容来看,我正在无限循环中从问题中读取相机帧。这两个类也有 gen() 方法,它为站点本身生成框架。唯一不同的是在检测流中我也在做人脸识别。
现在在我的主文件中:
主要.py
from flask import Blueprint, render_template, Response, abort, redirect, url_for
from flask_login import login_required, current_user
from queue import Queue
from . import db
from .Camera import Camera
from .NormalVideoStream import NormalVideoStream
from .DetectionVideoStream import DetectionVideoStream
from .models import User
import cv2
main = Blueprint('main', __name__)
# Queues for both streams
framesNormalQue = Queue(maxsize=0)
framesDetectionQue = Queue(maxsize=0)
print('Queues created')
# RPi camera instance
camera = Camera(cv2.VideoCapture(0), framesNormalQue, framesDetectionQue)
camera.start()
print('Camera thread started')
# Streams
normalStream = NormalVideoStream(framesNormalQue)
detectionStream = DetectionVideoStream(framesDetectionQue)
print('Streams created')
normalStream.start()
print('Normal stream thread started')
detectionStream.start()
print('Detection stream thread started')
@main.route('/')
def index():
return render_template('index.html')
@main.route('/profile', methods=["POST", "GET"])
def profile():
if not current_user.is_authenticated:
abort(403)
return render_template('profile.html', name=current_user.name, id=current_user.id, detectionState=current_user.detectionState)
@main.route('/video_stream/<int:stream_id>')
def video_stream(stream_id):
if not current_user.is_authenticated:
abort(403)
print(f'Current user detection: {current_user.detectionState}')
global detectionStream
global normalStream
stream = None
if current_user.detectionState:
stream = detectionStream
print('Stream set to detection one')
else:
stream = normalStream
print('Stream set to normal one')
return Response(stream.gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
@main.route('/detection')
def detection():
if not current_user.is_authenticated:
abort(403)
if current_user.detectionState:
current_user.detectionState = False
else:
current_user.detectionState = True
user = User.query.filter_by(id=current_user.id)
user.detectionState = current_user.detectionState
db.session.commit()
return redirect(url_for('main.profile', id=current_user.id, user_name=current_user.name))
@main.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.errorhandler(403)
def page_forbidden(e):
return render_template('403.html'), 403
我正在全局创建相机、问题和流对象。此外,当用户登录网站时,他将能够看到实时视频流。还有一个按钮可以更改当前显示的流。
整个项目运行良好,但有一个异常(exception):当我将流更改为检测流时,它会有很大的滞后(大约 10/15 秒),这使得整个项目无法正常工作。试图自己搜索错误/优化但找不到任何东西。我故意在单独的线程上运行所有内容以卸载应用程序,但看起来这还不够。延迟 1-2 秒是可以接受的,但 10+ 就不行了。那么伙计们,也许你可以在这里看到一些错误?或者知道如何优化它?
还需要提及整个应用程序在 RPi 4B 4GB 上运行,我正在桌面上访问网站。默认服务器更改为 Nginx 和 Gunicorn。据我所知,当应用程序运行时,Pi 的 CPU 使用率是 100%。在默认服务器上进行测试时,行为是相同的。猜猜1.5GHz的CPU有足够的功率让它运行起来更流畅。
最佳答案
一个选项是使用
VideoStream
VideoCapture 之所以如此缓慢,是因为 VideoCapture 管道将大部分时间花在读取和解码下一帧上。在读取、解码和返回下一帧时,OpenCV 应用程序被完全阻止。
VideoStream
通过使用队列结构,并发读取、解码并返回当前帧来解决问题。VideoStream
支持PiCamera
和webcam
。
您只需要:
- >
- 安装
imutils
:
- 安装
虚拟环境:
pip install imutils
对于anaconda环境:
conda install -c conda-forge imutils
- >
- 在
main.py
上初始化VideoStream
- 在
- >
import time from imutils.video import VideoStream vs = VideoStream(usePiCamera=True).start() # For PiCamera # vs = VideoStream(usePiCamera=False).start() # For Webcam camera = Camera(vs, framesNormalQue, framesDetectionQue)
- >
- 在你的
Camera.py
- 在你的
- 在
run(self)
方法中:
* ```python
def run(self):
while True:
frame = self.__cam.read()
frame = cv2.resize(frame, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
_, jpeg = cv2.imencode('.jpg', frame)
self.__normalQue.put(jpeg.tobytes())
self.__detectedQue.put(deepcopy(jpeg.tobytes()))
if self.__shouldStop:
break
```
关于python - Flask 应用程序与 opencv 一起工作真的很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63703313/