这个小项目是基于flask微型目标检测服务。使用的是YOLOv3版本,使用的是在coco数据集上已经训练好的参数。整个目录结构如下(我运行的环境是window10,pycharm):
其中:
- cfg是YOLOv3的配置文件,包括权重,网络结构和coco数据集的标签;
- static是通过网络访问该服务时被保存的图片;
- templates是网页UI的代码;
- flask_yolo.py是启动该服务的入口;
- testYoloSer.py是测试该服务的接口代码;
- yolomain.py是YOLOv3的实现代码;
项目下载地址:
需要注意的是,由于上传大小的限制只能上传小于220M的文件,而该目录中yolov3_coco.weights就有230多M。如果想运行你需要下载该文件,当然你用积分下载该项目后不想自己找,也可以在下面评论留言,我可以发给你。
其中,主要文件的代码如下:
flask_yolo.py
# -*- coding: utf-8 -*-#
# Author: weiz
# Date: 2019/9/16 14:12
# Name: flask_yolo
# Description: 一个可以在浏览器使用YOLO目标检测的函数,和一个可以通过网络访问的YOLO目标检测的接口
# 直接运行就可以在浏览器上面输入带检测图片,并返回检测的结果;同时,可以接受通过局域网
# 访问该接口的服务。
from flask import Flask, render_template, request, jsonify, make_response
from werkzeug.utils import secure_filename
import os
import cv2
import time
import json
from PIL import Image
from io import BytesIO
import json
import numpy as np
from datetime import timedelta
import yolomain
# 设置允许的文件格式
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'JPG', 'PNG', 'bmp'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
app = Flask(__name__)
# 设置静态文件缓存过期时间
app.send_file_max_age_default = timedelta(seconds=1)
# @app.route('/upload', methods=['POST', 'GET'])
@app.route('/upload', methods=['POST', 'GET']) # 添加路由
def upload():
if request.method == 'POST':
f = request.files['file']
if not (f and allowed_file(f.filename)):
return jsonify({"error": 1001, "msg": "请检查上传的图片类型,仅限于png、PNG、jpg、JPG、bmp"})
user_input = request.form.get("name")
# 当前文件所在路径
basepath = os.path.dirname(__file__)
# 注意:没有的文件夹一定要先创建,不然会提示没有该路径
upload_path = os.path.join(basepath, 'static/images', secure_filename(f.filename))
f.save(upload_path)
lab, img, loc = yolomain.yolo_detect(pathIn=upload_path)
cv2.imwrite(os.path.join(basepath, 'static/images', 'test.jpg'), img)
return render_template('upload_ok.html', userinput=user_input, val1=time.time(), data_dict=lab)
return render_template('upload.html')
@app.route('/yolo_service',methods=['POST'])
def detection():
img = request.stream.read()
f = BytesIO(img)
image = Image.open(f)
image = np.array(image, dtype='float32')
boxes, lab = yolomain.yolo_ser(image)
rsp = make_response(json.dumps(lab))
rsp.mimetype = 'application/json'
rsp.headers['Connection'] = 'close'
return rsp
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
testYoloSer.py
# -*- coding: utf-8 -*-#
# Author: weiz
# Date: 2019/9/16 17:05
# Name: test_yolo
# Description: 测试YOLOweb服务是否可以使用的代码(请先运行flask_yolo.py)
import requests
import os
import json
if __name__ == '__main__':
# 注意填写自己电脑的IP地址
url = 'http://192.168.124.19:5000/yolo_service'
headers = {'Content-Type': 'image/jpeg'}
file = {'media': open('./static/images/test.jpg', 'rb')}
requests.post(url, files=file)
data = open('./static/images/test.jpg', 'rb').read()
r = requests.post(url, data=data, headers=headers, verify=False)
print("类别:置信度 (左上角X坐标,左上角Y坐标) (目标的宽, 目标的高)")
print(r.text)
yolomain.py
# -*- coding: utf-8 -*-
# Author: weiz
# Date: 2019/9/16 17:00
# Name: test_yolo
# Description: YOLOv3目标检测的代码,提供检测的接口;也可以单独运行。
# 载入所需库
import cv2
import numpy as np
import os
import time
def yolo_detect(im=None,
pathIn=None,
label_path='./cfg/coco.names',
config_path='./cfg/yolov3_coco.cfg',
weights_path='./cfg/yolov3_coco.weights',
confidence_thre=0.5,
nms_thre=0.3):
'''
im:原始图片
pathIn:原始图片的路径
label_path:类别标签文件的路径
config_path:模型配置文件的路径
weights_path:模型权重文件的路径
confidence_thre:0-1,置信度(概率/打分)阈值,即保留概率大于这个值的边界框,默认为0.5
nms_thre:非极大值抑制的阈值,默认为0.3
'''
# 加载类别标签文件
LABELS = open(label_path).read().strip().split("\n")
nclass = len(LABELS)
# 为每个类别的边界框随机匹配相应颜色
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(nclass, 3), dtype='uint8')
# 载入图片并获取其维度
#base_path = os.path.basename(pathIn)
if pathIn == None:
img = im
else:
img = cv2.imread(pathIn)
(H, W) = img.shape[:2]
net = cv2.dnn.readNetFromDarknet(config_path, weights_path)
# 获取YOLO输出层的名字
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 将图片构建成一个blob,设置图片尺寸,然后执行一次
# YOLO前馈网络计算,最终获取边界框和相应概率
blob = cv2.dnn.blobFromImage(img, 1 / 255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob)
start = time.time()
layerOutputs = net.forward(ln)
end = time.time()
# 初始化边界框,置信度(概率)以及类别
boxes = []
confidences = []
classIDs = []
# 迭代每个输出层,总共三个
for output in layerOutputs:
# 迭代每个检测
for detection in output:
# 提取类别ID和置信度
scores = detection[5:]
classID = np.argmax(scores)
confidence = scores[classID]
# 只保留置信度大于某值的边界框
if confidence > confidence_thre:
# 将边界框的坐标还原至与原图片相匹配,记住YOLO返回的是
# 边界框的中心坐标以及边界框的宽度和高度
box = detection[0:4] * np.array([W, H, W, H])
(centerX, centerY, width, height) = box.astype("int")
# 计算边界框的左上角位置
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
# 更新边界框,置信度(概率)以及类别
boxes.append([x, y, int(width), int(height)])
confidences.append(float(confidence))
classIDs.append(classID)
# 使用非极大值抑制方法抑制弱、重叠边界框
idxs = cv2.dnn.NMSBoxes(boxes, confidences, confidence_thre, nms_thre)
lab = []
loc = []
# 确保至少一个边界框
if len(idxs) > 0:
# 迭代每个边界框
for i in idxs.flatten():
# 提取边界框的坐标
(x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3])
# 绘制边界框以及在左上角添加类别标签和置信度
color = [int(c) for c in COLORS[classIDs[i]]]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
text = '{}: {:.3f}'.format(LABELS[classIDs[i]], confidences[i])
(text_w, text_h), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
cv2.rectangle(img, (x, y-text_h-baseline), (x + text_w, y), color, -1)
cv2.putText(img, text, (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
text_inf = text + ' ' + '(' + str(x) + ',' + str(y) + ')' + ' ' + '宽:' + str(w) + '高:' + str(h)
loc.append([x, y, w, h])
lab.append(text_inf)
return lab, img, loc
def yolo_ser(im,
label_path='./cfg/coco.names',
config_path='./cfg/yolov3_coco.cfg',
weights_path='./cfg/yolov3_coco.weights',
confidence_thre=0.5,
nms_thre=0.3):
'''
im:原始图片
label_path:类别标签文件的路径
config_path:模型配置文件的路径
weights_path:模型权重文件的路径
confidence_thre:0-1,置信度(概率/打分)阈值,即保留概率大于这个值的边界框,默认为0.5
nms_thre:非极大值抑制的阈值,默认为0.3
'''
LABELS = open(label_path).read().strip().split("\n")
nclass = len(LABELS)
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(nclass, 3), dtype='uint8')
img = im
(H, W) = img.shape[:2]
net = cv2.dnn.readNetFromDarknet(config_path, weights_path)
# 获取YOLO输出层的名字
ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
# 将图片构建成一个blob,设置图片尺寸,然后执行一次
# YOLO前馈网络计算,最终获取边界框和相应概率
blob = cv2.dnn.blobFromImage(img, 1 / 255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob)
start = time.time()
layerOutputs = net.forward(ln)
end = time.time()
# 初始化边界框,置信度(概率)以及类别
boxes = []
confidences = []
classIDs = []
# 迭代每个输出层,总共三个
for output in layerOutputs:
# 迭代每个检测
for detection in output:
# 提取类别ID和置信度
scores = detection[5:]
classID = np.argmax(scores)
confidence = scores[classID]
# 只保留置信度大于某值的边界框
if confidence > confidence_thre:
# 将边界框的坐标还原至与原图片相匹配,记住YOLO返回的是
# 边界框的中心坐标以及边界框的宽度和高度
box = detection[0:4] * np.array([W, H, W, H])
(centerX, centerY, width, height) = box.astype("int")
# 计算边界框的左上角位置
x = int(centerX - (width / 2))
y = int(centerY - (height / 2))
# 更新边界框,置信度(概率)以及类别
boxes.append([x, y, int(width), int(height)])
confidences.append(float(confidence))
classIDs.append(classID)
# 使用非极大值抑制方法抑制弱、重叠边界框
idxs = cv2.dnn.NMSBoxes(boxes, confidences, confidence_thre, nms_thre)
lab_cfd = []
lab = []
loc = []
# 确保至少一个边界框
if len(idxs) > 0:
# 迭代每个边界框
for i in idxs.flatten():
# 提取边界框的坐标
(x, y) = (boxes[i][0], boxes[i][1])
(w, h) = (boxes[i][2], boxes[i][3])
# 绘制边界框以及在左上角添加类别标签和置信度
color = [int(c) for c in COLORS[classIDs[i]]]
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
text = '{}: {:.3f}'.format(LABELS[classIDs[i]], confidences[i])
(text_w, text_h), baseline = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
cv2.rectangle(img, (x, y - text_h - baseline), (x + text_w, y), color, -1)
cv2.putText(img, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
text_inf = text + ' ' + '(' + str(x) + ',' + str(y) + ')' + ' ' + '(' + str(w) + ',' + str(h) + ')'
loc.append([x, y, w, h])
lab.append(text_inf)
return loc, lab
if __name__ == '__main__':
pathIn = './static/images/test1.jpg'
im = cv2.imread('./static/images/test2.jpg')
lab, img, loc = yolo_detect(pathIn=pathIn)
print(lab)
# cv2.imshow("ret", img)
# cv2.waitKey(0)
web端展示(注意地址的输入):
接口端效果展示:
上面的项目不能部署到Linux系统上。下面的代码是可以让上面的项目快速部署成功。
文件名是runServer.sh
#!/usr/bin/env bash
export LANG=zh_CN.UTF-8
PRG_KEY="FWebYolo"
RUN_PATH=$(cd `dirname $0`;pwd) # Get the absolute path of the work
cd $RUN_PATH
case "$1" in
start)
nohup python3 -u $RUN_PATH/flask_yolo1.py runserver > nohup.log 2>&1 &
echo "$PRG_KEY started, please check log."
;;
stop)
pid=$(pgrep -f $PRG_KEY)
echo "$pid"
kill -9 $pid
##killall python3
echo "$PRG_KEY stoped!"
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
开启服务的命令:
开启服务:sh runServer.sh start
关闭服务:sh runServer.sh stop
查看进程:ps ef|grep FWebYolo1