1.前言
上一篇文章flask部署 目标检测算法中讲到可以将检测算法封装到flask框架中进行web端展示,但在实际应用中发现一些问题并进行了解决,在本文中进行补充。
2.利用线程,提高flask程序运行效率
flask web端访问时,每次都会从头加载程序,导致每次访问页面刷新率很低或者需要等好一段时间才能刷新成功,可以利用线程达到仅在程序启动后第一次从头加载程序,而后刷新获取到实时视频流即可,不再从头加载程序。
实例(部分代码):
import threading
from flask import Flask, render_template, Response
app = Flask(__name__)
lock = threading.Lock() #线程锁
result_img = None #初始化检测结果,并定义为全局变量
#zed获取视频,并进行目标检测,获取检测结果
def camera(cfgfile, weightfile):
import cv2
zed = sl.Camera()
init_params = sl.InitParameters()
init_params.camera_resolution = sl.RESOLUTION.HD720 #分辨率,分辨率越高,成像后的图像像素数就越高,图像就越清晰。
init_params.camera_fps = 30 # Set fps at 30,帧率越高,视频越流畅,最低是30
(。。。省略部分代码。。目标检测过程代码。。。。)
##结尾,获取检测结果图片,设为全局变量,便于后续函数调用
global result_img
result_img = plot_boxes_cv2(img, boxes[0], savename=None, class_names=class_names)
##相机推流: 获取检测结果视频流,并返回
def generate():
global result_img, lock #result_img 设置为全局变量
while True:
with lock:
if result_img is None:
continue
(flag, encodedImage) = cv2.imencode(".jpg", result_img) #将 OpenCV 图像转换为 JPEG 图像
if not flag:
continue
# yield the output frame in the byte format
yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encodedImage) + b'\r\n')
#相机拉流:调用generate函数实现向网页返回每一帧图片的编码,将视频流传输到web浏览器
@app.route("/tt_demo")
def tt_demo():
return Response(generate(),mimetype="multipart/x-mixed-replace; boundary=frame")
# 前端页面
@app.route('/bdlf')
def index():
return render_template("bdlf.html")
#主函数,线程调用
if __name__ =="__main__":
host="0.0.0.0"
port=7000
debug=False
use_reloader=False
t = threading.Thread(target=camera, args=(args.cfgfile, args.weightfile), daemon = True)
# t.daemon()
t.start() #线程启动
# app.run(host,port,debug,use_reloader)
t2 = threading.Thread(target=app.run, args=(host,port,debug,use_reloader))
t2.start()
前端引用
<img src="{{ url_for('tt_demo') }}"> ##与后端入口函数名保持一致
3.知识小记
(1)线程传参
- 使用args 传递参数 threading.Thread(target=sing, args=(10, 100, 100))
- 使用kwargs传递参数 threading.Thread(target=sing, kwargs={“a”: 10, “b”:100, “c”: 100})
- 同时使用 args 和 kwargs 传递参数 threading.Thread(target=sing, args=(10, ), kwargs={“b”: 100,“c”: 100})
(2) 守护线程
- 设置守护主线程有两种方式:
threading.Thread(target=show_info, daemon=True)
线程对象.setDaemon(True) - 一定在线程执行start()之前
- 守护线程就是会随着主线程的结束而结束。即:主进程在其代码结束后,守护进程在此时就被回收;主进程会一直等非守护子进程都运行完毕后回收子进程的资源
- 主线程在有非守护线程存在的情况下,设置守护线程作用不大
(3)查看线程数
- threading.enumerate()返回当前存活的threading.Thread线程对象列表
(4)线程锁
with temp_lock:
# do something...
相当于以下代码
temp_lock.acquire() #上锁
try:
# do something...
finally:
temp_lock.release() #释放锁
也就是在运行的时候对语句块的内容上锁了,运行完后再释放了这个锁。
图片怎么居中啊???