如何解决Flask应用程序在OpenCV中的运行速度非常慢
我有一个flask应用程序,它可以从相机读取帧并将其流传输到网站。
Camera.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流:
NormalVideoStream.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')
和
DetectionVideoStream.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),2)
(_,encodedImage) = cv2.imencode('.jpg',img)
retImg = encodedImage.tobytes()
except:
traceback.print_exc()
print('Face detection exception')
return retImg
从两个流中可以看到的内容中,我正在无限循环地从队列中读取摄像机帧。这两个类都具有gen()方法,该方法生成框架以定位自身。唯一的区别是,在检测流中,我也在进行人脸识别。
现在在我的主文件中:
main.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',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
我正在全局创建相机,查询和流对象。同样,当用户登录网站时,他将能够看到实时视频流。还有一个按钮可以更改当前显示的流。
整个项目运行良好,但有一个例外:当我将流更改为检测到一个项目时,它将有巨大的滞后时间(大约10/15秒),这会使整个功能失效。试图自己搜索错误/优化,但找不到任何东西。我故意在单独的线程上运行所有内容以卸载应用程序,但看起来这还不够。延迟1-2秒是可以接受的,但不能超过10秒。伙计们,也许您可以在这里看到一些错误?还是知道如何优化它?
还需要提及的是整个应用程序都在RPi 4B 4GB上运行,并且我正在桌面上访问网站。默认服务器更改为Nginx和Gunicorn。从我可以看到,当应用程序运行时,Pi的CPU使用率为100%。在默认服务器上进行测试时,行为是相同的。猜想1,5 GHz CPU有足够的功率来使其运行更平稳。
解决方法
甚至我遇到的问题之一就是重新编码和解码。就像 Opencv 的编码器太慢一样,因此请尝试使用 simplejpeg 中的编码器。使用 pip3 install simplejpeg ,关于使用 cv2.imencode(),使用 simplejpeg.encode_jpeg()
,-
一个选项正在使用
VideoStream
-
VideoCapture之所以这么慢是因为VideoCapture管道在读取和解码下一帧上花费了最多的时间。在读取,解码和返回下一帧时,OpenCV应用程序被完全阻止。
-
VideoStream
通过使用队列结构,同时读取,解码和返回当前帧来解决该问题。 -
VideoStream
同时支持PiCamera
和webcam
。
您需要做的只是:
-
- 安装
imutils
:
- 安装
-
对于虚拟环境:
pip install imutils
-
对于anaconda环境:
conda install -c conda-forge imutils
-
- 在
VideoStream
上初始化main.py
- 在
-
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
```
,
对于您的问题,我并不感到特别惊讶,因为通常会使用大量的计算时间进行“检测”,因为 执行级联分类算法是一项艰巨的计算任务。 我找到了一个比较级联分类算法的性能link
的来源一个简单的解决方案是在以下情况下降低帧速率 处理您的检测。 降低性能需求的简单实现可能像跳过计数器之类的
frameSkipFactor = 3 # use every third frame
frameCounter = 0
if (frameCounter%frameSkipFactor==0):
#process
else:
print("ignore frame",frameCounter)
frameCounter+=1
尽管如此,您仍会滞后,因为检测计算 将始终产生时间偏移。 如果您打算构建“实时”分类摄像头系统,请寻找另一类分类算法, 专门针对该用例设计的。 我在这里进行了讨论:real time class algos
另一种解决方案可能是使用比rpi更大的“硬件锤子”,例如通过Cuda等实现算法的GPU实现。
,我发现获得慢帧的主要原因是您具有更高的分辨率和帧速率。
为了解决这个问题,您可以将分辨率更改为 640 宽 x 480 高,fps 30 或更少,最高 5 fps(如果您只需要面部检测),并且可以通过以下方式实现 OpenCV 的大小调整(cv2.resize() 函数) fx 和 fy 的 0.25 调整因子。如果您不想要更高分辨率的流,请执行此操作。与 opencv 一起顺利工作。我想尝试一下 VideoStream 代码(来自 imutils),因为它也被 PyImageSearch 的 Adrian Rosebrock 使用。我会在以后的项目中使用。
作为参考,我在下面发布了代码片段。特别感谢 Adrian Rosebrock 和ageitey face_recognition 因为他们的代码帮助我完成了它。
class Camera(object):
SHRINK_RATIO = 0.25
FPS = 5
FRAME_RATE = 5
WIDTH = 640
HEIGHT = 480
def __init__(self):
""" initializing camera with settings """
self.cap = cv2.VideoCapture(0)
self.cap.set(3,Camera.WIDTH)
self.cap.set(4,Camera.HEIGHT)
self.cap.set(5,Camera.FPS)
self.cap.set(7,Camera.FRAME_RATE)
def get_frame(self):
""" get frames from the camera """
success,frame = self.cap.read()
if success == True:
# Resizing to 0.25 scale
rescale_frame = cv2.resize(frame,fx= Camera.SHRINK_RATIO,fy=Camera.SHRINK_RATIO)
cascPath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascPath)
gray = cv2.cvtColor(rescale_frame,cv2.COLOR_BGR2GRAY)
# detecting faces
faces = faceCascade.detectMultiScale(
gray,scaleFactor=1.1,minNeighbors=5,minSize=(30,30)
)
# Draw a rectangle around the faces
if len(faces) != 0:
for (x,y,w,h) in faces:
x *=4
y *=4
w *=4
h *=4
# Draw rectangle across faces in current frame
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
# return frame outside if-statement
return frame
还要记住,JPEG 是加速编解码器,请使用:
cv2.imencode(".jpg",frame)[1].tobytes()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。