目录

一、任务概述

二、官方示例部署

2.1 安装PaddleServing

2.2 导出静态图模型

2.3 转换为serving模型

2.4 启动服务

2.5 客户端请求

三、基于PipeLine的抠图功能部署

3.1 基于深度学习的抠图功能测试

3.1.1 算法库下载

3.1.2 抠图算法说明

3.1.3 抠图算法测试

3.2 基于PipeLine的Serving部署

3.2.1 转换为serving部署模型

3.2.2 设置config.yml部署配置文件

3.2.3 编写服务端脚本文件

3.2.4 客户端调用

四、小结


一、任务概述

本教程基于PaddleServing实现图像语义分割模型部署。首先我们会按照官方示例将部署流程跑一边,然后我们逐步调整代码和配置,基于更通用的PipeLine模式全流程实现抠图功能部署。

二、官方示例部署

2.1 安装PaddleServing

从官网下载最新稳定离线版whl文件进行安装,各组件安装命令如下:

  • 安装客户端:pip install paddle-serving-client
  • 安装服务器端(CPU或者GPU版二选一):
    • 安装CPU服务端:pip install paddle-serving-server
    • 安装GPU服务端:pip install paddle-serving-server-gpu
  • 安装工具组件:pip install paddle-serving-app

在安装时为了加速可以添加百度镜像源参数:

-i https://mirror.baidu.com/pypi/simple

2.2 导出静态图模型

一般来说我们使用PaddlePaddle动态图训练出来的模型如果直接部署,其推理效率是比较低的。为了能够实现高效、稳定部署,我们需要将训练好的模型转换为静态图模型。

导出示例请参考官网说明。

这里我们使用官网示例给出的转换好的模型进行操作。

下载静态图模型:

wget https://paddleseg.bj.bcebos.com/dygraph/demo/bisenet_demo_model.tar.gz
tar zxvf bisenet_demo_model.tar.gz

解压后看到模型文件夹中内容如下所示:

然后我们准备一张用于测试的街景图像:

到这里,需要的部署数据都准备好了。

2.3 转换为serving模型

为了能够使用Paddle Serving工具实现AI服务器云部署,我们需要前面准备好的静态图模型转换为Paddle Serving可以使用的部署模型。

我们将使用paddle_serving_client.convert工具进行转换,具体命令如下:

python -m paddle_serving_client.convert \--dirname ./bisenetv2_demo_model \--model_filename model.pdmodel \--params_filename model.pdiparams

执行完成后,当前目录下的serving_server文件夹保存服务端模型和配置,serving_client文件夹保存客户端模型和配置,如下图所示:

2.4 启动服务

按照官方示例,我们使用paddle_serving_server.serve的RPC服务模式,详细信息请参考文档。(需要注意的是,这种模式本质上是C/S架构,优势是响应快,缺点是在客户端需要安装相应的库并需要编写预处理代码)

我们在服务器端使用27008端口。

python3 -m paddle_serving_server.serve \--model serving_server \--thread 10 \--port 27008 \--ir_optim

启动后如果我们机器上没有安装对应版本的tensorrt,那么启动会出现如下错误:

error while loading shared libraries: libnvinfer.so.6: cannot open shared object file: No such file or directory

我们需要下载tensorrt库并将其添加到自己的环境变量去(注意tensortrt版本要与我们安装的paddle-serving-server-gpu版本一致)。相关解决方案请参考博客。安装完成后需要导入环境变量:

打开并编辑bashrc文件:

vim ~/.bashrc

在文件最后添加:

export LD_LIBRARY_PATH=/home/suser/copy/TensorRT-6.0.1.8/lib:$LD_LIBRARY_PATH

保存修改后把相关文件进行拷贝:

sudo cp TensorRT-6.0.1.8/targets/x86_64-linux-gnu/lib/libnvinfer.so.6  /usr/lib/

最后使用下面的命令使其生效:

source ~/.bashrc

重新启动服务端就可以正常跑起来了。

2.5 客户端请求

客户端采用python脚本进行访问请求。

完整请求代码如下:

import os
import numpy as np
import argparse
from PIL import Image as PILImagefrom paddle_serving_client import Client
from paddle_serving_app.reader import Sequential, File2Image, Resize, CenterCrop
from paddle_serving_app.reader import RGB2BGR, Transpose, Div, Normalizedef get_color_map_list(num_classes, custom_color=None):num_classes += 1color_map = num_classes * [0, 0, 0]for i in range(0, num_classes):j = 0lab = iwhile lab:color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j))color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j))color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j))j += 1lab >>= 3color_map = color_map[3:]if custom_color:color_map[:len(custom_color)] = custom_colorreturn color_mapdef get_pseudo_color_map(pred, color_map=None):pred_mask = PILImage.fromarray(pred.astype(np.uint8), mode='P')if color_map is None:color_map = get_color_map_list(256)pred_mask.putpalette(color_map)return pred_maskdef parse_args():parser = argparse.ArgumentParser(description='')parser.add_argument("--serving_client_path",help="The path of serving_client file.",type=str,required=True)parser.add_argument("--serving_ip_port",help="The serving ip.",type=str,default="127.0.0.1:9292",required=True)parser.add_argument("--image_path", help="The image path.", type=str, required=True)return parser.parse_args()def run(args):client = Client()client.load_client_config(os.path.join(args.serving_client_path, "serving_client_conf.prototxt"))client.connect([args.serving_ip_port])seq = Sequential([File2Image(), RGB2BGR(), Div(255),Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5], False), Transpose((2, 0, 1))])img = seq(args.image_path)fetch_map = client.predict(feed={"x": img}, fetch=["save_infer_model/scale_0.tmp_1"])result = fetch_map["save_infer_model/scale_0.tmp_1"]color_img = get_pseudo_color_map(result[0])color_img.save("./result.png")print("The segmentation image is saved in ./result.png")if __name__ == '__main__':args = parse_args()run(args)

然后使用下面的命令启动:

python test.py \--serving_client_path serving_client \--serving_ip_port 127.0.0.1:27008 \--image_path cityscapes_demo.png

运行后可能会出现下面的错误:

 libcrypto.so.10: cannot open shared object file: No such file or directory

解决方案如下:

wget https://paddle-serving.bj.bcebos.com/others/centos_ssl.tar
tar xf centos_ssl.tar
rm -rf centos_ssl.tar
sudo mv libcrypto.so.1.0.2k /usr/lib/libcrypto.so.1.0.2k
sudo mv libssl.so.1.0.2k /usr/lib/libssl.so.1.0.2k
sudo ln -sf /usr/lib/libcrypto.so.1.0.2k /usr/lib/libcrypto.so.10
sudo ln -sf /usr/lib/libssl.so.1.0.2k /usr/lib/libssl.so.10
sudo ln -sf /usr/lib/libcrypto.so.10 /usr/lib/libcrypto.so
sudo ln -sf /usr/lib/libssl.so.10 /usr/lib/libssl.so

修改后重新执行客户端请求代码,结果如下图所示:

I0423 10:45:54.155158 20937 naming_service_thread.cpp:202] brpc::policy::ListNamingService("127.0.0.1:27008"): added 1
I0423 10:46:01.689276 20937 general_model.cpp:490] [client]logid=0,client_cost=7292.98ms,server_cost=6954.06ms.
The segmentation image is saved in ./result.png

执行完成后,分割的图片保存在当前目录的"result.png"。

分割结果如下图所示:

想要彻底停止服务可以使用下面的命令:

ps -ef | grep serving | awk '{print $2}' | xargs kill -9
ps -ef | grep web_service | awk '{print $2}' | xargs kill -9

从整个执行上来分析,这种基于RPC的方式有个明显的缺点,就是需要客户端来实现所有的预处理和后处理操作。这对于跨语言的应用任务来说是比较麻烦的,例如我们如果采用java作为前台语言,那么就只能使用java来执行图像相关预处理和后处理。为了解决这个问题,paddle serving提供了java版的客户端,其本质是一个封装好的基于java的图像预处理工具。前端程序员还是需要手工编写客户端代码,协调合作时比较麻烦。

下面我们将使用另一种pipeline的方法,所有的预处理和后处理也一起交给服务端去做,这样就彻底跟前端功能剥离开来,前后端之间通过http接口进行通讯。这种方式相对于RPC模式来说速度会慢一些,但是很显然,其通用性更好。

下面我们就使用一个更加具体的抠图任务来实现整个的PipeLine部署。

三、基于PipeLine的抠图功能部署

3.1 基于深度学习的抠图功能测试

3.1.1 算法库下载

首先从github官网下载最新的paddleseg套件,也可以从我的gitee镜像上下载(速度会快一些):

git clone https://gitee.com/binghai228/PaddleSeg.git

然后进行安装:

cd PaddleSeg
pip install -r requirements.txt -i https://mirror.baidu.com/pypi/simple
python setup.py install

注意如果本地安装失败,也可以使用在线安装方式:

cd PaddleSeg
pip install -r requirements.txt -i https://mirror.baidu.com/pypi/simple
pip install paddleseg==2.5.0

cd到Matting文件夹中:

cd PaddleSeg/Matting

这个文件夹下面就是PaddleSeg官方在维护的抠图算法套件。

3.1.2 抠图算法说明

Matting(精细化分割/影像去背/抠图)是指借由计算前景的颜色和透明度,将前景从影像中撷取出来的技术,可用于替换背景、影像合成、视觉特效,在电影工业中被广泛地使用。影像中的每个像素会有代表其前景透明度的值,称作阿法值(Alpha),一张影像中所有阿法值的集合称作阿法遮罩(Alpha Matte),将影像被遮罩所涵盖的部分取出即可完成前景的分离。

相关功能实现效果如下:

PaddleSeg套件提供多种场景人像抠图模型, 可根据实际情况选择相应模型。这里我们选择PP-Matting-512模型进行部署应用。读者也可以参照官网教程自行训练模型,然后转为静态图模型使用。本教程更偏重算法部署,对于算法原理和训练本教程不再深入阐述,对深度学习抠图有兴趣的读者可以参考我的另一篇博客了解相关算法原理。

3.1.3 抠图算法测试

首先下载训练好的模型。如下图所示:

模型下载后解压放置在Matting/data文件夹下。

然后我们下载PPM-100数据集用于后续测试。下载下来后解压放置在Matting/data目录下。

最终我们可以使用下面的脚本命令进行测试:

python deploy/python/infer.py \--config data/pp-matting-hrnet_w18-human_512/deploy.yaml \--image_path data/PPM-100/val/fg/ \--save_dir output/results

推理完成后在output/results目录下保存了抠图后的测试图像结果。

部分效果如下:

 

从效果上分析,整体抠图性能还是比较好的。

当然,对于一些复杂的照片抠图效果还是有待再提高的,例如下面的示例:

 

3.2 基于PipeLine的Serving部署

3.2.1 转换为serving部署模型

使用paddle_serving_client.convert工具进行转换,具体命令如下:

python -m paddle_serving_client.convert \--dirname ./data/pp-matting-hrnet_w18-human_512 \--model_filename model.pdmodel \--params_filename model.pdiparams

执行完成后,当前目录下的serving_server文件夹保存服务端模型和配置,serving_client文件夹保存客户端模型和配置,如下图所示:

我们打开serving_server_conf.prototxt文件,其内容如下所示:

feed_var {name: "img"alias_name: "img"is_lod_tensor: falsefeed_type: 1shape: 3
}
fetch_var {name: "tmp_75"alias_name: "tmp_75"is_lod_tensor: falsefetch_type: 1shape: 1
}

根据这个文件,我们在写部署代码的时候需要注意对应的输入、输出变量名称,这里输入变量名为img,输出变量名为tmp_75。

3.2.2 设置config.yml部署配置文件

在当前目录下新建config.yml文件,内容如下:

dag:#op资源类型, True, 为线程模型;False,为进程模型is_thread_op: false#使用性能分析, True,生成Timeline性能数据,对性能有一定影响;False为不使用use_profile: false# tracer:#   interval_s: 30
#http端口, rpc_port和http_port不允许同时为空。当rpc_port可用且http_port为空时,不自动生成http_port
http_port: 27008#worker_num: 2      #最大并发数。当build_dag_each_worker=True时, 框架会创建worker_num个进程,每个进程内构建grpcSever和DAG#build_dag_each_worker, False,框架在进程内创建一条DAG;True,框架会每个进程内创建多个独立的DAG
build_dag_each_worker: false#rpc端口, rpc_port和http_port不允许同时为空。当rpc_port为空且http_port不为空时,会自动将rpc_port设置为http_port+1
#rpc_port: 27009op:matting:#并发数,is_thread_op=True时,为线程并发;否则为进程并发concurrency: 4local_service_conf:#client类型,包括brpc, grpc和local_predictor.local_predictor不启动Serving服务,进程内预测client_type: local_predictor# device_type, 0=cpu, 1=gpu, 2=tensorRT, 3=arm cpu, 4=kunlun xpudevice_type: 1#计算硬件ID,当devices为""或不写时为CPU预测;当devices为"0", "0,1,2"时为GPU预测,表示使用的GPU卡devices: '0,1,2,3'#Fetch结果列表,model中fetch_var的alias_name为准, 如果没有设置则全部返回fetch_list:- tmp_75#模型路径model_config: serving_server/

如果要部署自己的模型请根据注释结合自己需要部署的模型参数对照着进行修改。

3.2.3 编写服务端脚本文件

新建web_service.py文件,内容如下:

# 导入依赖库
import numpy as np
import cv2
from paddle_serving_app.reader import *
import base64
from paddle_serving_server.web_service import WebService, Opclass MattingOp(Op):'''定义抠图算子'''def init_op(self):'''初始化'''self.img_preprocess = Sequential([#BGR2RGB(), Div(255.0),#Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], False),Resize(512), Transpose((2, 0, 1))])self.ref_size = 512self.img_width = self.ref_sizeself.img_height = self.ref_size   def preprocess(self, input_dicts, data_id, log_id):'''预处理'''(_, input_dict), = input_dicts.items()imgs = []for key in input_dict.keys():# 解码图像data = base64.b64decode(input_dict[key].encode('utf8'))data = np.fromstring(data, np.uint8)im = cv2.imdecode(data, cv2.IMREAD_COLOR)self.im = imself.img_height,self.img_width,_ = im.shape# 短边对齐512,长边设置为32整数倍(根据算法模型要求)im_h, im_w, _ = im.shape  if im_w >= im_h:im_rh = self.ref_sizeim_rw = int(im_w *1.0 / im_h * self.ref_size)elif im_w < im_h:im_rw = self.ref_sizeim_rh = int(im_h *1.0 / im_w * self.ref_size)      im_rw = im_rw - im_rw % 32im_rh = im_rh - im_rh % 32im = cv2.resize(im,(im_rw,im_rh))# cv2转tensorim = self.img_preprocess(im)imgs.append({"img": im[np.newaxis, :],# "im_shape":np.array(list(im.shape[1:])).reshape(-1)[np.newaxis, :],# "scale_factor": np.array([1.0, 1.0]).reshape(-1)[np.newaxis, :],})# 准备输入数据feed_dict = {"img": np.concatenate([x["img"] for x in imgs], axis=0),# "im_shape": np.concatenate(#     [x["im_shape"] for x in imgs], axis=0),# "scale_factor": np.concatenate(#     [x["scale_factor"] for x in imgs], axis=0)}#for key in feed_dict.keys():#    print(key, feed_dict[key].shape)return feed_dict, False, None, ""def postprocess(self, input_dicts, fetch_dict, data_id, log_id):'''后处理'''# 取出掩码图alpha = fetch_dict["tmp_75"]alpha = alpha.squeeze(0).squeeze(0)alpha = (alpha * 255).astype('uint8')  alpha = cv2.resize(alpha, (self.img_width, self.img_height), interpolation=cv2.INTER_NEAREST)alpha = alpha[:, :, np.newaxis]clip = np.concatenate([self.im, alpha], axis=-1)  print(clip.shape)_, buffer_img = cv2.imencode('.png', clip)  # 在内存中编码为png格式img64 = base64.b64encode(buffer_img)  img64 = str(img64, encoding='utf-8')  # bytes转换为str类型 #封装成字典返回res_dict = {"alpha":img64}return res_dict, None, ""class MattingService(WebService):'''定义服务'''def get_pipeline_response(self, read_op):matting_op = MattingOp(name="matting", input_ops=[read_op])return matting_op# 创建服务
matting_service = MattingService(name="matting")
# 加载配置文件
matting_service.prepare_pipeline_config("config.yml")
# 启动服务
matting_service.run_service()

上述代码做了注释,读者可以自行阅读分析。

最后使用下面的命令启动服务:

python web_service.py

3.2.4 客户端调用

这里需要注意,由于我们采用了Pipeline模式,所有的图像预处理和后处理操作都放在了服务端,因此,客户端不需要加载额外的库,也不需要进行相关图像预处理代码编写。因此,我们可以采用任何客户端方式(浏览器、脚本、移动端等),只需要按照http restful协议传送相关json数据即可。

本文为了简单,采用python脚本来作为客户端(也可以仿照这个脚本使用postman进行测试)。新建脚本文件pipeline_http_client.py,具体代码如下:

# 导入依赖库
import numpy as np
import requests
import json
import cv2
import base64def cv2_to_base64(image):return base64.b64encode(image).decode('utf8')# 定义http接口
url = "http://127.0.0.1:27008/matting/prediction"# 打开待预测的图像文件
with open('./data/PPM-100/val/fg/1.jpg', 'rb') as file:image_data1 = file.read()# 采用base64编码图像文件
image = cv2_to_base64(image_data1)# 按照特定格式封装成字典
data = {"key": ["image"], "value": [image]}# 发送请求
r = requests.post(url=url, data=json.dumps(data))# 解析返回值
r = r.json()# 解码返回的图像数据
img = r["value"][0]
img = bytes(img, encoding='utf-8')  # str转bytes
img = base64.b64decode(img)  # base64解码
img = np.asarray(bytearray(img), dtype="uint8")
img = cv2.imdecode(img, cv2.IMREAD_UNCHANGED)# 保存图像到本地
if img is None:print('call error')
else:cv2.imwrite('result.png',img)print('完成')

执行预测:

python3 pipeline_http_client.py

四、小结

本教程以PaddleServing部署为目标,以语义分割(抠图)案例贯穿整个部署环节,最终成功实现服务器线上部署和调用。通过本教程的学习,可以快速将训练好的深度学习模型进行上线,同时具备良好的稳定性。

PaddleServing图像语义分割部署实践相关推荐

  1. 图像语义分割python_图像语义分割ICNET_飞桨-源于产业实践的开源深度学习平台...

    图像语义分割-ICNET 类别 智能视觉(PaddleCV) 应用 自动驾驶 室内导航 医学图像诊断 穿戴设备 虚拟现实与增强现实 无人机 模型概述 ICNet 主要用于图像实时语义分割,能够兼顾速度 ...

  2. AI 端侧落地+图像语义分割,百度 AI 快车道揭秘工业质检不再靠“人眼”的秘诀...

    AI时代下的人才缺口,已经让各企业感受强烈.人工智能的行业落地,需要多层次的人才结构.来自顶尖企业中的芯片.算法人才:拥揽世界大赛.论文的顶尖实验室是一种:更多的AI解决方案研发,工业级的落地应用等人 ...

  3. 图像语义分割入门:FCN/U-Net网络解析

    向AI转型的程序员都关注了这个号???????????? 机器学习AI算法工程   公众号:datayx 图像语义分割(Semantic Segmentation)是图像处理和是机器视觉技术中关于图像 ...

  4. 深度卷积网络CNN与图像语义分割

    转载请注明出处:  http://xiahouzuoxin.github.io/notes/html/深度卷积网络CNN与图像语义分割.html 级别1:DL快速上手 级别2:从Caffe着手实践 级 ...

  5. 笔记:基于DCNN的图像语义分割综述

    写在前面:一篇魏云超博士的综述论文,完整题目为<基于DCNN的图像语义分割综述>,在这里选择性摘抄和理解,以加深自己印象,同时达到对近年来图像语义分割历史学习和了解的目的,博古才能通今!感 ...

  6. 用CNN实现全景图像语义分割!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:张强,Datawhale成员 相信许多读者体验过b站上的全景视频, ...

  7. 【深度学习】用CNN实现全景图像语义分割!

    作者:张强,Datawhale成员 相信许多读者体验过b站上的全景视频,如果还没有,快来体验一下吧[1]!只需鼠标点击并移动,便可360度无死角的浏览全景视频,让人如同身临其境.全景图像,又称360° ...

  8. Python代码实现图像语义分割

    Python代码实现图像语义分割的步骤详解 原文链接:https://www.jb51.net/article/187249.htm 在网上看到了这篇,代码简洁,身为还没完全入门的小白,每跑通一个程序 ...

  9. 学习笔记-基于全局和局部对比自监督学习的高分辨率遥感图像语义分割-day2

    高分辨率遥感图像语义分割-day2 摘要 一.引言 二.方法 2.1 对比学习 2.2 全局风格与局部匹配对比学习网络(GLCNet) 2.2.1 全局风格对比学习模块: 2.2.2局部匹配对比学习: ...

最新文章

  1. LeetCode weekly contest 190 周赛
  2. 计算机主板在哪里找,台式电脑主板型号在哪里看
  3. OSGi:进入微服务架构的门户
  4. 在cmd命令行下编译运行C/C++源文件
  5. 安装 Tensorflow
  6. Javascript学习总结 - JS基础系列三
  7. 基于linux在线预览
  8. 至强3系列服务器cpu吗,做3D MAX是要求CPU好一点 还是显卡好一点? CPU的话是界面CPU(i 系列)好还是服务器CPU(至强系列)好?...
  9. Zabbix3.4监控Linux主机CPU温度
  10. c语言中结构体定义中的“冒号”
  11. 哪些股票自动交易接口好用呢?
  12. 倍福PLC——ADS上位机通讯
  13. Matlab笔记 第二章 基本操作与矩阵输入
  14. 百度2015校园招聘笔试题
  15. krpano 陀螺仪 相关问题
  16. 淘宝开放平台订单接口
  17. 机器学习模型管理平台_如何管理机器学习模型
  18. 利用计算机实现对个别学习者,利用计算机实现对学习者进行个别指导的教学模式是基于( )理论。...
  19. H5、CSS3照片墙的实现
  20. 实现豆瓣电影小程序(任性coding)

热门文章

  1. 用1元,2元,5元,10元,20元,50元和10元的纸币组成200元,共有多少种情况
  2. 女生到底选择前端还是测试?
  3. MySQL--》MySQL数据库以及可视化工具的安装与使用—保姆级教程
  4. 删除鲁大师节能省电方案
  5. 爱贝参加第十二届中国产学研合作创新大会
  6. Java中级面试题合集
  7. ffmpeg rtmp 花屏_FFmpeg代码架构
  8. python 中arange函数_Python numpy.arange函数方法的使用
  9. Windows Media Player不能播放rmvb视频解决办法
  10. php面试上机测试题_PHP面试题及答案