Yolov5部署成为Python接口 当然是用flask实现啦~ yolo5写成接口

    技术2023-06-28  69

    一、在此之前你是不是要先把yolov5跑通?

    yolov5的环境特别简单,建议在Ubuntu18.04下面部署,一次成型,省去很多麻烦。

    Ubuntu18.04RTX2080CUDA10.0CUDNN7.4.1Torch1.3.1GPUtorchvision0.4.2Python 3.6.5

    项目地址:https://gitee.com/situyufeng/yolov5.git

    github太慢了 我把他迁到了gitee上面

    装好Torch以后呢,记得修改pip为清华源,然后修改requirements.txt里面关于cocoapi的部分。

    第13行:

    git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI

    换成:

    git+https://gitee.com/situyufeng/cocoapi.git#subdirectory=PythonAPI

    然后

    pip install -r requirements.txt

    至此所有跑通yolov5的准备工作已经结束了

    权重的话不要担心,文末我上传了整个项目,希望可以换点积分。

    对了,运行下面这个命令的话是可以跑通的~

    python detect.py --source ./inference/images/ --weights yolov5s.pt --conf 0.4

    yolov5最方便的地方就是用Python写的,不需要编译,不需要编译,不需要编译!

    二、开始搭建flask接口!

    第一件事就是安装flask

    pip install flask

    接下来接口的主要函数文件都是基于detect.py,本来想做成yolov3那种项目和配置,奈何能力有限,读取权重的时候无法生成model。说到这里,yolov4我也尝试过做成flask接口,这里OpenCV拖了后腿,最新OpenCV不支持yolo4里面的mish函数,所以没有办法读取整个模型,可能是因为太新了不兼容。

    打开detect.py复制一份,改成yolo5.py

    因为我改动的部分实在是太多了,这里只能简要说一下了,具体的修改见我上传的代码。

    part1 detect.py就是核心推断函数,只不过传参使用命令行的方式传进去的, 但是所有的参数并不是每一个都需要修改,常用的也就是图片路径 (这里需要夸赞U神单张批量和视频都写到一个函数里面了,当初改yolov3的时候简直麻烦到天上去了), 剩下的参数写成默认参数就可以了: def detect( save_img=False, o_weights = "weights/yolov5s.pt", o_source = "inference/images", o_output = "inference/output", o_img_size = 640, o_conf_thres = 0.4, o_iou_thres = 0.5, o_fourcc = "mp4v", o_device = '', o_view_img = False, o_save_txt = False, o_classes = None, o_agnostic_nms = False, o_augment = False): p = '' c1 = (0,0) c2 = (0,0) label_no_value = '' detection_result_list = [] out, source, weights, view_img, save_txt, imgsz = \ o_output, o_source, o_weights, o_view_img, o_save_txt, o_img_size webcam = source == '0' or source.startswith('rtsp') or source.startswith('http') or source.endswith('.txt')

    第二个部分:

    part2 原始代码在获得推断结果以后会进行调用绘图函数画框,保存文本结果 ,保存图像结果等一系列操作,那我们的思路就是在他调用绘图函数之前截获他的目标检测坐标和标签信息: if det is not None and len(det): # 重新缩放框从img_size到im0的尺寸 det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round() # 打印结果 # for c in det[:, -1].unique(): # n = (det[:, -1] == c).sum() # 推断每一类 # s += '%g %ss, ' % (n, names[int(c)]) # 加到输出串里面个数 标签 # s = 'sssssaaaaassssaaaa %s' % ("asdffdsfsdfsfsf") # 写出结果 for *xyxy, conf, cls in det: label = '%s: %.2f' % (names[int(cls)], conf) label_no_value = '%s' % (names[int(cls)]) confidences_value = '%.2f' % (conf) c1,c2=plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) print(c1,c2,label_no_value)

    忽略我乱七八糟的调试代码~

    第三个部分:

    part3 我们只是为了拿他的坐标、标签和置信度,并不需要(至少暂时不需要)保存结果,真的完成绘图步骤,所以找到这些地方然后注释掉以提升速度: # 打印时间 (推断时间 + NMS) # print('%sDone. (%.3fs)' % (s, t2 - t1)) # # 视频流的结果 # if view_img: # #返回实时检测结果 # cv2.imshow(p, im0) # if cv2.waitKey(1) == ord('q'): # q to quit # raise StopIteration # # 保存结果 (推断后的图像) # if save_img: # if dataset.mode == 'images': # cv2.imwrite(save_path, im0) # else: # if vid_path != save_path: # 新的视频 # vid_path = save_path # if isinstance(vid_writer, cv2.VideoWriter): # vid_writer.release() # 释放上一个视频编写器句柄 # 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(*opt.fourcc), fps, (w, h)) # vid_writer.write(im0) # print(detection_result_list) 其中绘图函数写在了utils.py里面: def plot_one_box(x, img, color=None, label=None, line_thickness=None): # Plots one bounding box on image img # tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness # color = color or [random.randint(0, 255) for _ in range(3)] c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3])) # cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA) # print(c1,c2) # if label: # tf = max(tl - 1, 1) # font thickness # t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0] # c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3 # cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled # cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA) return c1,c2

    第四个部分就是把这些我们需要的信息封装成为json文件,通过flask来传送他们:

    part4 这里你可以改为你需要的信息: text = label text_inf = text + ' ' + '(' + str(c1[0]) + ',' + str(c1[1]) + ')' + ' ' + '宽:' + str(c2[0]-c1[0]) + '高:' + str(c2[1]-c1[1]) info.append({"label":names[int(cls)],"confidences":confidences_value}) loc.append([c1[0], c1[1], c2[0]-c1[0], c2[1]-c1[1]]) lab.append(text_inf) # for *xyxy, conf, cls in det: # if save_txt: # 写到文件 # xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # 归一化 xywh # with open(txt_path + '.txt', 'a') as f: # f.write(('%g ' * 5 + '\n') % (cls, *xywh)) # 格式化标签 # if save_img or view_img: # 把框画到图像上 # label = '%s %.2f' % (names[int(cls)], conf) # plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) data['data']=info res = jsonify(data) print(res)

    第五部分:编写flask的代码部分:

    part5 把flask的代码写好,调用yolo5的检测函数 set_upload_path = 'static/images' set_result_path = 'static/images' ALLOWED_EXTENSIONS = set(['png', 'jpg', 'JPG', 'PNG', 'bmp']) # glo._init() 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/face_detection5', methods=['POST', 'GET']) def upload(): 其中调用的yolo5的检测函数被我改成了这样: with torch.no_grad(): lab, loc, res = yolo5.detect(o_source = upload_path)

    第六部分:做成linux服务d

    part6 保存个日志文件,方便调试~ PRG_KEY="yolo5" 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_yolo5.py runserver > nohup.log 2>&1 & echo "$PRG_KEY started, please check log." ;; stop)

    第七部分:写个网页,用来上传图片:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传图片</title> </head> <body> <h1>上传</h1> <h2> yolo v5 </h2> <form action="" enctype='multipart/form-data' method='POST'> Identifier: <br> <input type="text" name="identifier" /> <br> <input type="file" name="file" style="margin-top:20px;"/> <br> <input type="submit" value="上传并识别" class="button-new" style="margin-top:15px;"/> </form> </body> </html>

    三、启动啊!等什么呢?

    sh runServer.sh start

    基本上yolo5写成api接口就是这些步骤了,https://download.csdn.net/download/Andrwin/12573384

    文件已经上传了

    Processed: 0.014, SQL: 9