这个小项目是基于flask微型目标检测服务。使用的是YOLOv3版本,使用的是在coco数据集上已经训练好的参数。整个目录结构如下(我运行的环境是window10,pycharm):

其中:

  1. cfg是YOLOv3的配置文件,包括权重,网络结构和coco数据集的标签;
  2. static是通过网络访问该服务时被保存的图片;
  3. templates是网页UI的代码;
  4. flask_yolo.py是启动该服务的入口;
  5. testYoloSer.py是测试该服务的接口代码;
  6. yolomain.py是YOLOv3的实现代码;

Ultralytics YOLO 部署到python项目_flask

项目下载地址:

需要注意的是,由于上传大小的限制只能上传小于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端展示(注意地址的输入):

Ultralytics YOLO 部署到python项目_ide_02

接口端效果展示:

Ultralytics YOLO 部署到python项目_ide_03

上面的项目不能部署到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