使用OpenCV的DNN模块调用yolo3默认是CPU版本的,而且也不是真的调用yolo3,只是需要一个权重weights文件,一个配置cfg文件和一个标签列表names文件。
【重要部分】
网上的关于这一块的代码都大同小异,但是关键的地方在于一次加载多次调用,简单的加入
net.setPreferableBackend(cv.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv.dnn.DNN_TARGET_CUDA)
这两句话是不管用的,实际运行速度和CPU的没区别。
先看一段代码:
# -*- coding: utf-8 -*
import numpy as np
import cv2 as cv
import os
import time
yolo_dir = '/home/ubuntu/model/yolo3' # YOLO文件路径
weightsPath = os.path.join(yolo_dir, 'yolov3-test.weights') # 权重文件
configPath = os.path.join(yolo_dir, 'yolov3-test.cfg') # 配置文件
labelsPath = os.path.join(yolo_dir, 'test.names') # label名称
imgPath = os.path.join(yolo_dir, 'test.jpg') # 测试图像
CONFIDENCE = 0.25 # 过滤弱检测的最小概率
THRESHOLD = 0.2 # 非最大值抑制阈值
#批量图片检测和保存位置
test_dir = '/home/ubuntu/model/yolo4-tiny/imgs'
save_dir = '/home/ubuntu/model/yolo4-tiny/imgsResultV3'
# 加载网络、配置权重
net = cv.dnn.readNetFromDarknet(configPath, weightsPath) # # 利用下载的文件
#批量加载图像
pics = os.listdir(test_dir)
start = time.time()
minTime = 10;
maxTime = 0;
for im in pics:
# 加载图片、转为blob格式、送入网络输入层
s = time.time()
imgPath = os.path.join(test_dir,im)
img = cv.imread(imgPath)
blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False) # # net需要的输入是blob格式的,用blobFromImage这个函数来转格式
net.setInput(blobImg) # # 调用setInput函数将图片送入输入层
# 获取网络输出层信息(所有输出层的名字),设定并前向传播
outInfo = net.getUnconnectedOutLayersNames() # # 前面的yolov3架构也讲了,yolo在每个scale都有输出,outInfo是每个scale的名字信息,供net.forward使用
layerOutputs = net.forward(outInfo) # 得到各个输出层的、各个检测框等信息,是二维结构。
# 拿到图片尺寸
(H, W) = img.shape[:2]
# 过滤layerOutputs
# layerOutputs的第1维的元素内容: [center_x, center_y, width, height, objectness, N-class score data]
# 过滤后的结果放入:
boxes = [] # 所有边界框(各层结果放一起)
confidences = [] # 所有置信度
classIDs = [] # 所有分类ID
# # 1)过滤掉置信度低的框框
for out in layerOutputs: # 各个输出层
for detection in out: # 各个框框
# 拿到置信度
scores = detection[5:] # 各个类别的置信度
classID = np.argmax(scores) # 最高置信度的id即为分类id
confidence = scores[classID] # 拿到置信度
# 根据置信度筛查
if confidence > CONFIDENCE:
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)
# # 2)应用非最大值抑制(non-maxima suppression,nms)进一步筛掉
idxs = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD) # boxes中,保留的box的索引index存入idxs
# 得到labels列表
with open(labelsPath, 'rt') as f:
labels = f.read().rstrip('\n').split('\n')
# 应用检测结果
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8") # 框框显示颜色,每一类有不同的颜色,每种颜色都是由RGB三个值组成的,所以size为(len(labels), 3)
if len(idxs) > 0:
for i in idxs.flatten(): # indxs是二维的,第0维是输出层,所以这里把它展平成1维
(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]]]
cv.rectangle(img, (x, y), (x+w, y+h), color, 2) # 线条粗细为2px
text = "{}: {:.4f}".format(labels[classIDs[i]], confidences[i])
cv.putText(img, text, (x, y-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) # cv.FONT_HERSHEY_SIMPLEX字体风格、0.5字体大小、粗细2px
print(imgPath+"检测耗时: {:.6f} seconds".format(time.time() - s)) # # 可以打印下信息
times = time.time() - s
if(minTime>times):
minTime = times
if(maxTime
maxTime = times
cv.imwrite(os.path.join(save_dir,im),img)
print("yolov3检测100张图总共耗时:%.6f秒" % (time.time() - start))
print("耗时最长为:%.6f秒" % (maxTime))
print("耗时最短为:%.6f秒" % (minTime))
这老哥有个很好的思路就是把net提到了最上边,众所周知flask接口调用一次就要重新读取一次网络结构,这样我的flask接口基本写成这样,一些设计业务逻辑的代码被我删掉了,为了看着清楚只保留最基本的代码:
# -*- coding: utf-8 -*-#
from flask import Flask, render_template, request, jsonify, make_response
from werkzeug.utils import secure_filename
import os
import cv2 as cv
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 global_var as glo
import yolo_origin
yolo_dir = '/home/ubuntu/YoloService/cfg'
weightsPath = os.path.join(yolo_dir, 'yolov3_333333.weights')
configPath = os.path.join(yolo_dir, 'yolov3_333333.cfg')
labelsPath = os.path.join(yolo_dir, 'voc_333333.names')
CONFIDENCE = 0.50
THRESHOLD = 0.45
net = cv.dnn.readNetFromDarknet(configPath, weightsPath)
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)
#URL地址
@app.route('/api/detection', methods=['POST', 'GET'])
def upload():
if request.method == 'POST':
f = request.files['file']
sss = request.form["identifier"]
glo.set_value("identifier",sss)
identifier = sss
if not (f and allowed_file(f.filename)):
return jsonify({"error": 1001, "msg": "File type exception !"})
t = f.filename
filename_ = t.split('.')[0]
user_input = request.form.get("name")
basepath = os.path.dirname(__file__)
upload_path = os.path.join(basepath, set_upload_path, secure_filename(f.filename))
f.save(upload_path)
s = time.time()
imgPath = upload_path
img = cv.imread(imgPath)
blobImg = cv.dnn.blobFromImage(img, 1.0/255.0, (416, 416), None, True, False)
net.setInput(blobImg)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
outInfo = net.getUnconnectedOutLayersNames()
layerOutputs = net.forward(outInfo)
(H, W) = img.shape[:2]
boxes = []
confidences = []
classIDs = []
for out in layerOutputs:
for detection in out:
scores = detection[5:]
classID = np.argmax(scores)
confidence = scores[classID]
if confidence > CONFIDENCE:
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 = cv.dnn.NMSBoxes(boxes, confidences, CONFIDENCE, THRESHOLD)
with open(labelsPath, 'rt') as f:
labels = f.read().rstrip('\n').split('\n')
need_NMS_list = []
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])
if(x<0):
x=0
if(y<0):
y=0
if(x+w>W):
w = W-x
if(y+h>H):
h = H-y
label = labels[classIDs[i]]
print(" [检测耗时]")
print(" "+str((time.time() - s))+" Seconds")
times = time.time() - s
np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(len(labels), 3), dtype="uint8")
lab = []
loc = []
data={}
info = []
flag = 1000
flag_str = ""
data["identifier"] = identifier
data['data']=info
res = jsonify(data)
cv2.imwrite(os.path.join(basepath, set_result_path, filename_+'_res.jpg'), img)
# 移除上传的图片
# os.remove(upload_path)
return res
return render_template('upload.html')
if __name__ == '__main__':
app.run(host='192.168.x.x', port=5000)
经过测试 CPU下 400ms/张 GPU下 30ms/张 速度提升10倍左右 虽然照官方测试速度提升22倍还差的很远,至少可以证明这条路行得通~
我是张益达 我们下篇博客见