用flask搭建tensorflow2的服务
# -*- coding: utf-8 -*-
# @File : www.py
# @Project : www
# @desc : 启动flask
import tensorflow as tf
import io
from PIL import Image
from flask import Flask, request
import cv2
import numpy as np
app = Flask(__name__)
TF2_DETECTION_URL = "/kctf2"
def load_test_img(path):
img_raw = tf.io.read_file(path) # 读取图片为二进制
img_tensor = tf.image.decode_jpeg(img_raw, channels=3) # 解码为张量
img_tensor = tf.image.resize(img_tensor, [299, 299]) # 图片大小缩放
img_tensor = tf.cast(img_tensor, tf.float32) # 转换图片数字类型从uint8转为float32便于计算
img_tensor = img_tensor / 255 # 图片数据归一化
return img_tensor
@app.route(TF2_DETECTION_URL, methods=["POST"])
def tfpredict():
if not request.method == "POST":
return
if request.files.get("image"):
image_file = request.files["image"]
image_bytes = image_file.read()
img_tensor = cv2.imdecode(np.array(bytearray(image_bytes), dtype='uint8'), cv2.IMREAD_UNCHANGED)
# img = Image.open(io.BytesIO(image_bytes))
# img.save("./static/img/tmp.png")
# testimg = load_test_img("./static/img/tmp.png")
# testimg = tf.expand_dims(testimg, 0)
# img_tensor = tf.image.encode_jpg(img_tensor, channels=3)
# model = tf.keras.models.load_model('kcEnterGame.h5')
# img_raw = tf.io.read_file('1.jpg')
# img_tensor = tf.image.decode_jpeg(img_raw, channels=3) # 解码为张量
img_tensor = tf.image.resize(img_tensor, [299, 299]) # 图片大小缩放
img_tensor = tf.cast(img_tensor, tf.float32) # 转换图片数字类型从uint8转为float32便于计算
img_tensor = img_tensor / 255 # 图片数据归一化
testimg = tf.expand_dims(img_tensor, 0)
pred = model.predict(testimg)
if np.max(pred) > 0.6:
if np.argmax(pred) == 0:
return 'fail'
else:
return 'pass'
else:
print('unknow')
if __name__ == '__main__':
gpus = tf.config.list_physical_devices("GPU")
print(gpus)
if gpus:
gpu0 = gpus[0] # 如果有多个GPU,仅使用第0个GPU
tf.config.experimental.set_memory_growth(gpu0, True) # 设置GPU显存用量按需使用
tf.config.set_visible_devices([gpu0], "GPU")
model = tf.keras.models.load_model('kcEnterGame.h5')
app.run(host="0.0.0.0", port=8987, debug=True) # debug=True causes Restarting with stat
用flask搭建YOLOV5的服务-客户端
# -*- coding: utf-8 -*-
# @Time : 2021/12/10 18:25
# @Author : 李思
# @99U : 234695
# @File : test.py
# @Project : www
# @desc : 发送请求
import pprint
import requests
import json
DETECTION_URL = "http://localhost:5000/kc"
TEST_IMAGE = "F:/kuaiceimage/images/Screenshot_2021-11-05-16-40-22-300_com.google.android.packageinstaller.png"
image_data = open(TEST_IMAGE, "rb").read()
# response = requests.post(DETECTION_URL, files={"image": image_data}).json()
response = requests.post(DETECTION_URL, files={"image": image_data})
pprint.pprint(response.text)
# {"x": 159.5, "y": 549.0}'
用flask搭建YOLOV5的服务-服务端
# -*- coding: utf-8 -*-
# @File : www.py
# @Project : www
# @desc : 启动flask
import argparse
import json
from pathlib import Path
from flask import Flask, request
import cv2
import torch
import numpy as np
from models.experimental import attempt_load
from utils.datasets import letterbox
from utils.general import check_img_size, non_max_suppression
from utils.torch_utils import select_device
app = Flask(__name__)
DETECTION_URL = "/kc"
def loadmodel():
save_dir, source, weights, view_img, save_txt, imgsz = \
Path(opt.save_dir), opt.source, opt.weights, opt.view_img, opt.save_txt, opt.img_size
# 获取设备
device = select_device(opt.device)
# 如果设备为gpu,使用Float16
half = device.type != 'cpu' # half precision only supported on CUDA
# Load model
# 加载Float32模型,确保用户设定的输入图片分辨率能整除32(如不能则调整为能整除并返回)
model = attempt_load(weights, map_location=device) # load FP32 model
imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size
# 设置Float16
if half:
model.half() # to FP16
return model
@app.route(DETECTION_URL, methods=["POST"])
def predict():
if not request.method == "POST":
return
if request.files.get("image"):
# 获取设备
device = select_device(opt.device)
# 如果设备为gpu,使用Float16
half = device.type != 'cpu' # half precision only supported on CUDA
# 进行一次前向推理,测试程序是否正常
img = torch.zeros((1, 3, opt.img_size, opt.img_size), device=device) # init img
_ = model(img.half() if half else img) if device.type != 'cpu' else None # run once
image_file = request.files["image"]
image_bytes = image_file.read()
img = cv2.imdecode(np.array(bytearray(image_bytes), dtype='uint8'), cv2.IMREAD_UNCHANGED)
# img = Image.open(io.BytesIO(image_bytes))
# img.save("./static/img/tmp.png")
# img = cv2.imread("./static/img/tmp.png")
# Padded resize
img = letterbox(img, new_shape=opt.img_size)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(device)
# 图片也设置为Float16
img = img.half() if half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
# 没有batch_size的话则在最前面添加一个轴
if img.ndimension() == 3:
img = img.unsqueeze(0)
"""
前向传播 返回pred的shape是(1, num_boxes, 5+num_class)
h,w为传入网络图片的长和宽,注意dataset在检测时使用了矩形推理,所以这里h不一定等于w
num_boxes = h/32 * w/32 + h/16 * w/16 + h/8 * w/8
pred[..., 0:4]为预测框坐标
预测框坐标为xywh(中心点+宽长)格式
pred[..., 4]为objectness置信度
pred[..., 5:-1]为分类结果
"""
pred = model(img, augment=opt.augment)[0]
# Apply NMS
# 进行NMS
"""
pred:前向传播的输出
conf_thres:置信度阈值
iou_thres:iou阈值
classes:是否只保留特定的类别
agnostic:进行nms是否也去除不同类别之间的框
经过nms之后,预测框格式:xywh-->xyxy(左上角右下角)
pred是一个列表list[torch.tensor],长度为batch_size
每一个torch.tensor的shape为(num_boxes, 6),内容为box+conf+cls
"""
pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres, classes=opt.classes, agnostic=opt.agnostic_nms)
# Process detections
# 对每一张图片作处理
for i, det in enumerate(pred): # detections per image
# 保存预测结果
for *xyxy, conf, cls in reversed(det):
mcx = (int(xyxy[2]) + int(xyxy[0])) / 2
mcy = (int(xyxy[1]) + int(xyxy[3])) / 2
print(mcx, mcy)
return json.dumps({'x': mcx, 'y': mcy})
return 'dddd'
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='I:/kuaicemodel/weights/best.pt', help='model.pt path(s)')
parser.add_argument('--source', type=str, default='F:/kuaiceimage', help='source') # file/folder, 0 for webcam
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--view-img', action='store_true', help='display results')
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
parser.add_argument('--save-dir', type=str, default='runs/detect', help='directory to save results')
parser.add_argument('--name', default='', help='name to append to --save-dir: i.e. runs/{N} -> runs/{N}_{name}')
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
parser.add_argument('--augment', action='store_true', help='augmented inference')
parser.add_argument('--update', action='store_true', help='update all models')
opt = parser.parse_args()
model = loadmodel()
app.run(host="0.0.0.0", port=5000, debug=True) # debug=True causes Restarting with stat