0


【YOLO5 项目实战】(9)将 YOLO5 部署到 Web 端

欢迎关注『youcans动手学模型』系列
本专栏内容和资源同步到 GitHub/youcans

【YOLO5 项目实战】(9)将 YOLO5 部署到 Web 端

本节详细讲解使用 Flask 框架构建 YOLOv5 模型的 Web 应用程序,将 YOLOv5 模型部署到Web端,实现基于 Web 的图像处理和目标检测系统。

限于篇幅,本节只介绍上传图片进行目标检测。关于视频流的处理,将在后文介绍。

1. Flask 框架的安装与使用

Flask 是一个Python 编写的Web微框架,让我们可以用Python语言快速实现Web服务。只提供Web服务器支持,不提供全栈开发支持。Flask 非常轻量、学习简单,适合小微原型系统的开发。

1.1 Flask 框架的安装与使用

为了使用 Flask 构建计算机视觉 Web 应用程序,首先进行安装:

$ pip3 install flask

安装完成后,通过控制台命令cmd或 Anaconda Prompt (miniconda)进入命令行进行测试。

$ python

>>>import  flask
>>>print(flask.__version__)2.3.1

在这里插入图片描述

1.2 Flask 框架例程

使用Flask框架编写一个显示“Hello Flask!”的web程序,介绍如何配置、调试Flask框架。

(1)新建一个Flask项目,项目的文件树如下。

---项目文件名\
    |---static\
    |---templates\
    |--- server.py

项目默认配置建立static和templates目录,static目录用来存放静态资源,例如图片、js、css文件等,templates目录存放模板文件。网站逻辑保存在Python程序文件server.py中。

# server.pyfrom flask import Flask  # 导入 Flask 包

app = Flask(__name__)# 用当前脚本名称实例化Flask对象@app.route('/')# 将函数绑定到指定URLdefhello():return'Hello Flask!'if __name__ =='__main__':
    app.run()# 启动一个本地开发服务器,激活该网页

例程首先创建 Flask 类的实例,作为Web服务器的网关接口(Web Server Gateway Interface, WSGI)。然后,route()装饰器用于指定URL的触发函数,将触发函数绑定到指定URL。最后,函数返回在浏览器中显示的内容。

(2)运行Python程序。

运行程序server.py,在控制台显示如下的消息,表明Web服务器已启动。

$ python server.py

* Serving Flask app 'server'* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.* Running on http://127.0.0.1:5000/(Press CTRL+C to quit)

(3)在浏览器中访问http://127.0.0.1:5000/。
这将对服务器发送GET请求,该请求将返回相应的消息:

127.0.0.1 - - [26/Apr/2023 21:42:13] “GET / HTTP/1.1” 200 -
127.0.0.1 - - [26/Apr/2023 21:42:13] “GET /favicon.ico HTTP/1.1” 404 -

在这里插入图片描述

1.3 Flask 路由访问

Web框架使用路由技术直接访问所需的页面,而无需从主页导航。

Flask中使用route()装饰器将应用程序的URL绑定到函数,可以实现路由访问。例如:

@app.route('/hello')

将URL

/hello

规则绑定到hello_world()函数。

@app.route('/hello')defhello_world():return'hello world'

用户访问 http://localhost:5000/hello,将在浏览器中显示hello_world()函数的输出。
application对 象的add_url_rule()函数也可用于将URL与函数绑定,如上例所示,使用route()装饰器的目的也由以下表示:

defhello_world():return'hello world'
app.add_url_rule('/','hello', hello_world)

通过向规则参数添加变量部分,可以动态构建URL。此变量部分标记为

<converter:variable_name>

。它作为关键字参数传递给与规则相关联的函数。
例如,在下面的例程中,route()装饰器的规则参数包含附加到URL’/hello’的

<name>

from flask import Flask

app = Flask(__name__)@app.route('/')defhello():return'Hello world!'@app.route('/user')defhello_user():return'Hello user!'@app.route('/hello/<name>')defhello_name(name):return'Hello %s!'% name

if __name__ =='__main__':
    app.run(host='0.0.0.0')

如果在浏览器中输入http://localhost:5000/hello/youcans作为URL,则’youcans’ 将作为参数提供给 hello_name()函数。该请求将返回相应的消息:

* Running on all addresses (0.0.0.0)* Running on http://127.0.0.1:80* Running on http://192.168.3.249:80
Press CTRL+C to quit
127.0.0.1--[27/Apr/202309:06:27]"GET /hello/youcans HTTP/1.1"200-127.0.0.1--[27/Apr/202309:06:27]"GET /favicon.ico HTTP/1.1"404-

在这里插入图片描述

2. Flask 部署 OpenCV DNN 模型的 Web 应用程序

本节介绍上传本地图片,对图片进行处理(使用OpenCV级联检测器检测上传图像中的人脸和人眼),返回处理图片并在网页上进行格式化显示。关心 YOLO5模型部署的读者,可以跳过本节阅读本文第3部分。

2.1 从指定的 url 地址读取图像

(1)首先从指定的 url 地址读取图像。
Flask中使用route()装饰器将应用程序的URL绑定到函数,可以接受URL参数实现路由访问。

@app.route('/enhance', methods=['GET'])defdetail_enhance():# 从指定的 url 地址读取图像with urllib.request.urlopen(request.args.get('url'))as url:
        image_array = np.asarray(bytearray(url.read()), np.uint8)# 从指定的内存缓存中读取数据,并把数据转换成图像格式
    imgCV = cv2.imdecode(image_array,-1)# 转换为 OpenCV 图像

使用函数request.args.get(‘url’)来获取URL参数,构建一个变量URL。当我们访问/user/时,就可以接收到URL地址,然后从URL地址读取图片。
注意不能使用函数cv.imread()读取图像,而要使用函数cv.imdecode()从指定的内存缓存中读取数据,并将传输数据转换为OpenCV图像。

(2)图像处理。
使用 OpenCV 级联分类器进行图像处理。

(3)将图像编码到内存缓冲区。
使用函数cv.imencode()将图像编码为流数据,存储到内存缓存中,方便网络传输。

(4)生成返回页面响应。
使用函数make_response()将缓存中的图像编码封装为响应对象,将页面响应返回到客户端。

2.2 OpenCV 级联分类器类 cv::CascadeClassifier

OpenCV 中定义了级联分类器类 cv::CascadeClassifier。在 Python 语言中,使用接口函数 cv.CascadeClassifier() 从文件加载级联分类器模型,成员函数 cv.CascadeClassifier.detectMultiScale() 对图像进行目标检测。

cv.CascadeClassifier(filename)
cv.CascadeClassifier.detectMultiScale(image[, scaleFactor=1.1, 
minNeighbors=3, flags=0, minSize=Size(), maxSize=Size()]) → objects

OpenCV 提供了 Haar 级联检测器的预训练模型如下,可以从 OpenCV 安装包 \data\haarcascades中提取,或者从【GitHub】opencv/data下载。

使用 Haar 级联检测器检测图片中的人脸的步骤如下:
(1)创建一个 CascadeClassifier 级联分类器对象,使用 load() 方法从 .xml 文件加载级联分类器模型。
(2)读取待检测的图片。
(3)使用detectMultiScale()函数检测图片,返回检测到的边界矩形。
(4)将检测到的边界矩形绘制到检测图片上。

2.3 cvFlask03 项目的构建步骤

项目cvFlask03的文件树如下。

---项目文件名\
|---static\
|---templates\
    |    |---processed.html
    |    |---upload.html
|--- cvFlask03.py

cvFlask03.py中图像处理子程序的代码如下。

# cvFlask03.pydefimageProcessing(filepath):# 图片处理子程序:人脸检测+人眼检测
    imgCV = cv2.imread(filepath)# 从 filepath 路径读取图片
    gray = cv2.cvtColor(imgCV, cv2.COLOR_BGR2GRAY)# 加载 Haar 级联分类器 预训练模型
    model_path ="../data/haarcascade_frontalface_alt2.xml"
    face_detector = cv2.CascadeClassifier(model_path)# <class 'cv2.CascadeClassifier'>
    eye_path ="../data/haarcascade_eye.xml"# 人眼检测器
    eye_detector = cv2.CascadeClassifier(eye_path)# <class 'cv2.CascadeClassifier'># 使用级联分类器检测人脸
    faces = face_detector.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=1,
                                           minSize=(40,40), maxSize=(300,300))# 绘制人脸检测框for x, y, w, h in faces:
        cv2.rectangle(imgCV,(x,y),(x+w,y+h),(0,0,255),3)# 在人脸区域内检测人眼
        roi = gray[y:y+h, x:x+w]# 提取人脸# 检测人眼
        eyes = eye_detector.detectMultiScale(roi, scaleFactor=1.1, minNeighbors=1,
                                             minSize=(15,15), maxSize=(80,80))# 绘制人眼for ex, ey, ew, eh in eyes:
            cv2.rectangle(imgCV,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(255,0,0),2)return imgCV

cvFlask03的脚本运行过程的具体步骤如下。

(1)进入cvFlask03项目的根目录,运行程序cvFlask03.py,启动流媒体服务器。
(2)在浏览器输入URL(http://192.168.3.249:5000/upload),打开upload.html网页。在浏览器点击页面上的选择按钮,选择本地的图片上传,上传的图片保存到

static\images

目录。
(3)程序cvFlask03.py对上传的图片进行人脸检测和人眼检测,在图片上以红色和蓝色方框标记检测到的人脸和人眼。然后激活processed.html网页,显示原始图像和处理后的图像。
(4)手机连接到局域网,按照控制台显示的内容在浏览器输入IP地址(http://192.168.3.249:5000/upload),也可以上传手机中的图片进行处理,结果如图所示。

在这里插入图片描述

3. Flask 部署 YOLOv5 模型的 Web 应用程序

上传本地图片,使用YOLOv5 模型对图片进行处理,返回处理图片并在网页上进行格式化显示。
本例程与上节 OpenCV + Flask 例程类似,只是将检测器换成 YOLOv5 模型。

3.1 Flask 框架的处理流程

(1)首先从指定的 url 地址读取图像。
Flask中使用route()装饰器将应用程序的URL绑定到函数,可以接受URL参数实现路由访问。

(2)图像处理。
使用YOLOv5 模型对图片进行处理。

(3)将图像编码到内存缓冲区。
使用函数cv.imencode()将图像编码为流数据,存储到内存缓存中,方便网络传输。

(4)生成返回页面响应。
使用函数make_response()将缓存中的图像编码封装为响应对象,将页面响应返回到客户端。

3.2 Yolo5Flask01 项目的构建步骤

(1)项目 Yolo5Flask01 的文件树如下。

---项目文件名\
|---templates\
    |    |---index3process.html
    |    |---index3upload.html
|---models\
|---static\
|---utils\
|---weights\
|---Yolo5Flask03.py

(2)编写index3upload.html文档,用于上传本地图片,保存在templates目录。

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>OpenCV+Flask 上传图片</title></head><body><h1 align="center">OpenCV+Flask 例程:上传本地图片处理</h1><p align="center">Developed by youcans@xupt,2023</p><form action="" enctype='multipart/form-data' method='POST'><label>选择按钮:</label><input type="file" name="file" style="margin-top:25px;"/><br><label>选中文件:</label><input type="text"class="txt_input" name="name"  value="png/jpg/jpeg/bmp" style="margin-top:20px;"/><br><label>上传按钮:</label><input type="submit" value="上传图片"class="button-new" style="margin-top:20px;"/><br></form></body></html>

(3)编写index3processed.html文档,用于显示上传图片和处理图片,保存在templates目录。

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>OpenCV+Flask 上传图片</title></head><body><h1 align="center">OpenCV+Flask 例程:上传本地图片进行处理</h1><p align="center">Developed by youcans@xupt,2023</p><form action="" enctype='multipart/form-data' method='POST'><label>选中文件:</label><input type="text"class="txt_input" name="name" value={{userinput}} style="margin-top:10px;"/><br></form><h2 align="center">图片处理结果</h2><table width="700" height="350" border="5" align="center" frame=void><tr><td align="center" valign="middle">原始图片</td><td align="center" valign="middle">处理结果</td></tr><tr><td align="center" valign="middle"><img src="{{ url_for('static', filename= './images/upload.jpg',_t=val1) }}" width="300" height="300" alt="上传的原始图片"/></td><td align="center" valign="middle"><img src="{{ url_for('static', filename= './images/processed.jpg',_t=val1) }}" width="300" height="300" alt="处理完成的图片"/></td></tr></table></body></html>

3.3 Yolo5Flask03 的主程序

Yolo5Flask03.py 图像处理子程序的代码如下。

# Yolo5Flask03.py# Yolo5+Flask 图像处理例程 03# 上传本地图片进行处理,在网页上显示处理结果# Copyright 2024 Youcans, XUPT# Crated:2024-9-10# coding:utf-8import cv2
import torch

from flask import Flask, render_template, request, make_response, jsonify
from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import(check_img_size, non_max_suppression, apply_classifier,
     scale_coords, xyxy2xywh, plot_one_box, strip_optimizer, set_logging)from utils.torch_utils import select_device, load_classifier, time_synchronized
from numpy import random

import os
import time
from datetime import timedelta
from pathlib import Path
import shutil
import platform

# 设置允许的文件格式
ALLOWED_EXTENSIONS =set(['png','jpg','JPG','PNG','bmp'])

app = Flask(__name__)# 用当前脚本名称实例化Flask对象
app.send_file_max_age_default = timedelta(seconds=1)# 设置静态文件缓存过期时间@app.route('/upload', methods=['POST','GET'])# 添加路由defupload():if request.method =='POST':
        f = request.files['file']# 从表单的 file 字段获取文件,file为该表单的name值print("user_input:", f.filename)# tiger02.pngifnot(f and allowed_file(f.filename)):# 检查图片类型return jsonify({"error":1001,"msg":"上传图片必须是 png/jpg/jpeg/bmp 类型"})

        user_input = request.form.get("name")# 获取表单输入的 name 值, png/jpg/jpeg/bmp
        basepath = os.path.dirname(__file__)# 当前文件所在路径 C:\Users\David\cvFlask\# upload_filepath = os.path.join(basepath, 'static\images', secure_filename(f.filename))  # 合成上传图片的保存路径
        upload_filepath = os.path.join(basepath,'static\images','upload.jpg')# 合成上传图片的保存路径print("upload_filepath", upload_filepath)# upload_path, C:\Users\David\cvFlask\static\images\upload.jpg
        f.save(upload_filepath)# 将上传的图片保存到 upload_path 路径# OpenCV 图像处理
        dst = imageProcessing(upload_filepath)# 调用图片处理子程序
        cv2.imwrite(os.path.join(basepath,'static/images','processed.jpg'), dst)# 保存处理后的图片
        user_input = f.filename  # 上传图片的文件名return render_template('index3process.html', userinput=user_input, val1=time.time())return render_template('index3upload.html')defimageProcessing(filepath):# 图片处理子程序
    source = filepath
    imgCV = cv2.imread(source)# 从 filepath 路径读取图片print("source:", source, imgCV.shape)# 使用 YOLOv5 检测图片
    output ='inference/output'
    weights ='weights/yolov5s.pt'# Yolo5 权重模型的路径
    view_img =True
    save_txt ='store_true'
    imgsz =640#check_img_size(imgsz, s=model.stride.max())  # check img_sizewith torch.no_grad():
        detect(output, source, weights, view_img, save_txt, imgsz)

    outFilePath ='inference/output/upload.jpg'
    imgOut = cv2.imread(outFilePath)# 从 filepath 路径读取图片print("output:", outFilePath, imgOut.shape)
    cv2.imshow("out", imgOut)
    key = cv2.waitKey(3000)# 5000ms 后自动关闭
    cv2.destroyAllWindows()return imgOut

defallowed_file(filename):return'.'in filename and filename.rsplit('.',1)[1]in ALLOWED_EXTENSIONS

defdetect(out, source, weights, view_img, save_txt, imgsz, save_img=False):
    webcam =False# webcam = source == '0' or source.startswith('rtsp') or source.startswith('http')# Initialize
    set_logging()
    device = select_device('')if os.path.exists(out):
        shutil.rmtree(out)# delete output folder
    os.makedirs(out)# make new output folder
    half = device.type!='cpu'# half precision only supported on CUDA# Load model
    model = attempt_load(weights, map_location=device)# load FP32 model
    imgsz = check_img_size(imgsz, s=model.stride.max())# check img_sizeif half:
        model.half()# to FP16# Set Dataloader
    vid_path, vid_writer =None,Noneif webcam:
        view_img =True
        torch.backends.cudnn.benchmark =True# set True to speed up constant image size inference
        dataset = LoadStreams(source, img_size=imgsz)else:
        save_img =True
        dataset = LoadImages(source, img_size=imgsz)# Get names and colors
    names = model.module.names ifhasattr(model,'module')else model.names
    colors =[[random.randint(0,255)for _ inrange(3)]for _ inrange(len(names))]# Run inference
    t0 = time.time()
    img = torch.zeros((1,3, imgsz, imgsz), device=device)# init img
    _ = model(img.half()if half else img)if device.type!='cpu'elseNone# run oncefor path, img, im0s, vid_cap in dataset:
        img = torch.from_numpy(img).to(device)
        img = img.half()if half else img.float()# uint8 to fp16/32
        img /=255.0# 0 - 255 to 0.0 - 1.0if img.ndimension()==3:
            img = img.unsqueeze(0)# Inference
        t1 = time_synchronized()# pred = model(img, augment=opt.augment)[0]
        pred = model(img)[0]# Apply NMS
        conf_thres =0.4
        iou_thres =0.5
        pred = non_max_suppression(pred, conf_thres, iou_thres)
        t2 = time_synchronized()# Process detectionsfor i, det inenumerate(pred):# detections per imageif webcam:# batch_size >= 1
                p, s, im0 = path[i],'%g: '% i, im0s[i].copy()else:
                p, s, im0 = path,'', im0s

            save_path =str(Path(out)/ Path(p).name)
            txt_path =str(Path(out)/ Path(p).stem)+('_%g'% dataset.frame if dataset.mode =='video'else'')
            s +='%gx%g '% img.shape[2:]# print string
            gn = torch.tensor(im0.shape)[[1,0,1,0]]# normalization gain whwhif det isnotNoneandlen(det):# Rescale boxes from img_size to im0 size
                det[:,:4]= scale_coords(img.shape[2:], det[:,:4], im0.shape).round()# Print resultsfor c in det[:,-1].unique():
                    n =(det[:,-1]== c).sum()# detections per class
                    s +='%g %ss, '%(n, names[int(c)])# add to string# Write resultsfor*xyxy, conf, cls inreversed(det):if save_txt:# Write to file
                        xywh =(xyxy2xywh(torch.tensor(xyxy).view(1,4))/ gn).view(-1).tolist()# normalized xywhwithopen(txt_path +'.txt','a')as f:
                            f.write(('%g '*5+'\n')%(cls,*xywh))# label formatif save_img or view_img:# Add bbox to image
                        label ='%s %.2f'%(names[int(cls)], conf)
                        plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3)# Print time (inference + NMS)print('%sDone. (%.3fs)'%(s, t2 - t1))# Stream resultsif view_img:
                cv2.imshow(p, im0)if cv2.waitKey(1)==ord('q'):# q to quitraise StopIteration

            # Save results (image with detections)if save_img:if dataset.mode =='images':
                    cv2.imwrite(save_path, im0)else:if vid_path != save_path:# new video
                        vid_path = save_path
                        ifisinstance(vid_writer, cv2.VideoWriter):
                            vid_writer.release()# release previous video writer

                        fourcc ='mp4v'# output video codec
                        fps = vid_cap.get(cv2.CAP_PROP_FPS)
                        w =int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                        h =int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                        vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*fourcc), fps,(w, h))
                    vid_writer.write(im0)if save_txt or save_img:print('Results saved to %s'% Path(out))print('Done. (%.3fs)'%(time.time()- t0))if __name__ =='__main__':# 启动一个本地开发服务器,激活该网页print("URL: http://127.0.0.1:5000/upload")
    app.run(host='0.0.0.0', port=5000, debug=True)# 绑定 IP 地址和端口号

3.4 运行 Yolo5Flask03 项目

Yolo5Flask03 的脚本运行过程的具体步骤如下。

(1)进入Yolo5Flask03 项目的根目录,运行程序Yolo5Flask03.py,启动流媒体服务器。
在这里插入图片描述

(2)在浏览器输入URL(http://192.168.3.96:5000/upload),打开upload.html网页。在浏览器点击页面上的选择按钮,选择本地的图片上传,上传的图片保存到

static\images

目录。

在这里插入图片描述

(3)程序 Yolo5Flask03.py对上传的图片进行目标检测。然后激活processed.html网页,显示原始图像和处理后的图像。

(4)手机连接到局域网,按照控制台显示的内容在浏览器输入IP地址(http://192.168.3.96:5000/upload),也可以上传手机中的图片进行处理,结果如图所示。

请添加图片描述

4. YOLOv5 官方 Flask REST API‌ 的使用

Flask REST API是利用Flask框架及其扩展构建的RESTful Web服务,这些服务遵循 REST 架构风格,通过HTTP请求和响应进行客户端和服务器之间的交互,提供数据资源的增删改查等功能。

4.1 Flask REST API‌ 例程

YOLOv5 官方已经提供了一个简单的 Flask REST API‌,位于 utils\flask_rest_api。其中包括以下文件:

  • restapi.py,用于运行 Flask REST API 以构建一个 YOLOv5s 模型。
  • example_request.py,用于执行测试请求。
  • readme.md,说明文档

官方文档中的例程 restapi.py 是 从PyTorch Hub 部署YOLOv5s模型,本文将其修改为从本地 部署 YOLOv5s 模型,程序如下:

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Run a Flask REST API exposing one or more YOLOv5s models."""import argparse
import io

import torch
from flask import Flask, request
from PIL import Image

app = Flask(__name__)
models ={}

DETECTION_URL ="/v1/object-detection/<model>"@app.route(DETECTION_URL, methods=["POST"])defpredict(model):"""Predict and return object detections in JSON format given an image and model name via a Flask REST API POST
    request.
    """if request.method !="POST":returnif request.files.get("image"):# Method 1# with request.files["image"] as f:#     im = Image.open(io.BytesIO(f.read()))# Method 2
        im_file = request.files["image"]
        im_bytes = im_file.read()
        im = Image.open(io.BytesIO(im_bytes))if model in models:
            results = models[model](im, size=640)# reduce size=320 for faster inferencereturn results.pandas().xyxy[0].to_json(orient="records")if __name__ =="__main__":
    parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")
    parser.add_argument("--port", default=5000,type=int,help="port number")
    parser.add_argument("--model", nargs="+", default=["yolov5s"],help="model(s) to run, i.e. --model yolov5n yolov5s")
    opt = parser.parse_args()for m in opt.model:# 从本地加载 YOLOv5 模型
        models[m]= torch.hub.load("./", m, source="local")

    app.run(host="0.0.0.0", port=opt.port)# debug=True causes Restarting with stat

对官方的测试请求程序,修改测试图像的路径为 data/images/zidane.jpg(根据测试图片的位置决定)。

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Perform test request."""import pprint

import requests

DETECTION_URL ="http://localhost:5000/v1/object-detection/yolov5s"
IMAGE ="data/images/zidane.jpg"# Read imagewithopen(IMAGE,"rb")as f:
    image_data = f.read()

response = requests.post(DETECTION_URL, files={"image": image_data}).json()

pprint.pprint(response)

4.2 YOLOv5s 模型部署的步骤

(1)在 PyCharm 的命令行,先运行 restapi.py 部署 YOLOv5s 模型:

> python3 restapi.py --port5000

(2)然后运行测试请求程序:

$ python example_request.py

(3)运行结果
模型推理结果以JSON响应的形式返回:

[{"class":0,"confidence":0.8900438547,"height":0.9318675399,"name":"person","width":0.3264600933,"xcenter":0.7438579798,"ycenter":0.5207948685},{"class":0,"confidence":0.8440024257,"height":0.7155083418,"name":"person","width":0.6546785235,"xcenter":0.427829951,"ycenter":0.6334488392},{"class":27,"confidence":0.3771208823,"height":0.3902671337,"name":"tie","width":0.0696444362,"xcenter":0.3675483763,"ycenter":0.7991207838},{"class":27,"confidence":0.3527112305,"height":0.1540903747,"name":"tie","width":0.0336618312,"xcenter":0.7814827561,"ycenter":0.5065554976}]

以上,就完成了一个基本的 YOLOv5s 模型部署和测试请求的过程。

5. 基于图片文件的 Flask REST API

对官方例程进行修改,使其响应基于文件的请求。从指定的文件读取图像,进行模型预测。

例程如下:

(1)例程 FlaskRestAPI2.py,用于部署 YOLOv5s 模型:

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Run a Flask REST API exposing one or more YOLOv5s models."""import argparse
import io
import numpy as np
import cv2
import torch
from flask import Flask, request
from PIL import Image

app = Flask(__name__)
models ={}

DETECTION_URL ="/v1/object-detection/<model>"@app.route(DETECTION_URL, methods=["POST"])defpredict(model):"""Predict and return object detections in JSON format given an image and model name via a Flask REST API POST
    request.
    """if request.method !="POST":returnif request.data:
        img = cv2.imdecode(np.frombuffer(request.data, dtype=np.uint8), cv2.IMREAD_COLOR)if model in models:
            results = models[model](img)# reduce size=320 for faster inferencereturn results.pandas().xyxy[0].to_json(orient="records")if request.files.get("image"):
        im_file = request.files["image"]
        im_bytes = im_file.read()
        im = Image.open(io.BytesIO(im_bytes))if model in models:
            results = models[model](im, size=640)# reduce size=320 for faster inferencereturn results.pandas().xyxy[0].to_json(orient="records")if __name__ =="__main__":
    parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")
    parser.add_argument("--port", default=5000,type=int,help="port number")
    parser.add_argument("--model", nargs="+", default=["yolov5s"],help="model(s) to run, i.e. --model yolov5n yolov5s")
    opt = parser.parse_args()for m in opt.model:
        models[m]= torch.hub.load("./", m, source="local")

    app.run(host="0.0.0.0", port=opt.port)# debug=True causes Restarting with stat

(2)例程 RequestDemo2.py,用于发送请求:

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Perform test request."""import pprint
import requests
import cv2

DETECTION_URL ="http://localhost:5000/v1/object-detection/yolov5s"
filepath ="data/images/zidane.jpg"

imgCV = cv2.imread(filepath)# 从 filepath 路径读取图片
imgCV = cv2.cvtColor(imgCV, cv2.COLOR_BGR2RGB)# 从指定的内存缓存中读取数据,并把数据转换成图像格式
img = cv2.imencode(".jpg", imgCV)[1].tobytes()

response = requests.post(DETECTION_URL, data=img).json()

pprint.pprint(response)

(2)部署YOLOv5s 模型和发送请求:

  1. 在 PyCharm 的命令行,先运行 FlaskRestAPI2.py 部署 YOLOv5s 模型:

python3 FlaskRestAPI2.py --port 5000

  1. 然后运行测试请求程序 RequestDemo2:
$ python RequestDemo2.py
  1. 运行结果与上节相同,模型推理结果以JSON响应的形式返回。

6. 返回检测结果图片

在上例基础上进行修改,使其响应基于文件的请求,并返回模型预测结果的图片。

例程如下:

(1)例程 FlaskRestAPI3.py,用于部署 YOLOv5s 模型:

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Run a Flask REST API exposing one or more YOLOv5s models."""# 运行 Flask REST API 以构建一个 YOLOv5s 模型,响应基于图片文件的请求,并返回模型预测结果图片import argparse
import io
import numpy as np
import cv2
import torch
from flask import Flask, request
from PIL import Image

app = Flask(__name__)
models ={}

DETECTION_URL ="/v1/object-detection/<model>"@app.route(DETECTION_URL, methods=["POST"])defpredict(model):"""Predict and return object detections in JSON format given an image and model name via a Flask REST API POST
    request.
    """if request.method !="POST":returnif request.data:
        img = cv2.imdecode(np.frombuffer(request.data, dtype=np.uint8), cv2.IMREAD_COLOR)if model in models:
            results = models[model](img)# reduce size=320 for faster inference
            results = results.render()[0]return cv2.imencode(".jpg", results)[1].tobytes()if request.files.get("image"):# Method 1# with request.files["image"] as f:#     im = Image.open(io.BytesIO(f.read()))# Method 2
        im_file = request.files["image"]
        im_bytes = im_file.read()
        im = Image.open(io.BytesIO(im_bytes))if model in models:
            results = models[model](im, size=640)# reduce size=320 for faster inferencereturn results.pandas().xyxy[0].to_json(orient="records")if __name__ =="__main__":
    parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")
    parser.add_argument("--port", default=5000,type=int,help="port number")
    parser.add_argument("--model", nargs="+", default=["yolov5s"],help="model(s) to run, i.e. --model yolov5n yolov5s")
    opt = parser.parse_args()for m in opt.model:
        models[m]= torch.hub.load("./", m, source="local")

    app.run(host="0.0.0.0", port=opt.port)# debug=True causes Restarting with stat

(2)例程 RequestDemo3.py,用于发送请求和处理返回的预测结果图片:

# Ultralytics YOLOv5 🚀, AGPL-3.0 license"""Perform test request."""import pprint
import requests
import cv2
import numpy as np
import matplotlib.pyplot as plt

DETECTION_URL ="http://localhost:5000/v1/object-detection/yolov5s"
filepath ="data/images/zidane.jpg"

imgCV = cv2.imread(filepath)# 从 filepath 路径读取图片
imgCV = cv2.cvtColor(imgCV, cv2.COLOR_BGR2RGB)# 从指定的内存缓存中读取数据,并把数据转换成图像格式
img = cv2.imencode(".jpg", imgCV)[1].tobytes()

response = requests.post(DETECTION_URL, data=img)# 对返回的数据流进行解码,得到处理后的图片
img = cv2.imdecode(np.frombuffer(response.content, dtype=np.uint8), cv2.IMREAD_COLOR)# pprint.pprint(response)
plt.imshow(img)
plt.show()

(2)部署YOLOv5s 模型和发送请求:

  1. 在 PyCharm 的命令行,先运行 FlaskRestAPI3.py 部署 YOLOv5s 模型:

python3 FlaskRestAPI3.py --port 5000

  1. 然后运行测试请求程序 RequestDemo3:
$ python RequestDemo3.py
  1. 运行结果,返回模型推理结果的图片,解码后显示结果如下。

在这里插入图片描述

限于篇幅,本节只介绍上传图片进行目标检测。关于视频流的处理,将在后文介绍。

【本节完】

版权声明:
欢迎关注『youcans动手学模型』系列
转发请注明原文链接:
【YOLO5 项目实战】(9)将 YOLO5 部署到 Web 端
Copyright 2024 youcans, Xidian
Crated:2024-09-10

标签: 人工智能 YOLO flask

本文转载自: https://blog.csdn.net/youcans/article/details/141105006
版权归原作者 youcans_ 所有, 如有侵权,请联系我们删除。

“【YOLO5 项目实战】(9)将 YOLO5 部署到 Web 端”的评论:

还没有评论