From 23a4a62daeba0c7ac1568114097f9a31ee13ac44 Mon Sep 17 00:00:00 2001 From: zhoushunjie Date: Tue, 27 Aug 2019 14:57:28 +0800 Subject: [PATCH] first commit to github project --- serving/COMPILE_GUIDE.md | 94 +------- serving/README.md | 179 +++++++++++++-- serving/UBUNTU.md | 76 +++++++ serving/seg-serving/conf/gflags.conf | 3 + .../seg-serving/conf/model_toolkit.prototxt | 2 +- serving/seg-serving/op/image_seg_op.cpp | 22 +- serving/seg-serving/op/reader_op.cpp | 3 +- serving/seg-serving/op/write_json_op.cpp | 3 +- serving/tools/image_seg_client.py | 210 +++++++++--------- 9 files changed, 374 insertions(+), 218 deletions(-) create mode 100644 serving/UBUNTU.md diff --git a/serving/COMPILE_GUIDE.md b/serving/COMPILE_GUIDE.md index bc415682..e6c9cda2 100644 --- a/serving/COMPILE_GUIDE.md +++ b/serving/COMPILE_GUIDE.md @@ -1,95 +1,7 @@ # 源码编译安装及搭建服务流程 -本文将介绍源码编译安装以及在服务搭建流程。 +本文将介绍源码编译安装以及在服务搭建流程。编译前确保PaddleServing的依赖项安装完毕。依赖安装教程请前往[PaddleSegServing 依赖安装](./README.md). -## 1. 系统依赖项 - -依赖项 | 验证过的版本 - -- | -- -Linux | Centos 6.10 / 7 -CMake | 3.0+ -GCC | 4.8.2/5.4.0 -Python| 2.7 -GO编译器| 1.9.2 -openssl| 1.0.1+ -bzip2 | 1.0.6+ - -如果需要使用GPU预测,还需安装以下几个依赖库 - - GPU库 | 验证过的版本 - -- | -- -CUDA | 9.2 -cuDNN | 7.1.4 -nccl | 2.4.7 - - -## 2. 安装依赖项 - -以下流程在百度云CentOS7.5+CUDA9.2环境下进行。 -### 2.1. 安装openssl、Go编译器以及bzip2 - -```bash -yum -y install openssl openssl-devel golang bzip2-libs bzip2-devel -``` - -### 2.2. 安装GPU预测的依赖项(如果需要使用GPU预测,必须执行此步骤) -#### 2.2.1. 安装配置CUDA9.2以及cuDNN 7.1.4 -该百度云机器已经安装CUDA以及cuDNN,仅需复制相关头文件与链接库 - -```bash -# 看情况确定是否需要安装 cudnn -# 进入 cudnn 根目录 -cd /home/work/cudnn/cudnn7.1.4 -# 拷贝头文件 -cp include/cudnn.h /usr/local/cuda/include/ -# 拷贝链接库 -cp lib64/libcudnn* /usr/local/cuda/lib64/ -# 修改头文件、链接库访问权限 -chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn* -``` - -#### 2.2.2. 安装nccl库 - -```bash -# 下载文件 nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm -wget -c https://paddlehub.bj.bcebos.com/serving/nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm -# 安装nccl的repo -rpm -i nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm -# 更新索引 -yum -y update -# 安装包 -yum -y install libnccl-2.4.7-1+cuda9.2 libnccl-devel-2.4.7-1+cuda9.2 libnccl-static-2.4.7-1+cuda9.2 -``` - -### 2.3. 安装 cmake 3.15 -如果机器没有安装cmake或者已安装cmake的版本低于3.0,请执行以下步骤 - -```bash -# 如果原来的已经安装低于3.0版本的cmake,请先卸载原有低版本 cmake -yum -y remove cmake -# 下载源代码并解压 -wget -c https://github.com/Kitware/CMake/releases/download/v3.15.0/cmake-3.15.0.tar.gz -tar xvfz cmake-3.15.0.tar.gz -# 编译cmake -cd cmake-3.15.0 -./configure -make -j4 -# 安装并检查cmake版本 -make install -cmake --version -# 在cmake-3.15.0目录中,将相应的头文件目录(curl目录,为PaddleServing的依赖头文件目录)拷贝到系统include目录下 -cp -r Utilities/cmcurl/include/curl/ /usr/include/ -``` - -### 2.4. 为依赖库增加相应的软连接 - - 现在Linux系统中大部分链接库的名称都以版本号作为后缀,如libcurl.so.4.3.0。这种命名方式最大的问题是,CMakeList.txt中find_library命令是无法识别使用这种命名方式的链接库,会导致CMake时候出错。由于本项目是用CMake构建,所以务必保证相应的链接库以 .so 或 .a为后缀命名。解决这个问题最简单的方式就是用创建一个软连接指向相应的链接库。在百度云的机器中,只有curl库的命名方式有问题。所以命令如下:(如果是其他库,解决方法也类似): - -```bash -ln -s /usr/lib64/libcurl.so.4.3.0 /usr/lib64/libcurl.so -``` - - -### 2.5. 编译安装PaddleServing +## 1. 编译安装PaddleServing 下列步骤介绍CPU版本以及GPU版本的PaddleServing编译安装过程。 ```bash @@ -134,7 +46,7 @@ serving └── tools ``` -### 2.6. 安装PaddleSegServing +## 2. 安装PaddleSegServing ```bash # Step 1. 在~目录下下载PaddleSeg代码 diff --git a/serving/README.md b/serving/README.md index 826b53ef..e2440c3b 100644 --- a/serving/README.md +++ b/serving/README.md @@ -1,25 +1,113 @@ -# PaddleSeg Serving +# PaddleSegServing ## 1.简介 PaddleSegServing是基于PaddleSeg开发的实时图像分割服务的企业级解决方案。用户仅需关注模型本身,无需理解模型模型的加载、预测以及GPU/CPU资源的并发调度等细节操作,通过设置不同的参数配置,即可根据自身的业务需求定制化不同图像分割服务。目前,PaddleSegServing支持人脸分割、城市道路分割、宠物外形分割模型。本文将通过一个人脸分割服务的搭建示例,展示PaddleSeg服务通用的搭建流程。 ## 2.预编译版本安装及搭建服务流程 -### 2.1. 下载预编译的PaddleSegServing +运行PaddleSegServing需要依赖其他的链接库,请保证在下载安装前系统环境已经具有相应的依赖项。 +安装以及搭建服务的流程均在Centos和Ubuntu系统上验证。以下是Centos系统上的搭建流程,Ubuntu版本的依赖项安装流程介绍在[Ubuntu系统下依赖项的安装教程](UBUNTU.md)。 + +### 2.1. 系统依赖项 +依赖项 | 验证过的版本 + -- | -- +Linux | Centos 6.10 / 7, Ubuntu16.07 +CMake | 3.0+ +GCC | 4.8.2 +Python| 2.7 +GO编译器| 1.9.2 +openssl| 1.0.1+ +bzip2 | 1.0.6+ + +如果需要使用GPU预测,还需安装以下几个依赖库 + + GPU库 | 验证过的版本 + -- | -- +CUDA | 9.2 +cuDNN | 7.1.4 +nccl | 2.4.7 + +### 2.2. 安装依赖项 + +#### 2.2.1. 安装openssl、Go编译器以及bzip2 + +```bash +yum -y install openssl openssl-devel golang bzip2-libs bzip2-devel +``` +#### 2.2.2. 安装GPU预测的依赖项(如果需要使用GPU预测,必须执行此步骤) +#### 2.2.2.1. 安装配置CUDA9.2以及cuDNN 7.1.4 +该百度云机器已经安装CUDA以及cuDNN,仅需复制相关头文件与链接库(具体目录以自己测试的机器为准) 。 + +```bash +# 看情况确定是否需要安装 cudnn +# 进入 cudnn 根目录 +cd /home/work/cudnn/cudnn7.1.4 +# 拷贝头文件 +cp include/cudnn.h /usr/local/cuda/include/ +# 拷贝链接库 +cp lib64/libcudnn* /usr/local/cuda/lib64/ +# 修改头文件、链接库访问权限 +chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn* +``` + +#### 2.2.2.2. 安装nccl库 + +```bash +# 下载文件 nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm +wget -c https://paddlehub.bj.bcebos.com/serving/nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm +# 安装nccl的repo +rpm -i nccl-repo-rhel7-2.4.7-ga-cuda9.2-1-1.x86_64.rpm +# 更新索引 +yum -y update +# 安装包 +yum -y install libnccl-2.4.7-1+cuda9.2 libnccl-devel-2.4.7-1+cuda9.2 libnccl-static-2.4.7-1+cuda9.2 +``` + +### 2.2.3. 安装 cmake 3.15 +如果机器没有安装cmake或者已安装cmake的版本低于3.0,请执行以下步骤 + +```bash +# 如果原来的已经安装低于3.0版本的cmake,请先卸载原有低版本 cmake +yum -y remove cmake +# 下载源代码并解压 +wget -c https://github.com/Kitware/CMake/releases/download/v3.15.0/cmake-3.15.0.tar.gz +tar xvfz cmake-3.15.0.tar.gz +# 编译cmake +cd cmake-3.15.0 +./configure +make -j4 +# 安装并检查cmake版本 +make install +cmake --version +# 在cmake-3.15.0目录中,将相应的头文件目录(curl目录,为PaddleServing的依赖头文件目录)拷贝到系统include目录下 +cp -r Utilities/cmcurl/include/curl/ /usr/include/ +``` + +### 2.2.4. 为依赖库增加相应的软连接 + + 现在Linux系统中大部分链接库的名称都以版本号作为后缀,如libcurl.so.4.3.0。这种命名方式最大的问题是,CMakeList.txt中find_library命令是无法识别使用这种命名方式的链接库,会导致CMake时候出错。由于本项目是用CMake构建,所以务必保证相应的链接库以 .so 或 .a为后缀命名。解决这个问题最简单的方式就是用创建一个软连接指向相应的链接库。在百度云的机器中,只有curl库的命名方式有问题。所以命令如下:(如果是其他库,解决方法也类似): + +```bash +ln -s /usr/lib64/libcurl.so.4.3.0 /usr/lib64/libcurl.so +``` + +### 2.3. 下载预编译的PaddleSegServing 预编译版本在Centos7.6系统下编译,如果想快速体验PaddleSegServing,可在此系统下下载预编译版本进行安装。预编译版本有两个,一个是针对有GPU的机器,推荐安装GPU版本PaddleSegServing。另一个是CPU版本PaddleServing,针对无GPU的机器。 -#### 2.1.1. 下载并解压GPU版本PaddleSegServing +#### 2.3.1. 下载并解压GPU版本PaddleSegServing ```bash +# TODO:修改链接 cd ~ wget -c XXXX/PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz -tar xvfz PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz +tar xvfz PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz seg-serving ``` -#### 2.1.2. 下载并解压CPU版本PaddleSegServing +#### 2.3.2. 下载并解压CPU版本PaddleSegServing ```bash +# TODO:修改链接 cd ~ wget -c XXXX/PaddleSegServing.centos7.6_cuda9.2_cpu.tar.gz -tar xvfz PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz +tar xvfz PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz seg-serving ``` 解压后的PaddleSegServing目录如下。 @@ -36,13 +124,22 @@ tar xvfz PaddleSegServing.centos7.6_cuda9.2_gpu.tar.gz └── log ``` -### 2.2. 运行PaddleSegServing +### 2.4 安装动态库 +把 libiomp5.so, libmklml_gnu.so, libmklml_intel.so拷贝到/usr/lib。 + +```bash +cd seg-serving/bin/ +cp libiomp5.so libmklml_gnu.so libmklml_intel.so /usr/lib +``` + + +### 2.5. 运行PaddleSegServing 本节将介绍如何运行以及测试PaddleSegServing。 -#### 2.2.1. 搭建人脸分割服务 +#### 2.5.1. 搭建人脸分割服务 搭建人脸分割服务只需完成一些配置文件的编写即可,其他分割服务的搭建流程类似。 -##### 2.2.1.1. 下载人脸分割模型文件,并将其复制到相应目录。 +##### 2.5.1.1. 下载人脸分割模型文件,并将其复制到相应目录。 ```bash # 下载人脸分割模型 wget -c https://paddleseg.bj.bcebos.com/inference_model/deeplabv3p_xception65_humanseg.tgz @@ -52,11 +149,7 @@ cp -r deeplabv3p_xception65_humanseg seg-serving/bin/data/model/paddle/fluid ``` -##### 2.2.1.2. 配置参数文件 - -参数文件如,PaddleSegServing仅新增一个配置文件seg_conf.yaml,用来指定具体分割模型的一些参数,如均值、方差、图像尺寸等。该配置文件可在gflags.conf中通过--seg_conf_file指定。 - -其他配置文件的字段解释可参考以下链接:https://github.com/PaddlePaddle/Serving/blob/develop/doc/SERVING_CONFIGURE.md (TODO:介绍seg_conf.yaml中每个字段的含义) +##### 2.5.1.2. 配置参数文件。参数文件如下。PaddleSegServing仅新增一个配置文件seg_conf.yaml,用来指定具体分割模型的一些参数,如均值、方差、图像尺寸等。该配置文件可在gflags.conf中通过--seg_conf_file指定。其他配置文件的字段解释可参考以下链接:https://github.com/PaddlePaddle/Serving/blob/develop/doc/SERVING_CONFIGURE.md ```bash conf/ @@ -68,7 +161,28 @@ conf/ └── workflow.prototxt ``` -#### 2.2.2 运行服务端程序 +以下为seg_conf.yaml文件内容以及每一个配置项的内容。 + +```bash +%YAML:1.0 +# 输入到模型的图像的尺寸。会将任意图片resize到513*513尺寸的图像,再放入模型进行推测。 +SIZE: [513, 513] +# 均值 +MEAN: [104.008, 116.669, 122.675] +# 方差 +STD: [1.0, 1.0, 1.0] +# 通道数 +CHANNELS: 3 +# 类别数量 +CLASS_NUM: 2 +# 加载的模型的名称,需要与model_toolkit.prototxt中对应模型的名称保持一致。 +MODEL_NAME: "human_segmentation" +``` + + + + +#### 2.5.2 运行服务端程序 ```bash # 1. 设置环境变量 @@ -77,9 +191,36 @@ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/lib64:$LD_LIBRARY_PATH cd ~/serving/build/output/demo/seg-serving/bin/ ./seg-serving ``` -#### 2.2.3.运行客户端程序进行测试 (建议在windows、mac测试,可直接查看分割后的图像) - -客户端程序是用Python3编写的,代码简洁易懂,可以通过运行客户端验证服务的正确性以及性能表现。 +#### 2.5.3.运行客户端程序进行测试 (建议在windows、mac测试,可直接查看分割后的图像) +```bash +以下为PaddleSeg的目录结构,客户端在PaddleSeg/serving/tools目录。 +PaddleSeg +├── configs +├── contrib +├── dataset +├── docs +├── inference +├── pdseg +├── README.md +├── requirements.txt +├── scripts +├── serving +│ ├── COMPILE_GUIDE.md +│ ├── imgs +│ ├── README.md +│ ├── requirements.txt +│ ├── seg-serving +│ ├── tools # 客户端目录 +│ │ ├── images # 测试的图像目录,可放置jpg格式或其他三通道格式的图像,以jpg或jpeg作为文件后缀名 +│ │ │  ├── 1.jpg +│ │ │ ├── 2.jpg +│ │ │ └── 3.jpg +│ │ └── image_seg_client.py # 客户端测试代码 +│ └── UBUNTU.md +├── test +└── test.md +``` +客户端程序是用Python3编写的,代码简洁易懂,可以通过运行客户端验证服务的正确性以及性能表现。测试的图像放在images目录下, ```bash # 使用Python3.6,需要安装opencv-python、requests、numpy包(建议安装anaconda) @@ -90,4 +231,4 @@ python3.6 image_seg_client.py ``` ## 3. 源码编译安装及搭建服务流程 (可选) -源码编译安装时间较长,一般推荐在centos7.6下安装预编译版本进行使用。如果您系统版本非centos7.6或者您想进行二次开发,请点击以下链接查看[源码编译安装流程](./COMPILE_GUIDE.md)。 +源码编译安装时间较长,一般推荐在centos7.6下安装预编译版本进行使用。如果您系统版本非centos7.6或者您想进行二次开发,请点击以下链接查看[源码编译安装流程](./COMPILE_GUIDE.md)。 \ No newline at end of file diff --git a/serving/UBUNTU.md b/serving/UBUNTU.md new file mode 100644 index 00000000..84d33551 --- /dev/null +++ b/serving/UBUNTU.md @@ -0,0 +1,76 @@ +# Ubuntu系统下依赖项的安装教程 +运行PaddleSegServing需要系统安装一些依赖库。在不同发行版本的Linux系统下,安装依赖项的具体命令略有不同,以下介绍在Ubuntu 16.07下安装依赖项的方法。 + +## 1. 安装ssl、go、python、bzip2、crypto. + +```bash +sudo apt-get install golang-1.10 python2.7 libssl1.0.0 libssl-dev libssl-doc libcrypto++-dev libcrypto++-doc libcrypto++-utils libbz2-1.0 libbz2-dev +``` + +## 2. 为ssl、crypto、curl链接库添加软连接 + +```bash +ln -s /lib/x86_64-linux-gnu/libssl.so.1.0.0 /usr/lib/x86_64-linux-gnu/libssl.so +ln -s /lib/x86_64-linux-gnu/libcrypto.so.1.0.0 /usr/lib/x86_64-linux-gnu/libcrypto.so.10 +ln -s /usr/lib/x86_64-linux-gnu/libcurl.so.4.4.0 /usr/lib/x86_64-linux-gnu/libcurl.so +``` + +## 3. 安装GPU依赖项(如果需要使用GPU预测,必须执行此步骤) +### 3.1. 安装配置CUDA9.2以及cuDNN 7.1.4 +方法与[预编译安装流程](README.md) 2.2.2.1节一样。 + +### 3.2. 安装nccl库 + +```bash +# 下载nccl相关的deb包 +wget -c xxx +sudo apt-key add /var/nccl-repo-2.4.8-ga-cuda9.2/7fa2af80.pub +# 安装deb包 +sudo dpkg -i nccl-repo-ubuntu1604-2.4.8-ga-cuda9.2_1-1_amd64.deb +# 更新索引 +sudo apt update +# 安装nccl库 +sudo apt-get install libnccl2 libnccl-dev +``` + +## 4. 安装cmake 3.15 +如果机器没有安装cmake或者已安装cmake的版本低于3.0,请执行以下步骤 + +```bash +# 如果原来的已经安装低于3.0版本的cmake,请先卸载原有低版本 cmake +sudo apt-get autoremove cmake +``` +其余安装cmake的流程请参考以下链接[预编译安装流程](README.md) 2.2.3节。 + +## 5. 安装PaddleSegServing +### 5.1. 下载并解压GPU版本PaddleSegServing + +```bash +# TODO:修改链接 +cd ~ +wget -c XXXX/PaddleSegServing.ubuntu16.07_cuda9.2_gpu.tar.gz +tar xvfz PaddleSegServing.ubuntu16.07_cuda9.2_gpu.tar.gz seg-serving +``` + +### 5.2. 下载并解压CPU版本PaddleSegServing + +```bash +# TODO:修改链接 +cd ~ +wget -c XXXX/PaddleSegServing.ubuntu16.07_cuda9.2_cpu.tar.gz +tar xvfz PaddleSegServing.ubuntu16.07_cuda9.2_gpu.tar.gz seg-serving +``` + +## 6. gcc版本问题 +在Ubuntu 16.07系统中,默认的gcc版本为5.4.0。而目前PaddleSegServing仅支持gcc 4.8编译,所以如果测试的机器gcc版本为5.4,请先进行降级(无需卸载原有的gcc)。 + +```bash +# 安装gcc 4.8 +sudo apt-get install gcc-4.8 +# 查看是否成功安装gcc4.8 +ls /usr/bin/gcc* +# 设置gcc4.8的优先级,使其能被gcc命令优先连接gcc4.8 +sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100 +# 查看设置结果(非必须) +sudo update-alternatives --config gcc +``` diff --git a/serving/seg-serving/conf/gflags.conf b/serving/seg-serving/conf/gflags.conf index 87318f8c..1c49f429 100644 --- a/serving/seg-serving/conf/gflags.conf +++ b/serving/seg-serving/conf/gflags.conf @@ -1,2 +1,5 @@ --enable_model_toolkit --seg_conf_file=./conf/seg_conf.yaml +--num_threads=1 +--bthread_min_concurrency=4 +--bthread_concurrency=4 diff --git a/serving/seg-serving/conf/model_toolkit.prototxt b/serving/seg-serving/conf/model_toolkit.prototxt index 0cf63dff..9bc44c7e 100644 --- a/serving/seg-serving/conf/model_toolkit.prototxt +++ b/serving/seg-serving/conf/model_toolkit.prototxt @@ -1,6 +1,6 @@ engines { name: "human_segmentation" - type: "FLUID_GPU_NATIVE" + type: "FLUID_GPU_ANALYSIS" reloadable_meta: "./data/model/paddle/fluid_time_file" reloadable_type: "timestamp_ne" model_data_path: "./data/model/paddle/fluid/deeplabv3p_xception65_humanseg" diff --git a/serving/seg-serving/op/image_seg_op.cpp b/serving/seg-serving/op/image_seg_op.cpp index 77314705..0473b515 100644 --- a/serving/seg-serving/op/image_seg_op.cpp +++ b/serving/seg-serving/op/image_seg_op.cpp @@ -128,16 +128,30 @@ int ImageSegOp::inference() { mask_raw[di] = label; } + //cv::Mat mask_mat = cv::Mat(height, width, CV_32FC1); cv::Mat mask_mat = cv::Mat(height, width, CV_8UC1); - mask_mat.data = mask_raw.data(); + //scoremap + // mask_mat.data = reinterpret_cast(data + out_size); + //mask_mat.data = mask_raw.data(); + std::vector temp_mat(out_size, 0); + for(int i = 0; i < out_size; ++i){ + temp_mat[i] = 255 * data[i + out_size]; + } + mask_mat.data = temp_mat.data(); + cv::Mat mask_temp_mat((*height_vec)[si], (*width_vec)[si], mask_mat.type()); //Size(cols, rows) cv::resize(mask_mat, mask_temp_mat, mask_temp_mat.size()); - // cv::resize(mask_mat, mask_temp_mat, cv::Size((*width_vec)[si], (*height_vec)[si])); - +//debug + //for(int i = 0; i < (*height_vec)[si]; ++i){ + // for(int j = 0; j < (*width_vec)[si]; ++j) { + // std::cout << mask_temp_mat.at(i, j) << " "; + // } + // std::cout << std::endl; + //} std::vector mat_buff; cv::imencode(".png", mask_temp_mat, mat_buff); - ins->set_mask(mat_buff.data(), mat_buff.size()); + ins->set_mask(reinterpret_cast(mat_buff.data()), mat_buff.size()); } // release out tensor object resource diff --git a/serving/seg-serving/op/reader_op.cpp b/serving/seg-serving/op/reader_op.cpp index d7a630e0..b825d81b 100644 --- a/serving/seg-serving/op/reader_op.cpp +++ b/serving/seg-serving/op/reader_op.cpp @@ -103,7 +103,8 @@ int ReaderOp::inference() { const ImageSegReqItem& ins = req->instances(si); // read dense image from request bytes const char* binary = ins.image_binary().c_str(); - size_t length = ins.image_length(); + //size_t length = ins.image_length(); + size_t length = ins.image_binary().length(); if (length == 0) { LOG(ERROR) << "Empty image, length is 0"; return -1; diff --git a/serving/seg-serving/op/write_json_op.cpp b/serving/seg-serving/op/write_json_op.cpp index 17a4dc3c..fa80d61d 100644 --- a/serving/seg-serving/op/write_json_op.cpp +++ b/serving/seg-serving/op/write_json_op.cpp @@ -50,7 +50,7 @@ int WriteJsonOp::inference() { std::string err_string; uint32_t batch_size = seg_out->item_size(); LOG(INFO) << "batch_size = " << batch_size; - LOG(INFO) << seg_out->ShortDebugString(); +// LOG(INFO) << seg_out->ShortDebugString(); for (uint32_t si = 0; si < batch_size; si++) { ResponseItem* ins = res->add_prediction(); //LOG(INFO) << "Original image width = " << seg_out->width(si) << ", height = " << seg_out->height(si); @@ -59,6 +59,7 @@ int WriteJsonOp::inference() { return -1; } std::string* text = ins->mutable_info(); + LOG(INFO) << seg_out->item(si).ShortDebugString(); if (!ProtoMessageToJson(seg_out->item(si), text, &err_string)) { LOG(ERROR) << "Failed convert message[" << seg_out->item(si).ShortDebugString() diff --git a/serving/tools/image_seg_client.py b/serving/tools/image_seg_client.py index caef60fb..a537fecc 100644 --- a/serving/tools/image_seg_client.py +++ b/serving/tools/image_seg_client.py @@ -1,6 +1,5 @@ # coding: utf-8 -import sys - +import os import cv2 import requests import json @@ -8,115 +7,96 @@ import base64 import numpy as np import time import threading +import re #分割服务的地址 -#IMAGE_SEG_URL = 'http://yq01-gpu-151-23-00.epc:8010/ImageSegService/inference' -#IMAGE_SEG_URL = 'http://106.12.25.202:8010/ImageSegService/inference' -IMAGE_SEG_URL = 'http://180.76.118.53:8010/ImageSegService/inference' - -# 请求预测服务 -# input_img 要预测的图片列表 -def get_item_json(input_img): - with open(input_img, mode="rb") as fp: - # 使用 http 协议请求服务时, 请使用 base64 编码发送图片 - item_binary_b64 = str(base64.b64encode(fp.read()), 'utf-8') - item_size = len(item_binary_b64) - item_json = { - "image_length": item_size, - "image_binary": item_binary_b64 - } - return item_json - - -def request_predictor_server(input_img_list, dir_name): - data = {"instances" : [get_item_json(dir_name + input_img) for input_img in input_img_list]} - response = requests.post(IMAGE_SEG_URL, data=json.dumps(data)) - try: - response = json.loads(response.text) - prediction_list = response["prediction"] - mask_response_list = [mask_response["info"] for mask_response in prediction_list] - mask_raw_list = [json.loads(mask_response)["mask"] for mask_response in mask_response_list] - except Exception as err: - print ("Exception[%s], server_message[%s]" % (str(err), response.text)) - return None - # 使用 json 协议回复的包也是 base64 编码过的 - mask_binary_list = [base64.b64decode(mask_raw) for mask_raw in mask_raw_list] - m = [np.fromstring(mask_binary, np.uint8) for mask_binary in mask_binary_list] - return m - -# 对预测结果进行可视化 -# input_raw_mask 是server返回的预测结果 -# output_img 是可视化结果存储路径 -def visualization(mask_mat, output_img): - # ColorMap for visualization more clearly - color_map = [[128, 64, 128], - [244, 35, 231], - [69, 69, 69], - [102, 102, 156], - [190, 153, 153], - [153, 153, 153], - [250, 170, 29], - [219, 219, 0], - [106, 142, 35], - [152, 250, 152], - [69, 129, 180], - [219, 19, 60], - [255, 0, 0], - [0, 0, 142], - [0, 0, 69], - [0, 60, 100], - [0, 79, 100], - [0, 0, 230], - [119, 10, 32]] - - im = cv2.imdecode(mask_mat, 1) - w, h, c = im.shape - im2 = cv2.resize(im, (w, h)) - im = im2 - for i in range(0, h): - for j in range(0, w): - im[i, j] = color_map[im[i, j, 0]] - cv2.imwrite(output_img, im) - - -#benchmark test -def benchmark_test(batch_size, img_list): - start = time.time() - total_size = len(img_list) - for i in range(0, total_size, batch_size): - mask_mat_list = request_predictor_server(img_list[i : np.min([i + batch_size, total_size])], "images/") - # 将获得的mask matrix转换成可视化图像,并在当前目录下保存为图像文件 - # 如果进行压测,可以把这句话注释掉 - # for j in range(len(mask_mat_list)): - # visualization(mask_mat_list[j], img_list[j + i]) - latency = time.time() - start - print("batch size = %d, total latency = %f s" % (batch_size, latency)) - +IMAGE_SEG_URL = 'http://xxx.xxx.xxx.xxx:8010/ImageSegService/inference' class ClientThread(threading.Thread): - def __init__(self, thread_id, batch_size): + def __init__(self, thread_id, image_data_repo): threading.Thread.__init__(self) self.__thread_id = thread_id - self.__batch_size = batch_size + self.__image_data_repo = image_data_repo def run(self): - self.request_image_seg_service(3) + self.__request_image_seg_service() - def request_image_seg_service(self, imgs_num): - total_size = imgs_num - img_list = [str(i + 1) + ".jpg" for i in range(total_size)] - # batch_size_list = [2**i for i in range(0, 4)] + def __request_image_seg_service(self): # 持续发送150个请求 - batch_size_list = [self.__batch_size] * 150 - i = 1 - for batch_size in batch_size_list: + for i in range(1, 151): print("Epoch %d, thread %d" % (i, self.__thread_id)) - i += 1 - benchmark_test(batch_size, img_list) - + self.__benchmark_test() + + # benchmark test + def __benchmark_test(self): + start = time.time() + for image_filename in self.__image_data_repo: + mask_mat_list = self.__request_predictor_server(image_filename) + input_img = self.__image_data_repo.get_image_matrix(image_filename) + # 将获得的mask matrix转换成可视化图像,并在当前目录下保存为图像文件 + # 如果进行压测,可以把这句话注释掉 + for j in range(len(mask_mat_list)): + self.__visualization(mask_mat_list[j], image_filename, 2, input_img) + latency = time.time() - start + print("total latency = %f s" % (latency)) + + # 对预测结果进行可视化 + # input_raw_mask 是server返回的预测结果 + # output_img 是可视化结果存储路径 + def __visualization(self, mask_mat, output_img, num_cls, input_img): + # ColorMap for visualization more clearly + n = num_cls + color_map = [] + for j in range(n): + lab = j + a = b = c = 0 + color_map.append([a, b, c]) + i = 0 + while lab: + color_map[j][0] |= (((lab >> 0) & 1) << (7 - i)) + color_map[j][1] |= (((lab >> 1) & 1) << (7 - i)) + color_map[j][2] |= (((lab >> 2) & 1) << (7 - i)) + i += 1 + lab >>= 3 + im = cv2.imdecode(mask_mat, 1) + w, h, c = im.shape + im2 = cv2.resize(im, (w, h)) + im = im2 + # I = aF + (1-a)B + a = im / 255.0 + im = a * input_img + (1 - a) * [255, 255, 255] + cv2.imwrite(output_img, im) + + def __request_predictor_server(self, input_img): + data = {"instances": [self.__get_item_json(input_img)]} + response = requests.post(IMAGE_SEG_URL, data=json.dumps(data)) + try: + response = json.loads(response.text) + prediction_list = response["prediction"] + mask_response_list = [mask_response["info"] for mask_response in prediction_list] + mask_raw_list = [json.loads(mask_response)["mask"] for mask_response in mask_response_list] + except Exception as err: + print("Exception[%s], server_message[%s]" % (str(err), response.text)) + return None + # 使用 json 协议回复的包也是 base64 编码过的 + mask_binary_list = [base64.b64decode(mask_raw) for mask_raw in mask_raw_list] + m = [np.fromstring(mask_binary, np.uint8) for mask_binary in mask_binary_list] + return m + + # 请求预测服务 + # input_img 要预测的图片列表 + def __get_item_json(self, input_img): + # 使用 http 协议请求服务时, 请使用 base64 编码发送图片 + item_binary_b64 = str(base64.b64encode(self.__image_data_repo.get_image_binary(input_img)), 'utf-8') + item_size = len(item_binary_b64) + item_json = { + "image_length": item_size, + "image_binary": item_binary_b64 + } + return item_json -def create_thread_pool(thread_num, batch_size): - return [ClientThread(i + 1, batch_size) for i in range(thread_num)] +def create_thread_pool(thread_num, image_data_repo): + return [ClientThread(i + 1, image_data_repo) for i in range(thread_num)] def run_threads(thread_pool): @@ -126,7 +106,35 @@ def run_threads(thread_pool): for thread in thread_pool: thread.join() +class ImageDataRepo: + def __init__(self, dir_name): + print("Loading images data...") + self.__data = {} + pattern = re.compile(".+\.(jpg|jpeg)", re.I) + if os.path.isdir(dir_name): + for image_filename in os.listdir(dir_name): + if pattern.match(image_filename): + full_path = os.path.join(dir_name, image_filename) + fp = open(full_path, mode="rb") + image_binary_data = fp.read() + image_mat_data = cv2.imread(full_path) + self.__data[image_filename] = (image_binary_data, image_mat_data) + else: + raise Exception("Please use directory to initialize"); + print("Finish loading.") + def __iter__(self): + for filename in self.__data: + yield filename + + def get_image_binary(self, image_name): + return self.__data[image_name][0] + + def get_image_matrix(self, image_name): + return self.__data[image_name][1] + + if __name__ == "__main__": - thread_pool = create_thread_pool(thread_num=2, batch_size=1) + #preprocess + IDR = ImageDataRepo("images") + thread_pool = create_thread_pool(thread_num=1, image_data_repo=IDR) run_threads(thread_pool) - -- GitLab