提交 267f6508 编写于 作者: D dongshuilong

fix ppshitu_lite bugs and fix README.md

上级 96e3a208
# Paddle-Lite端侧部署 # PP-ShiTu在Paddle-Lite端侧部署
本教程将介绍基于[Paddle Lite](https://github.com/PaddlePaddle/Paddle-Lite) 在移动端部署PaddleDetection模型的详细步骤。 本教程将介绍基于[Paddle Lite](https://github.com/PaddlePaddle/Paddle-Lite) 在移动端部署PaddleDetection模型的详细步骤。
...@@ -125,30 +125,71 @@ Paddle-Lite 提供了多种策略来自动优化原始的模型,其中包括 ...@@ -125,30 +125,71 @@ Paddle-Lite 提供了多种策略来自动优化原始的模型,其中包括
#### 2.1.3 转换示例 #### 2.1.3 转换示例
下面以PaddleDetection中的 `PicoDet` 模型为例,介绍使用`paddle_lite_opt`完成预训练模型到inference模型,再到Paddle-Lite优化模型的转换。 下面介绍使用`paddle_lite_opt`完成主体检测模型和识别模型的预训练模型,转成inference模型,最终转换成Paddle-Lite的优化模型的过程。
##### 2.1.3.1 转换主体检测模型
```shell ```shell
# 当前目录为 $PaddleClas/deploy/lite_shitu
# $code_path需替换成相应的运行目录,可以根据需要,将$code_path设置成需要的目录
export $code_path=~
cd $code_path
git clone https://github.com/PaddlePaddle/PaddleDetection.git
# 进入PaddleDetection根目录 # 进入PaddleDetection根目录
cd PaddleDetection_root_path cd PaddleDetection
# 将预训练模型导出为inference模型 # 将预训练模型导出为inference模型
python tools/export_model.py -c configs/picodet/picodet_s_320_coco.yml \ python tools/export_model.py -c configs/picodet/application/mainbody_detection/picodet_lcnet_x2_5_640_mainbody.yml -o weights=https://paddledet.bj.bcebos.com/models/picodet_lcnet_x2_5_640_mainbody.pdparams --output_dir=inference
-o weights=https://paddledet.bj.bcebos.com/models/picodet_s_320_coco.pdparams --output_dir=output_inference
# 将inference模型转化为Paddle-Lite优化模型 # 将inference模型转化为Paddle-Lite优化模型
# FP32 paddle_lite_opt --model_file=inference/picodet_lcnet_x2_5_640_mainbody/model.pdmodel --param_file=inference/picodet_lcnet_x2_5_640_mainbody/model.pdiparams --optimize_out=inference/picodet_lcnet_x2_5_640_mainbody/mainbody_det
paddle_lite_opt --valid_targets=arm --model_file=output_inference/picodet_s_320_coco/model.pdmodel --param_file=output_inference/picodet_s_320_coco/model.pdiparams --optimize_out=output_inference/picodet_s_320_coco/model
# FP16
paddle_lite_opt --valid_targets=arm --model_file=output_inference/picodet_s_320_coco/model.pdmodel --param_file=output_inference/picodet_s_320_coco/model.pdiparams --optimize_out=output_inference/picodet_s_320_coco/model --enable_fp16=true
# 将inference模型配置转化为json格式 # 将转好的模型复制到lite_shitu目录下
python deploy/lite/convert_yml_to_json.py output_inference/picodet_s_320_coco/infer_cfg.yml cd $PaddleClas/deploy/lite_shitu
mkdir models
cp $code_path/PaddleDetection/inference/picodet_lcnet_x2_5_640_mainbody/mainbody_det.nb $PaddleClas/deploy/lite_shitu/models
``` ```
最终在output_inference/picodet_s_320_coco/文件夹下生成`model.nb``infer_cfg.json`的文件。 ##### 2.1.3.2 转换识别模型
```shell
# 转换inference model
待补充,生成的inference model存储在PaddleClas/inference下,同时生成label.txt,也存在此文件夹下
# 转换为Paddle-Lite模型
paddle_lite_opt --model_file=inference/inference.pdmodel --param_file=inference/inference.pdiparams --optimize_out=inference/rec
# 将模型、label文件拷贝到lite_shitu下
cp inference/rec.nb deploy/lite_shitu/models/
cp inference/label.txt deploy/lite_shitu/models/
cd deploy/lite_shitu
```
**注意**`--optimize_out` 参数为优化后模型的保存路径,无需加后缀`.nb``--model_file` 参数为模型结构信息文件的路径,`--param_file` 参数为模型权重信息文件的路径,请注意文件名。 **注意**`--optimize_out` 参数为优化后模型的保存路径,无需加后缀`.nb``--model_file` 参数为模型结构信息文件的路径,`--param_file` 参数为模型权重信息文件的路径,请注意文件名。
##### 2.1.3.3 准备测试图像
```shell
mkdir images
# 根据需要准备测试图像,可以在images文件夹中存放多张图像
cp ../images/wangzai.jpg images/
```
##### 2.1.3.4 将yaml文件转换成json文件
```shell
# 如果测试单张图像
python generate_json_config.py --det_model_path models/mainbody_det.nb --rec_model_path models/rec.nb --rec_label_path models/label.txt --img_path images/wangzai.jpg
# or
# 如果测试多张图像
python generate_json_config.py --det_model_path models/mainbody_det.nb --rec_model_path models/rec.nb --rec_label_path models/label.txt --img_dir images
# 执行完成后,会在lit_shitu下生成shitu_config.json配置文件
```
### 2.2 与手机联调 ### 2.2 与手机联调
首先需要进行一些准备工作。 首先需要进行一些准备工作。
...@@ -183,41 +224,28 @@ List of devices attached ...@@ -183,41 +224,28 @@ List of devices attached
4. 编译lite部署代码生成移动端可执行文件 4. 编译lite部署代码生成移动端可执行文件
```shell ```shell
cd {PadddleDetection_Root} cd $PaddleClas/deploy/lite_shitu
cd deploy/lite/
inference_lite_path=/{lite prediction library path}/inference_lite_lib.android.armv8.gcc.c++_static.with_extra.with_cv/ inference_lite_path=/{lite prediction library path}/inference_lite_lib.android.armv8.gcc.c++_static.with_extra.with_cv/
mkdir $inference_lite_path/demo/cxx/lite mkdir $inference_lite_path/demo/cxx/ppshitu_lite
cp -r Makefile src/ include/ *runtime_config.json $inference_lite_path/demo/cxx/lite cp -r Makefile src/ include/ *.json models/ images/ $inference_lite_path/demo/cxx/ppshitu_lite
cd $inference_lite_path/demo/cxx/lite cd $inference_lite_path/demo/cxx/ppshitu_lite
# 执行编译,等待完成后得到可执行文件main # 执行编译,等待完成后得到可执行文件main
make ARM_ABI=arm8 make ARM_ABI=arm8
#如果是arm7,则执行 make ARM_ABI = arm7 (或者在Makefile中修改该项) #如果是arm7,则执行 make ARM_ABI = arm7 (或者在Makefile中修改该项)
``` ```
5. 准备优化后的模型、预测库文件、测试图像。 5. 准备优化后的模型、预测库文件、测试图像。
```shell ```shell
mkdir deploy mkdir deploy
cp main *runtime_config.json deploy/ mv models deploy/
mv images deploy/
cp pp_shitu deploy/
cd deploy cd deploy
mkdir model_det
mkdir model_keypoint
# 将优化后的模型、预测库文件、测试图像放置在预测库中的demo/cxx/detection文件夹下
cp {PadddleDetection_Root}/output_inference/picodet_s_320_coco/model.nb ./model_det/
cp {PadddleDetection_Root}/output_inference/picodet_s_320_coco/infer_cfg.json ./model_det/
# 如果需要关键点模型,则只需操作:
cp {PadddleDetection_Root}/output_inference/hrnet_w32_256x192/model.nb ./model_keypoint/
cp {PadddleDetection_Root}/output_inference/hrnet_w32_256x192/infer_cfg.json ./model_keypoint/
# 将测试图像复制到deploy文件夹中
cp [your_test_img].jpg ./demo.jpg
# 将C++预测动态库so文件复制到deploy文件夹中 # 将C++预测动态库so文件复制到deploy文件夹中
cp ../../../cxx/lib/libpaddle_light_api_shared.so ./ cp ../../../cxx/lib/libpaddle_light_api_shared.so ./
...@@ -227,45 +255,19 @@ cp ../../../cxx/lib/libpaddle_light_api_shared.so ./ ...@@ -227,45 +255,19 @@ cp ../../../cxx/lib/libpaddle_light_api_shared.so ./
``` ```
deploy/ deploy/
|-- model_det/ |-- models/
| |--model.nb 优化后的检测模型文件 | |--mainbody_det.nb 优化后的主体检测模型文件
| |--infer_cfg.json 检测器模型配置文件 | |--rec.nb 优化后的识别模型文件
|-- model_keypoint/ | |--label.txt 识别模型的label文件
| |--model.nb 优化后的关键点模型文件 |-- images/
| |--infer_cfg.json 关键点模型配置文件 | ... 图片文件
|-- main 生成的移动端执行文件 |-- pp_shitu 生成的移动端执行文件
|-- det_runtime_config.json 目标检测执行时参数配置文件 |-- shitu_config.json 执行时参数配置文件
|-- keypoint_runtime_config.json 关键点检测执行时参数配置文件
|-- libpaddle_light_api_shared.so Paddle-Lite库文件 |-- libpaddle_light_api_shared.so Paddle-Lite库文件
``` ```
**注意:** **注意:**
* `det_runtime_config.json` 包含了目标检测的超参数,请按需进行修改: * `shitu_config.json` 包含了目标检测的超参数,请按需进行修改
```shell
{
"model_dir_det": "./model_det/", #检测器模型路径
"batch_size_det": 1, #检测预测时batchsize
"threshold_det": 0.5, #检测器输出阈值
"image_file": "demo.jpg", #测试图片
"image_dir": "", #测试图片文件夹
"run_benchmark": true, #性能测试开关
"cpu_threads": 4 #线程数
}
```
* `keypoint_runtime_config.json` 包含了关键点检测的超参数,请按需进行修改:
```shell
{
"model_dir_keypoint": "./model_keypoint/", #关键点模型路径(不使用需为空字符)
"batch_size_keypoint": 8, #关键点预测时batchsize
"threshold_keypoint": 0.5, #关键点输出阈值
"image_file": "demo.jpg", #测试图片
"image_dir": "", #测试图片文件夹
"run_benchmark": true, #性能测试开关
"cpu_threads": 4 #线程数
}
```
6. 启动调试,上述步骤完成后就可以使用ADB将文件夹 `deploy/` push到手机上运行,步骤如下: 6. 启动调试,上述步骤完成后就可以使用ADB将文件夹 `deploy/` push到手机上运行,步骤如下:
...@@ -278,23 +280,20 @@ cd /data/local/tmp/deploy ...@@ -278,23 +280,20 @@ cd /data/local/tmp/deploy
export LD_LIBRARY_PATH=/data/local/tmp/deploy:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=/data/local/tmp/deploy:$LD_LIBRARY_PATH
# 修改权限为可执行 # 修改权限为可执行
chmod 777 main chmod 777 pp_shitu
# 以检测为例,执行程序 # 执行程序
./main det_runtime_config.json ./pp_shitu shitu_config.json
``` ```
如果对代码做了修改,则需要重新编译并push到手机上。 如果对代码做了修改,则需要重新编译并push到手机上。
运行效果如下: 运行效果如下:
<div align="center"> ![](../../docs/images/ppshitu_lite_demo.png)
<img src="../../docs/images/lite_demo.jpg" width="600">
</div>
## FAQ ## FAQ
Q1:如果想更换模型怎么办,需要重新按照流程走一遍吗? Q1:如果想更换模型怎么办,需要重新按照流程走一遍吗?
A1:如果已经走通了上述步骤,更换模型只需要替换 `.nb` 模型文件即可,同时要注意修改下配置文件中的 `.nb` 文件路径以及类别映射文件(如有必要)。 A1:如果已经走通了上述步骤,更换模型只需要替换 `.nb` 模型文件即可,同时要注意修改下配置文件中的 `.nb` 文件路径以及类别映射文件(如有必要)。
Q2:换一个图测试怎么做? Q2:换一个图测试怎么做?
A2:替换 deploy 下的测试图像为你想要测试的图像,使用 ADB 再次 push 到手机上即可。 A2:替换 deploy 下的测试图像为你想要测试的图像,并重新生成json配置文件(或者直接修改图像路径),使用 ADB 再次 push 到手机上即可。
...@@ -7,40 +7,63 @@ import yaml ...@@ -7,40 +7,63 @@ import yaml
def parse_args(): def parse_args():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--yaml_path', parser.add_argument(
type=str, '--yaml_path', type=str, default='../configs/inference_drink.yaml')
default='../configs/inference_drink.yaml') parser.add_argument(
parser.add_argument('--img_dir', type=str, default=None, '--img_dir',
help='The dir path for inference images') type=str,
parser.add_argument('--det_model_path', default=None,
type=str, default='./det.nb', help='The dir path for inference images')
help="The model path for mainbody detection") parser.add_argument(
parser.add_argument('--rec_model_path', type=str, default='./rec.nb', help="The rec model path") '--img_path',
parser.add_argument('--rec-label-path', type=str, default='./label.txt', help='The rec model label') type=str,
parser.add_argument('--arch', default=None,
type=str, help='The dir path for inference images')
default='GFL', parser.add_argument(
help='The model structure for detection model') '--det_model_path',
parser.add_argument('--fpn-stride', type=str,
type=list, default='./det.nb',
default=[8, 16, 32, 64], help="The model path for mainbody detection")
help="The fpn strid for detection model") parser.add_argument(
parser.add_argument('--keep_top_k', '--rec_model_path',
type=int, type=str,
default=100, default='./rec.nb',
help='The params for nms(postprocess for detection)') help="The rec model path")
parser.add_argument('--nms-name', parser.add_argument(
type=str, '--rec_label_path',
default='MultiClassNMS', type=str,
help='The nms name for postprocess of detection model') default='./label.txt',
parser.add_argument('--nms_threshold', help='The rec model label')
type=float, parser.add_argument(
default=0.5, '--arch',
help='The nms nms_threshold for detection postprocess') type=str,
parser.add_argument('--nms_top_k', default='PicoDet',
type=int, help='The model structure for detection model')
default=1000, parser.add_argument(
help='The nms_top_k in postprocess of detection model') '--fpn-stride',
type=list,
default=[8, 16, 32, 64],
help="The fpn strid for detection model")
parser.add_argument(
'--keep_top_k',
type=int,
default=100,
help='The params for nms(postprocess for detection)')
parser.add_argument(
'--nms-name',
type=str,
default='MultiClassNMS',
help='The nms name for postprocess of detection model')
parser.add_argument(
'--nms_threshold',
type=float,
default=0.5,
help='The nms nms_threshold for detection postprocess')
parser.add_argument(
'--nms_top_k',
type=int,
default=1000,
help='The nms_top_k in postprocess of detection model')
parser.add_argument( parser.add_argument(
'--score_threshold', '--score_threshold',
type=float, type=float,
...@@ -55,18 +78,24 @@ def main(): ...@@ -55,18 +78,24 @@ def main():
config_yaml = yaml.safe_load(open(args.yaml_path)) config_yaml = yaml.safe_load(open(args.yaml_path))
config_json = {} config_json = {}
config_json["Global"] = {} config_json["Global"] = {}
config_json["Global"]["infer_imgs"] = config_yaml["Global"]["infer_imgs"] config_json["Global"][
config_json["Global"]["infer_imgs_dir"] = args.img_dir "infer_imgs"] = args.img_path if args.img_path else config_yaml[
"Global"]["infer_imgs"]
if args.img_dir is not None:
config_json["Global"]["infer_imgs_dir"] = args.img_dir
config_json["Global"]["infer_imgs"] = None
else:
config_json["Global"][
"infer_imgs"] = args.img_path if args.img_path else config_yaml[
"Global"]["infer_imgs"]
config_json["Global"]["batch_size"] = config_yaml["Global"]["batch_size"] config_json["Global"]["batch_size"] = config_yaml["Global"]["batch_size"]
config_json["Global"]["cpu_num_threads"] = config_yaml["Global"][ config_json["Global"]["cpu_num_threads"] = min(
"cpu_num_threads"] config_yaml["Global"]["cpu_num_threads"], 4)
config_json["Global"]["image_shape"] = config_yaml["Global"]["image_shape"] config_json["Global"]["image_shape"] = config_yaml["Global"]["image_shape"]
config_json["Global"][ config_json["Global"]["det_model_path"] = args.det_model_path
"det_model_path"] = args.det_model_path config_json["Global"]["rec_model_path"] = args.rec_model_path
config_json["Global"][
"rec_model_path"] = args.rec_model_path
config_json["Global"]["rec_label_path"] = args.rec_label_path config_json["Global"]["rec_label_path"] = args.rec_label_path
config_json["Global"]["labe_list"] = config_yaml["Global"]["labe_list"] config_json["Global"]["label_list"] = config_yaml["Global"]["labe_list"]
config_json["Global"]["rec_nms_thresold"] = config_yaml["Global"][ config_json["Global"]["rec_nms_thresold"] = config_yaml["Global"][
"rec_nms_thresold"] "rec_nms_thresold"]
config_json["Global"]["max_det_results"] = config_yaml["Global"][ config_json["Global"]["max_det_results"] = config_yaml["Global"][
......
...@@ -60,7 +60,7 @@ class ConfigPaser { ...@@ -60,7 +60,7 @@ class ConfigPaser {
// Get label_list for visualization // Get label_list for visualization
if (config["Global"].isMember("label_list")) { if (config["Global"].isMember("label_list")) {
label_list_.clear(); label_list_.clear();
for (auto item : config["label_list"]) { for (auto item : config["Global"]["label_list"]) {
label_list_.emplace_back(item.as<std::string>()); label_list_.emplace_back(item.as<std::string>());
} }
} else { } else {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#pragma once
#include "paddle_api.h" // NOLINT #include "paddle_api.h" // NOLINT
#include "json/json.h" #include "json/json.h"
#include <arm_neon.h> #include <arm_neon.h>
...@@ -54,8 +55,10 @@ public: ...@@ -54,8 +55,10 @@ public:
} }
LoadLabel(config_file["Global"]["rec_label_path"].as<std::string>()); LoadLabel(config_file["Global"]["rec_label_path"].as<std::string>());
SetPreProcessParam(config_file["RecPreProcess"]["transform_ops"]); SetPreProcessParam(config_file["RecPreProcess"]["transform_ops"]);
if (!config_file["Global"].isMember("return_k")) if (!config_file["Global"].isMember("return_k")){
this->topk = config_file["Global"]["return_k"].as<int>(); this->topk = config_file["Global"]["return_k"].as<int>();
}
printf("rec model create!\n");
} }
void LoadLabel(std::string path) { void LoadLabel(std::string path) {
......
...@@ -14,13 +14,14 @@ ...@@ -14,13 +14,14 @@
#pragma once #pragma once
#include <string> #include <algorithm>
#include <vector>
#include <memory>
#include <utility>
#include <ctime> #include <ctime>
#include <include/recognition.h>
#include <memory>
#include <numeric> #include <numeric>
#include <algorithm> #include <string>
#include <utility>
#include <vector>
namespace PPShiTu { namespace PPShiTu {
...@@ -32,8 +33,11 @@ struct ObjectResult { ...@@ -32,8 +33,11 @@ struct ObjectResult {
int class_id; int class_id;
// Confidence of detected object // Confidence of detected object
float confidence; float confidence;
// RecModel result
std::vector<RESULT> rec_result;
}; };
void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold); void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold, bool rec_nms=false);
} // namespace PPShiTu } // namespace PPShiTu
...@@ -73,17 +73,10 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs, ...@@ -73,17 +73,10 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs,
std::vector<double> det_t = {0, 0, 0}; std::vector<double> det_t = {0, 0, 0};
int steps = ceil(float(batch_imgs.size()) / batch_size_det); int steps = ceil(float(batch_imgs.size()) / batch_size_det);
for (int idx = 0; idx < steps; idx++) { for (int idx = 0; idx < steps; idx++) {
std::vector<cv::Mat> batch_imgs;
int left_image_cnt = batch_imgs.size() - idx * batch_size_det; int left_image_cnt = batch_imgs.size() - idx * batch_size_det;
if (left_image_cnt > batch_size_det) { if (left_image_cnt > batch_size_det) {
left_image_cnt = batch_size_det; left_image_cnt = batch_size_det;
} }
/* for (int bs = 0; bs < left_image_cnt; bs++) { */
/* std::string image_file_path = all_img_paths.at(idx * batch_size_det +
* bs); */
/* cv::Mat im = cv::imread(image_file_path, 1); */
/* batch_imgs.insert(batch_imgs.end(), im); */
/* } */
// Store all detected result // Store all detected result
std::vector<PPShiTu::ObjectResult> result; std::vector<PPShiTu::ObjectResult> result;
std::vector<int> bbox_num; std::vector<int> bbox_num;
...@@ -108,32 +101,7 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs, ...@@ -108,32 +101,7 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs,
} }
detect_num += 1; detect_num += 1;
im_result.push_back(item); im_result.push_back(item);
/* if (item.rect.size() > 6) { */
/* is_rbox = true; */
/* printf("class=%d confidence=%.4f rect=[%d %d %d %d %d %d %d %d]\n",
*/
/* item.class_id, */
/* item.confidence, */
/* item.rect[0], */
/* item.rect[1], */
/* item.rect[2], */
/* item.rect[3], */
/* item.rect[4], */
/* item.rect[5], */
/* item.rect[6], */
/* item.rect[7]); */
/* } else { */
/* printf("class=%d confidence=%.4f rect=[%d %d %d %d]\n", */
/* item.class_id, */
/* item.confidence, */
/* item.rect[0], */
/* item.rect[1], */
/* item.rect[2], */
/* item.rect[3]); */
/* } */
} }
/* std::cout << all_img_paths.at(idx * batch_size_det + i) */
/* << " The number of detected box: " << detect_num << std::endl; */
item_start_idx = item_start_idx + bbox_num[i]; item_start_idx = item_start_idx + bbox_num[i];
} }
...@@ -144,14 +112,13 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs, ...@@ -144,14 +112,13 @@ void DetPredictImage(const std::vector<cv::Mat> &batch_imgs,
} }
void PrintResult(const std::string &image_path, void PrintResult(const std::string &image_path,
std::vector<PPShiTu::ObjectResult> &det_result, std::vector<PPShiTu::ObjectResult> &det_result) {
std::vector<std::vector<PPShiTu::RESULT>> &rec_results) {
printf("%s:\n", image_path.c_str()); printf("%s:\n", image_path.c_str());
for (int i = 0; i < det_result.size(); ++i) { for (int i = 0; i < det_result.size(); ++i) {
printf("\tresult%d: bbox[%d, %d, %d, %d], score: %f, label: %s\n", i, printf("\tresult%d: bbox[%d, %d, %d, %d], score: %f, label: %s\n", i,
det_result[i].rect[0], det_result[i].rect[1], det_result[i].rect[2], det_result[i].rect[0], det_result[i].rect[1], det_result[i].rect[2],
det_result[i].rect[3], rec_results[i][0].score, det_result[i].rect[3], det_result[i].rec_result[0].score,
rec_results[i][0].class_name.c_str()); det_result[i].rec_result[0].class_name.c_str());
} }
} }
...@@ -163,37 +130,33 @@ int main(int argc, char **argv) { ...@@ -163,37 +130,33 @@ int main(int argc, char **argv) {
return -1; return -1;
} }
std::string config_path = argv[1]; std::string config_path = argv[1];
std::string img_path = ""; std::string img_dir = "";
if (argc >= 3) { if (argc >= 3) {
img_path = argv[2]; img_dir = argv[2];
} }
// Parsing command-line // Parsing command-line
PPShiTu::load_jsonf(config_path, RT_Config); PPShiTu::load_jsonf(config_path, RT_Config);
if (RT_Config["Global"]["det_inference_model_dir"] if (RT_Config["Global"]["det_model_path"].as<std::string>().empty()) {
.as<std::string>() std::cout << "Please set [det_model_path] in " << config_path << std::endl;
.empty()) {
std::cout << "Please set [det_inference_model_dir] in " << config_path
<< std::endl;
return -1; return -1;
} }
if (RT_Config["Global"]["infer_imgs"].as<std::string>().empty() && if (RT_Config["Global"]["infer_imgs"].as<std::string>().empty() &&
img_path.empty()) { img_dir.empty()) {
std::cout << "Please set [infer_imgs] in " << config_path std::cout << "Please set [infer_imgs] in " << config_path
<< " Or use command: <" << argv[0] << " [shitu_config]" << " Or use command: <" << argv[0] << " [shitu_config]"
<< " [image_dir]>" << std::endl; << " [image_dir]>" << std::endl;
return -1; return -1;
} }
if (!img_path.empty()) { if (!img_dir.empty()) {
std::cout << "Use image_dir in command line overide the path in config file" std::cout << "Use image_dir in command line overide the path in config file"
<< std::endl; << std::endl;
RT_Config["Global"]["infer_imgs_dir"] = img_path; RT_Config["Global"]["infer_imgs_dir"] = img_dir;
RT_Config["Global"]["infer_imgs"] = ""; RT_Config["Global"]["infer_imgs"] = "";
} }
// Load model and create a object detector // Load model and create a object detector
PPShiTu::ObjectDetector det( PPShiTu::ObjectDetector det(
RT_Config, RT_Config, RT_Config["Global"]["det_model_path"].as<std::string>(),
RT_Config["Global"]["det_inference_model_dir"].as<std::string>(),
RT_Config["Global"]["cpu_num_threads"].as<int>(), RT_Config["Global"]["cpu_num_threads"].as<int>(),
RT_Config["Global"]["batch_size"].as<int>()); RT_Config["Global"]["batch_size"].as<int>());
// create rec model // create rec model
...@@ -202,7 +165,6 @@ int main(int argc, char **argv) { ...@@ -202,7 +165,6 @@ int main(int argc, char **argv) {
std::vector<PPShiTu::ObjectResult> det_result; std::vector<PPShiTu::ObjectResult> det_result;
std::vector<cv::Mat> batch_imgs; std::vector<cv::Mat> batch_imgs;
std::vector<std::vector<PPShiTu::RESULT>> rec_results;
double rec_time; double rec_time;
if (!RT_Config["Global"]["infer_imgs"].as<std::string>().empty() || if (!RT_Config["Global"]["infer_imgs"].as<std::string>().empty() ||
!RT_Config["Global"]["infer_imgs_dir"].as<std::string>().empty()) { !RT_Config["Global"]["infer_imgs_dir"].as<std::string>().empty()) {
...@@ -239,7 +201,7 @@ int main(int argc, char **argv) { ...@@ -239,7 +201,7 @@ int main(int argc, char **argv) {
// add the whole image for recognition to improve recall // add the whole image for recognition to improve recall
PPShiTu::ObjectResult result_whole_img = { PPShiTu::ObjectResult result_whole_img = {
{0, 0, srcimg.cols - 1, srcimg.rows - 1}, 0, 1.0}; {0, 0, srcimg.cols, srcimg.rows}, 0, 1.0};
det_result.push_back(result_whole_img); det_result.push_back(result_whole_img);
// get rec result // get rec result
...@@ -250,13 +212,14 @@ int main(int argc, char **argv) { ...@@ -250,13 +212,14 @@ int main(int argc, char **argv) {
cv::Mat crop_img = srcimg(rect); cv::Mat crop_img = srcimg(rect);
std::vector<PPShiTu::RESULT> result = std::vector<PPShiTu::RESULT> result =
rec.RunRecModel(crop_img, rec_time); rec.RunRecModel(crop_img, rec_time);
rec_results.push_back(result); det_result[j].rec_result.assign(result.begin(), result.end());
} }
PrintResult(img_path, det_result, rec_results); // rec nms
PPShiTu::nms(det_result,
RT_Config["Global"]["rec_nms_thresold"].as<float>(), true);
PrintResult(img_path, det_result);
batch_imgs.clear(); batch_imgs.clear();
det_result.clear(); det_result.clear();
rec_results.clear();
} }
} }
return 0; return 0;
......
...@@ -95,7 +95,7 @@ cv::Mat VisualizeResult(const cv::Mat& img, ...@@ -95,7 +95,7 @@ cv::Mat VisualizeResult(const cv::Mat& img,
void ObjectDetector::Preprocess(const cv::Mat& ori_im) { void ObjectDetector::Preprocess(const cv::Mat& ori_im) {
// Clone the image : keep the original mat for postprocess // Clone the image : keep the original mat for postprocess
cv::Mat im = ori_im.clone(); cv::Mat im = ori_im.clone();
cv::cvtColor(im, im, cv::COLOR_BGR2RGB); // cv::cvtColor(im, im, cv::COLOR_BGR2RGB);
preprocessor_.Run(&im, &inputs_); preprocessor_.Run(&im, &inputs_);
} }
...@@ -235,7 +235,7 @@ void ObjectDetector::Predict(const std::vector<cv::Mat>& imgs, ...@@ -235,7 +235,7 @@ void ObjectDetector::Predict(const std::vector<cv::Mat>& imgs,
auto postprocess_start = std::chrono::steady_clock::now(); auto postprocess_start = std::chrono::steady_clock::now();
// Get output tensor // Get output tensor
output_data_list_.clear(); output_data_list_.clear();
int num_class = 80; int num_class = 1;
int reg_max = 7; int reg_max = 7;
auto output_names = predictor_->GetOutputNames(); auto output_names = predictor_->GetOutputNames();
// TODO: Unified model output. // TODO: Unified model output.
...@@ -281,6 +281,7 @@ void ObjectDetector::Predict(const std::vector<cv::Mat>& imgs, ...@@ -281,6 +281,7 @@ void ObjectDetector::Predict(const std::vector<cv::Mat>& imgs,
out_bbox_num_data_.data()); out_bbox_num_data_.data());
} }
// Postprocessing result // Postprocessing result
result->clear(); result->clear();
if (config_.arch_ == "PicoDet") { if (config_.arch_ == "PicoDet") {
PPShiTu::PicoDetPostProcess( PPShiTu::PicoDetPostProcess(
......
...@@ -127,11 +127,11 @@ void TopDownEvalAffine::Run(cv::Mat* im, ImageBlob* data) { ...@@ -127,11 +127,11 @@ void TopDownEvalAffine::Run(cv::Mat* im, ImageBlob* data) {
// Preprocessor op running order // Preprocessor op running order
const std::vector<std::string> Preprocessor::RUN_ORDER = {"InitInfo", const std::vector<std::string> Preprocessor::RUN_ORDER = {"InitInfo",
"TopDownEvalAffine", "DetTopDownEvalAffine",
"Resize", "DetResize",
"NormalizeImage", "DetNormalizeImage",
"PadStride", "DetPadStride",
"Permute"}; "DetPermute"};
void Preprocessor::Run(cv::Mat* im, ImageBlob* data) { void Preprocessor::Run(cv::Mat* im, ImageBlob* data) {
for (const auto& name : RUN_ORDER) { for (const auto& name : RUN_ORDER) {
......
...@@ -38,7 +38,7 @@ std::vector<RESULT> Recognition::RunRecModel(const cv::Mat &img, ...@@ -38,7 +38,7 @@ std::vector<RESULT> Recognition::RunRecModel(const cv::Mat &img,
// Get output and post process // Get output and post process
std::unique_ptr<const Tensor> output_tensor( std::unique_ptr<const Tensor> output_tensor(
std::move(this->predictor->GetOutput(0))); std::move(this->predictor->GetOutput(1)));
auto *output_data = output_tensor->data<float>(); auto *output_data = output_tensor->data<float>();
auto end = std::chrono::system_clock::now(); auto end = std::chrono::system_clock::now();
auto duration = auto duration =
......
...@@ -16,14 +16,23 @@ ...@@ -16,14 +16,23 @@
namespace PPShiTu { namespace PPShiTu {
void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold) { void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold,
std::sort(input_boxes.begin(), bool rec_nms) {
input_boxes.end(), if (!rec_nms) {
[](ObjectResult a, ObjectResult b) { return a.confidence > b.confidence; }); std::sort(input_boxes.begin(), input_boxes.end(),
[](ObjectResult a, ObjectResult b) {
return a.confidence > b.confidence;
});
} else {
std::sort(input_boxes.begin(), input_boxes.end(),
[](ObjectResult a, ObjectResult b) {
return a.rec_result[0].score > b.rec_result[0].score;
});
}
std::vector<float> vArea(input_boxes.size()); std::vector<float> vArea(input_boxes.size());
for (int i = 0; i < int(input_boxes.size()); ++i) { for (int i = 0; i < int(input_boxes.size()); ++i) {
vArea[i] = (input_boxes.at(i).rect[2] - input_boxes.at(i).rect[0] + 1) vArea[i] = (input_boxes.at(i).rect[2] - input_boxes.at(i).rect[0] + 1) *
* (input_boxes.at(i).rect[3] - input_boxes.at(i).rect[1] + 1); (input_boxes.at(i).rect[3] - input_boxes.at(i).rect[1] + 1);
} }
for (int i = 0; i < int(input_boxes.size()); ++i) { for (int i = 0; i < int(input_boxes.size()); ++i) {
for (int j = i + 1; j < int(input_boxes.size());) { for (int j = i + 1; j < int(input_boxes.size());) {
...@@ -36,14 +45,13 @@ void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold) { ...@@ -36,14 +45,13 @@ void nms(std::vector<ObjectResult> &input_boxes, float nms_threshold) {
float inter = w * h; float inter = w * h;
float ovr = inter / (vArea[i] + vArea[j] - inter); float ovr = inter / (vArea[i] + vArea[j] - inter);
if (ovr >= nms_threshold) { if (ovr >= nms_threshold) {
input_boxes.erase(input_boxes.begin() + j); input_boxes.erase(input_boxes.begin() + j);
vArea.erase(vArea.begin() + j); vArea.erase(vArea.begin() + j);
} } else {
else { j++;
j++;
} }
} }
} }
} }
} // namespace PPShiTu } // namespace PPShiTu
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册