Flask应用程序在OpenCV中的运行速度非常慢

如何解决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同时支持PiCamerawebcam

您需要做的只是:

    1. 安装imutils

  • 对于虚拟环境:pip install imutils


  • 对于anaconda环境:conda install -c conda-forge imutils

    1. 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)
    
    1. 在您的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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-