flask+gunicorn多线程部署yolov5

flask部署过程中受制于网络传输,和存读图片时间,检测存在较高延迟,通过多线程部署以达到降低延迟的目的。gunicorn可实现多线程和生产环境部署。

gunicorn目前支持linux,不支持windows,所以在linux里面安装。

在linux建立虚拟环境,正常环境可能出问题。之后

pip install gunicorn gevent

安装好之后,找不到gunicorn,就为gunicorn添加软链接。

server.py

from flask import Flask, request
import torch
import io
from io import BytesIO
import base64
import json
from PIL import Image
#加载模型
model = torch.hub.load('ultralytics/yolov5','custom',path = 'weight/face.pt',force_reload=False,source='local')

# 设置允许上传的文件格式
ALLOW_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif']
# 判断文件后缀是否存在
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[-1] in ALLOW_EXTENSIONS
# 定义路由
app = Flask(__name__)
@app.route("/photo", methods=['POST','GET'])
def uploads():
    if not request.method == "POST":
        return
    if request.files.get("image"):
        image_file = request.files["image"]
        file_name = image_file.filename
        if allowed_file(file_name)==True:
            image_bytes = image_file.read()
            img = Image.open(io.BytesIO(image_bytes))
            results = model(img, size=640)
            #str = results.pandas().xyxy[0].to_json(orient="records")

		  #删除检测框坐标,看着比较烦人
            sss = results.pandas().xyxy[0]
            sss.drop(columns=['xmin', 'ymin', 'xmax', 'ymax'], inplace=True)
            str = sss.to_json(orient="records")
            
            im = request.values.to_dict()
            value = im['download_image']
            if value == 'True':
                # results.imgs  # array of original images (as np array) passed to model for inference
                results.render()  # updates results.imgs with boxes and labels
                image_str = []
                for img in results.imgs:
                    buffered = BytesIO()
                    img_base64 = Image.fromarray(img)
                    img_base64.save(buffered, format="JPEG")
                    image = base64.b64encode(buffered.getvalue()).decode('utf-8')  # base64 encoded image with results
                    image_str.append(image)
                s = {'result': str, 'image': image_str[0]}
            else:
                s = {'result': str}
        else:
            s = {file_name: "格式错误,无法正常打开",'error': 'error',}
            s = json.dumps(s)
        return s
if __name__ == "__main__":
    from werkzeug.middleware.proxy_fix import ProxyFix
    app.wsgi_app = ProxyFix(app.wsgi_app)
    app.run()

使用gunicorn多线程部署

创建一个配置文件

gunicorn_config.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# 监听本机的端口
bind = "127.0.0.1:5000"
# 未决连接的最大数量,即等待服务的客户的数量
backlog = 2048
# 进程数
workers = 2
# 线程数
threads= 4
# 工作模式为gevent
worker_class = 'gevent'
# 最大客户端并发数量,默认情况下这个值为1000。
worker_connections = 1000
# 超时 默认30秒
timeout = 120
# 连接上等待请求的秒数,默认情况下值为2
keepalive = 50
# 根目录,server.py所在目录
chdir = '/opt/examine-face'
#记录PID
pidfile='gunicorn.pid'

启动服务:

server是服务端的文件名

app是server的部署方式

gunicorn -c gunicorn_config.py server:app

客户端多线程访问

client.py

import requests
import os
import time
from threading import Thread
from PIL import Image
from io import BytesIO
import base64
#设置一下图片的后缀
ALLOW_EXTENSIONS = ['png', 'jpg', 'jpeg', 'gif','pic','bmp']
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[-1] in ALLOW_EXTENSIONS
aa = []
def make_txt(path):
    #这个脚本是基于一个文件夹下面有很多文件夹,图片存在文件夹里面,找出设置后缀格式的文件,添加到表格
    if os.path.isdir(path):
        for i in os.listdir(path):
            new_path = os.path.join(path+'/',i)
            make_txt(new_path)
    else:
        if allowed_file(path) == True:
            aa.append(path)
    return aa

def jpg_compress(filelist,url,download_image):
    #图片传输获取结果的代码
    for i in filelist:
        #获取每张图片的路径,以二进制格式打开
        img_path = os.path.join(path+'/',i)
        file_name = img_path.split('/')[-1]
        file = open(img_path, 'rb')
        files = {'image': file}
        #设置一个参数,服务端读取参数判断是否返回图片  True或者False。
        data = {'download_image': download_image}
        # 设置请求头,不保持每个线程的连接,防止线程太多,报错
        headers = {
            'Connection': 'close',
        }
        r = requests.post(url, headers=headers,files=files,data = data)
        file.close()
        result = r.json()
        # result.pop('name')
        result['path'] = img_path
        try:
            # 对图片进行解码并打开
            img = Image.open(BytesIO(base64.b64decode(result['image'])))
            # 结果删除图片编码进行打印
            result.pop('image')
            # if result['result'] == '[]':
            print(result)
            # 储存图片,在本项目目录,需要建立一个名字为3的文件夹
            if result['result'] != '[]':
                img.save('3/'+file_name)
                # img.show()#展示图片
                img.close()
        except KeyError:
            print(result)
def multi_thread_process(list,num_threads,url,download_image):
    #写线程代码
    if num_threads == 1:
        jpg_compress(list,url,download_image)
        return
    filelist_total = list
    filenum = len(filelist_total)
    filenum_each_thread = int(filenum/num_threads)
    thread_list = []
    for i in range(num_threads-1):
        time.sleep(0.1)
        thread_list.append(Thread(target=jpg_compress, args=(filelist_total[i*filenum_each_thread : (i+1)*filenum_each_thread],url,download_image)))
    thread_list.append(Thread(target=jpg_compress, args=(filelist_total[(num_threads-1)*filenum_each_thread :],url,download_image)))
    for th in thread_list:
        th.start()
    for th in thread_list:
        th.join()

if __name__ == '__main__':
    #设置一下需要的参数
    #路径,这个路径下面的所有图片,包括下属文件夹的所有图片都会被读取。
    path = 'C:/Users/Administrator/Desktop/test/2'
    #设置一个目标服务器的地址
    url = 'http://127.0.0.1:5000/photo'
    #是否下载图片,True  False
    download_image = False
    #设置线程数量,线程数量基于部署的线程数,不要太大,不然会报错
    num_threads = 8
    #计时,开始
    a = time.time()
    #调用路径函数,读取该路径及下属文件夹所有的图片存入表格
    list = make_txt(path)
    #调用线程函数,线程函数里面会调用传输图片的函数,输入所需要的参数,进行端口访问,获取结果
    multi_thread_process(list,num_threads,url,download_image)
    #计时结束
    b = time.time()
    num = len(list)
    cost = b - a
    #计算耗时情况
    print('耗时', cost)
    print('图片总数:', num)
    print('平均耗时:',cost/num)

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值