深度学习之RESTful API使用

作者
发布于 2023-02-28 / 226 阅读
0
0

深度学习之RESTful API使用

API(Application Programming Interface,应用程序接口)函数是一些预先定义的函数。其需求来源是团队协同开发一个产品/系统时,由于各个团队的进度可能不一致,为避免拖慢项目进度便逐渐转向“对接”方式来开发,架设有A和B两个团队共同开发一个项目,在开始后可能A团队效率很高,已经做完并等待B团队,那么总不能A团队一直闲着,也不太可能让A团队马上负责下一个案子,于是出现一种对接标准:只需A团队满足一开始谈定的通讯方式就OK。如果是web开发小伙伴一听就知道了,这不就是web开发的前后端分离么,确实如此,但是今天我们希望将API用在深度学习推论上。

 

API能用在深度学习的哪里

深度学习在目前还需要服务器运行来推论结果,我们称之为Inference PC,Inference PC的GPU配置和散热有一定需求,所以会被部署到无层机房中,而移动端我们称之为Edge PC(边缘设备),可以是手机,笔记本电脑等轻薄设备,而在Inference PC和Edge PC之间我们会使用网络来连接,当我们把Inference PC当作后端,Edge PC当作前端,那么用什么将其连接起来更方便?对!就是API,我们已经实现了用Restful API来连接两者,那我们来看看具体实现吧。

 

Restful API如何搭建(Flask-RESTful for python)

深度学习使用的是python,我们用python实现Restful API有三种主流方式,分别是Flask-RESTful,Django REST framework和Fast API,我们采用Flask-RESTful来进行演示。

安装Flask-RESTful只需要你安装了python,然后输入安装指令(这里-i表示使用了清华源来加速下载):

pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install flask-restful -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成之后会显示“Successfully installed”字样

值得注意的是,如果想要复现我这里的flask-restful API传输图片,那么还需要安装如下库:

pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple

pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成可以通过命令行命令(windows+R-CMD)输入“pip list”查看

 restfulAPI官网.png

图:RESTful API官网主页

Restful API如何测试

前面提到Restful API其实是一种预定义了函数的接口,现在我们通过web常用的POST方式来进行提交图片给服务器(web API主要有四种通讯,分别为POST + DELETE + PUT + GET,也就是代码圈常见的“增删改查”功能),目前的web开发主流是前后端分离的方式,所以我这里也将前后端进行了分离,使用Postman作为webAPI模拟传输调试工具(为了加快开发进度让团队不要处在等待过程中,前后端分离开发中大都具备这种中间件的调试工具,产品级别的开发可能会涉及更大的敏捷开发SCRUM)。

 

Restful API实现源代码(python)

我们本次的目的是使用Restful API将图片从前端传送到后端,传送成功后后端传送回来结果。

Edge PC前端代码(负责传送图片并接收结果)

import requests
import cv2 as cv
import json

img = cv.imread("1.jpg")
img_update=[]
for i in img:
    img_update.append(i.tolist())
print(type(img_update))
r=requests.post("http://127.0.0.1:5000/", data=json.dumps({"name": img_update}))
print(r.text)

Inference PC后端代码(负责解析图片并返回结果)

from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
import cv2 as cv
import numpy as np
app = Flask(__name__)
api = Api(app)

TODOS = {
    'todo1': {'task1': 'writeimg'},
    'todo2': {'task2': 'blueimg'},
    'todo3': {'task3': 'greenimg'}
}

def abort_if_todo_doesnt_exist(todo_id):
    if todo_id not in TODOS:
        abort(404, message="Todo {} doesn't exist".format(todo_id))

parser = reqparse.RequestParser()
parser.add_argument('task', type=str)

class Todo(Resource):
    def get(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        return TODOS[todo_id]

    def delete(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        del TODOS[todo_id]
        return '', 204

    def put(self, todo_id):
        args = parser.parse_args()
        task = {'task': args['task']}
        TODOS[todo_id] = task
        return task, 201

# TodoList
class TodoList(Resource):
    def get(self):
        return TODOS

    def post(self):
        args = parser.parse_args()
        todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
        todo_id = 'todo%i' % todo_id
        TODOS[todo_id] = {'task': args['task']}
        img=TODOS[todo_id]["task"]
        #彩色图片判定
        if "[[[" in img:
            img_out1,img_out2,item_num=[],[],0
            img=img.strip('[[[')
            img=img.strip(']]]')
            img=img.split(']], [[')
            for item_cut1 in img:
                img_out1.append(item_cut1.split('], ['))
            for item_cut1 in img_out1:
                img_out2.append([])
                for item_cut2_item in item_cut1:
                    img_out2[item_num].append(np.array((item_cut2_item.split(',')),dtype=np.uint8))
                item_num+=1
            img_out2 = np.ascontiguousarray(img_out2)
            cv.imwrite("reimgRGB.png",img_out2)
            return "RGBPASS", 201
        #黑白图片判定
        elif "[[" in  img:
            img_out=[]
            img=img.strip('[[')
            img=img.strip(']]')
            img=img.split('], [')
            for item_cut in img:
                img_out.append(np.array((item_cut.split(',')),dtype=np.uint8))
            img_out = np.ascontiguousarray(img_out)
            cv.imwrite("reimg.png",img_out)
            return "PASS", 201
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')

if __name__ == '__main__':
    app.run(debug=True)#默认地址为:http://127.0.0.1:5000/todos

 

Restful API调试过程

开发过程中我发现前端代码其实很好写,源代码简单清晰,反而是服务端图像解析部分异常难写,在实际使用中我发现图片传送过来有两种,彩色图片和黑白图片,于是我写了判定语句来兼容两种图片,解析图片我们看到其实图片是二维或者三维的list(数组),但是传送过程会将其转换成str(字符串),如何在服务端将其还原成图片格式就成了问题。在这里我试了强制转换和分开传送,效果均不理想,最后使用字串拆分str.split()函数进行拆分,然后使用append()函数将其添加到空list中形成数组来实现了图片还原,这个过程中因为OpenCV的格式要求问题,还是用了np.array()函数进行格式转换,最终成功将图片传送到服务端并调用函数使用图片。

这个期间我修改了前端代码,然后使用Postman模拟发送前端需要发送给服务端的数据,快速查看和调试问题点。

修改后的前端代码,取消了传送,存成了图像的CSV文件生成传送数据进行模拟:

import requests
import cv2 as cv
import json
img = cv.imread("1.jpg")
print(img)
img_update=[]
for i in img:
    img_update.append(i.tolist())
print(type(img_update))
#print(img_update)
open("1.csv",'a').write(str(img_update)+'\n')
"""黑白图像生成了如下数据:
[[113, 89, 112, 113, 118, 117, 117, 106, 102, 97, 104, 102, 107, 105, 102], [127, 126, 96, 120, 114, 117, 129, 120, 119, 126, 138, 132, 120, 127, 118], [133, 119, 148, 149, 160, 152, 129, 141, 131, 126, 141, 160, 161, 167, 160], [145, 87, 48, 126, 131, 187, 248, 255, 255, 245, 158, 90, 157, 78, 67], [142, 88, 99, 131, 218, 254, 250, 255, 253, 253, 255, 215, 67, 151, 49], [134, 101, 120, 174, 249, 250, 253, 253, 255, 252, 250, 249, 154, 115, 98], [141, 94, 86, 240, 241, 241, 223, 225, 203, 245, 225, 223, 233, 71, 131], [145, 90, 91, 234, 219, 226, 121, 109, 87, 135, 210, 200, 209, 99, 142], [139, 89, 92, 211, 183, 192, 172, 56, 99, 155, 208, 191, 201, 99, 139], [136, 90, 77, 180, 156, 149, 168, 139, 123, 144, 181, 181, 192, 90, 139], [141, 85, 82, 171, 162, 173, 202, 216, 206, 163, 143, 182, 180, 71, 116], [134, 81, 128, 107, 166, 159, 150, 138, 145, 168, 167, 170, 101, 35, 68], [139, 83, 83, 145, 117, 146, 135, 121, 144, 158, 168, 113, 24, 89, 135], [142, 88, 34, 104, 145, 95, 120, 149, 141, 111, 107, 38, 75, 127, 150], [138, 125, 53, 48, 76, 93, 71, 51, 52, 68, 85, 79, 127, 144, 140]]
"""
##r=requests.post("http://127.0.0.1:5000/", data=json.dumps({"name": img_update}))
##print(r.text)

复制生成的图像数据(黑白图片是一个二维数组),接下来是调试窗口的POST数据发送,绿色框是操作顺序,蓝色框是回传数据,表示已经传给后端并存图PASS了:

POST return.png

后记

前后端分离和敏捷开发确实是未来的趋势,因为它能缩短开发时间并立刻产生效益,而API确实是前后端连接的有效桥梁,RESTful API是其中很优秀的框架,希望我在未来的学习中不要落下脚步,毕竟再往前走一步,可能真的会存在《流浪地球2》里面的550W的即时系统生成覆写模式来进行调试了,总之,趁着有动力有兴趣,不忘初心,继续前进吧。


评论