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

youcans_ 2024-10-13 10:33:01 阅读 84

欢迎关注『youcans动手学模型』系列

本专栏内容和资源同步到 GitHub/youcans

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

1. Flask 框架的安装与使用1.1 Flask 框架的安装与使用1.2 Flask 框架例程1.3 Flask 路由访问

2. Flask 部署 OpenCV DNN 模型的 Web 应用程序2.1 从指定的 url 地址读取图像2.2 OpenCV 级联分类器类 cv::CascadeClassifier2.3 cvFlask03 项目的构建步骤

3. Flask 部署 YOLOv5 模型的 Web 应用程序3.1 Flask 框架的处理流程3.2 Yolo5Flask01 项目的构建步骤3.3 Yolo5Flask03 的主程序3.4 运行 Yolo5Flask03 项目

4. YOLOv5 官方 Flask REST API‌ 的使用4.1 Flask REST API‌ 例程4.2 YOLOv5s 模型部署的步骤

5. 基于图片文件的 Flask REST API6. 返回检测结果图片

本节详细讲解使用 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)进入命令行进行测试。

<code>$ python

>>> import flask

>>> print(flask.__version__)

2.3.1

在这里插入图片描述

1.2 Flask 框架例程

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

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

<code>---项目文件名\

|---static\

|---templates\

|--- server.py

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

# server.py

from flask import Flask # 导入 Flask 包

app = Flask(__name__) # 用当前脚本名称实例化Flask对象

@app.route('/') # 将函数绑定到指定URL

def hello():

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绑定到函数,可以实现路由访问。例如:<code>@app.route('/hello')将URL/hello规则绑定到hello_world()函数。

@app.route('/hello')

def hello_world():

return 'hello world'

用户访问 http://localhost:5000/hello,将在浏览器中显示hello_world()函数的输出。

application对 象的add_url_rule()函数也可用于将URL与函数绑定,如上例所示,使用route()装饰器的目的也由以下表示:

def hello_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('/')

def hello():

return 'Hello world!'

@app.route('/user')

def hello_user():

return 'Hello user!'

@app.route('/hello/<name>')

def hello_name(name):

return 'Hello %s!' % name

if __name__ == '__main__':

app.run(host='0.0.0.0')code>

如果在浏览器中输入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/2023 09:06:27] "GET /hello/youcans HTTP/1.1" 200 -

127.0.0.1 - - [27/Apr/2023 09: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参数实现路由访问。

<code>@app.route('/enhance', methods=['GET'])

def detail_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.py

def imageProcessing(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">code>

<head>

<meta charset="UTF-8">code>

<title>OpenCV+Flask 上传图片</title>

</head>

<body>

<h1 align="center">OpenCV+Flask 例程:上传本地图片处理</h1>code>

<p align="center">Developed by youcans@xupt,2023</p>code>

<form action="" enctype='multipart/form-data' method='POST'>code>

<label>选择按钮:</label>

<input type="file" name="file" style="margin-top:25px;"/>code>

<br>

<label>选中文件:</label>

<input type="text" class="txt_input" name="name" value="png/jpg/jpeg/bmp" style="margin-top:20px;"/>code>

<br>

<label>上传按钮:</label>

<input type="submit" value="上传图片" class="button-new" style="margin-top:20px;"/>code>

<br>

</form>

</body>

</html>

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

<!DOCTYPE html>

<html lang="en">code>

<head>

<meta charset="UTF-8">code>

<title>OpenCV+Flask 上传图片</title>

</head>

<body>

<h1 align="center">OpenCV+Flask 例程:上传本地图片进行处理</h1>code>

<p align="center">Developed by youcans@xupt, 2023</p> code>

<form action="" enctype='multipart/form-data' method='POST'>code>

<label>选中文件:</label>

<input type="text" class="txt_input" name="name" value={ { userinput}} style="margin-top:10px;"/>code>

<br>

</form>

<h2 align="center">图片处理结果</h2>code>

<table width="700" height="350" border="5" align="center" frame=void>code>

<tr>

<td align="center" valign="middle">原始图片</td>code>

<td align="center" valign="middle">处理结果</td>code>

</tr>

<tr>

<td align="center" valign="middle">code>

<img src="{ { url_for('static', filename= './images/upload.jpg',_t=val1) }}" width="300" height="300" alt="上传的原始图片"/>code>

</td>

<td align="center" valign="middle">code>

<img src="{ { url_for('static', filename= './images/processed.jpg',_t=val1) }}" width="300" height="300" alt="处理完成的图片"/>code>

</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-8

import 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']) # 添加路由

def upload():

if request.method == 'POST':

f = request.files['file'] # 从表单的 file 字段获取文件,file为该表单的name值

print("user_input:", f.filename) # tiger02.png

if not (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')

def imageProcessing(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_size

with 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

def allowed_file(filename):

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

def detect(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_size

if half:

model.half() # to FP16

# Set Dataloader

vid_path, vid_writer = None, None

if 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 if hasattr(model, 'module') else model.names

colors = [[random.randint(0, 255) for _ in range(3)] for _ in range(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' else None # run once

for 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.0

if 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 detections

for i, det in enumerate(pred): # detections per image

if 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 whwh

if det is not None and len(det):

# Rescale boxes from img_size to im0 size

det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

# Print results

for c in det[:, -1].unique():

n = (det[:, -1] == c).sum() # detections per class

s += '%g %ss, ' % (n, names[int(c)]) # add to string

# Write results

for *xyxy, conf, cls in reversed(det):

if save_txt: # Write to file

xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh

with open(txt_path + '.txt', 'a') as f:

f.write(('%g ' * 5 + '\n') % (cls, *xywh)) # label format

if 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 results

if view_img:

cv2.imshow(p, im0)

if cv2.waitKey(1) == ord('q'): # q to quit

raise 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

if isinstance(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 地址和端口号code>

3.4 运行 Yolo5Flask03 项目

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

(1)进入Yolo5Flask03 项目的根目录,运行程序Yolo5Flask03.py,启动流媒体服务器。

在这里插入图片描述

(2)在浏览器输入URL(http://192.168.3.96:5000/upload),打开upload.html网页。在浏览器点击页面上的选择按钮,选择本地的图片上传,上传的图片保存到<code>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 模型,程序如下:

<code># 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"])

def predict(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":

return

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 inference

return results.pandas().xyxy[0].to_json(orient="records")code>

if __name__ == "__main__":

parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")code>

parser.add_argument("--port", default=5000, type=int, help="port number")code>

parser.add_argument("--model", nargs="+", default=["yolov5s"], help="model(s) to run, i.e. --model yolov5n yolov5s")code>

opt = parser.parse_args()

for m in opt.model:

# 从本地加载 YOLOv5 模型

models[m] = torch.hub.load("./", m, source="local")code>

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

对官方的测试请求程序,修改测试图像的路径为 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 image

with open(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 --port 5000

(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"])

def predict(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":

return

if 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

return results.pandas().xyxy[0].to_json(orient="records")code>

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 inference

return results.pandas().xyxy[0].to_json(orient="records")code>

if __name__ == "__main__":

parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")code>

parser.add_argument("--port", default=5000, type=int, help="port number")code>

parser.add_argument("--model", nargs="+", default=["yolov5s"], help="model(s) to run, i.e. --model yolov5n yolov5s")code>

opt = parser.parse_args()

for m in opt.model:

models[m] = torch.hub.load("./", m, source="local")code>

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

(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 模型和发送请求:

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

python3 FlaskRestAPI2.py --port 5000

然后运行测试请求程序 RequestDemo2:

$ python RequestDemo2.py

运行结果与上节相同,模型推理结果以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"])

def predict(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":

return

if 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 inference

return results.pandas().xyxy[0].to_json(orient="records")code>

if __name__ == "__main__":

parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")code>

parser.add_argument("--port", default=5000, type=int, help="port number")code>

parser.add_argument("--model", nargs="+", default=["yolov5s"], help="model(s) to run, i.e. --model yolov5n yolov5s")code>

opt = parser.parse_args()

for m in opt.model:

models[m] = torch.hub.load("./", m, source="local")code>

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

(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 模型和发送请求:

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

python3 FlaskRestAPI3.py --port 5000

然后运行测试请求程序 RequestDemo3:

$ python RequestDemo3.py

运行结果,返回模型推理结果的图片,解码后显示结果如下。

在这里插入图片描述

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

【本节完】

版权声明:

欢迎关注『youcans动手学模型』系列

转发请注明原文链接:

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

Copyright 2024 youcans, Xidian

Crated:2024-09-10



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。