首页 PaddleX 帖子详情
目标检测---05---Inference模型预测部署
收藏
快速回复
PaddleX 文章入门 2262 15
目标检测---05---Inference模型预测部署
收藏
快速回复
PaddleX 文章入门 2262 15

1、准备预测模型

这里直接使用paddlex --export_inference 导出的模型。模型文件内容如图

2、导入相关库

from paddle.inference import Config   # AnalysisConfig的相关设置
from paddle.inference import create_predictor  # 创建PaddlePredictor


# 其他的一些辅助库
import cv2
import numpy as np
import yaml
import time
import datetime

3、设置Config,创建predictor

    # 根据预测部署的实际情况,设置Config
    config = Config()
    # 读取模型文件
    config.set_prog_file(model_file)
    config.set_params_file(params_file)
    # Config默认是使用CPU预测,若要使用GPU预测,需要手动开启,设置运行的GPU卡号和分配的初始显存。
    config.enable_use_gpu(300, 0)
    # 可以设置开启IR优化、开启内存优化。
    config.switch_ir_optim()
    config.enable_memory_optim()
    # 由于不使用tensorrt,注释相应配置
    predictor = create_predictor(config)

4、设置输入

从Predictor中获取输入的names和handle,然后设置输入数据。这里Inference官方给的linux例子,不知道什么原因运行不起来,个人觉得可能是官方例子中用的模型可能对输入的要求与我这个训练的ppyolotiny模型不一样。经过半天的调试和各种查资料,ppyolotiny训练的inferenc模型,输入这块需要进行如下的操作。具体看代码

    im_size = 608
    # 被预测图片
    img = cv2.imread('testplane/660.jpg')
    # 对预测图片进行resize和归一化处理。
    data = preprocess(img, im_size)
    # x、y轴缩放比例
    scale_factor = np.array([im_size * 1. / img.shape[0], im_size * 1. / img.shape[1]]).reshape((1, 2)).astype(
        np.float32)
    #  ???
    im_shape = np.array([im_size, im_size]).reshape((1, 2)).astype(np.float32)
    # 这个是predictor.get_input_names()返回的数组,需要将这三个值传进入,才能正常执行predictor.run(),没查到对应的API.....
    ims = [im_shape, data, scale_factor]

    input_names = predictor.get_input_names()
    for i, name in enumerate(input_names):
        input_tensor = predictor.get_input_handle(name)
        input_tensor.reshape(imgs[i].shape)
        input_tensor.copy_from_cpu(imgs[i].copy())

5、执行预测并输出

    # 执行Predictor
    predictor.run()
    # 获取输出
    results = []
    # 获取输出
    output_names = predictor.get_output_names()
    for i, name in enumerate(output_names):
        output_tensor = predictor.get_output_handle(name)
        output_data = output_tensor.copy_to_cpu()
        results.append(output_data)

完整的代码如下:

import numpy as np
import cv2

from paddle.inference import Config
from paddle.inference import create_predictor
import yaml


def resize(img, target_size):
    if not isinstance(img, np.ndarray):
        raise TypeError('image type is not numpy.')
    im_shape = img.shape
    im_scale_x = float(target_size) / float(im_shape[1])
    im_scale_y = float(target_size) / float(im_shape[0])
    img = cv2.resize(img, None, None, fx=im_scale_x, fy=im_scale_y)
    return img


def normalize(img, mean, std):
    img = img / 255.0
    mean = np.array(mean)[np.newaxis, np.newaxis, :]
    std = np.array(std)[np.newaxis, np.newaxis, :]
    img -= mean
    img /= std
    return img


# 图像预处理
def preprocess(img, img_size):
    mean = [0.485, 0.456, 0.406]
    std = [0.229, 0.224, 0.225]
    img = resize(img, img_size)
    img = img[:, :, ::-1].astype('float32')  # bgr -> rgb
    img = normalize(img, mean, std)
    img = img.transpose((2, 0, 1))  # hwc -> chw
    return img[np.newaxis, :]  # ??? 为什么要增加维度?


# ——————————————————————后处理函数—————————————————————————— #
def draw_bbox_image(frame, result, label_list, threshold=0.5):
    for res in result:
        cat_id, score, bbox = res[0], res[1], res[2:]
        if score < threshold:
            continue
        for i in bbox:
            int(i)
        xmin, ymin, xmax, ymax = [int(pts) for pts in bbox]
        cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (255, 0, 255), 2)
        print('category id is {}, bbox is {}'.format(cat_id, bbox))
        try:
            label_id = label_list[int(cat_id)]
            # #cv2.putText(图像, 文字, (x, y), 字体, 大小, (b, g, r), 宽度)
            cv2.putText(frame, label_id, (int(xmin + 50), int(ymin - 2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
            cv2.putText(frame, str(round(score, 2)), (int(xmin), int(ymin - 2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (0, 255, 0), 2)
        except KeyError:
            pass



if __name__ == '__main__':
    # 加载inference模型文件
    config = Config('inference_model/ppyolotiny/model.pdmodel','inference_model/ppyolotiny/model.pdiparams')
    config.enable_use_gpu(500, 0)
    config.switch_ir_optim()
    config.enable_memory_optim()
    # 加载yml文件,获取label_list,用于根据label_id获取label名称
    infer_cfg = open('inference_model/ppyolotiny/model.yml')
    data = infer_cfg.read()
    yaml_reader = yaml.load(data)
    label_list = yaml_reader['_Attributes']["labels"]
    print(label_list)

    # Create predictor
    predictor = create_predictor(config)

    # Set input
    img = cv2.imread('testplane/660.jpg')
    im_size = 608
    data = preprocess(img, im_size)

    scale_factor = np.array([im_size * 1. / img.shape[0], im_size * 1. / img.shape[1]]).reshape((1, 2)).astype(
        np.float32)

    im_shape = np.array([im_size, im_size]).reshape((1, 2)).astype(np.float32)

    ims = [im_shape, data, scale_factor]
    input_names = predictor.get_input_names()
    for i, name in enumerate(input_names):
        input_tensor = predictor.get_input_handle(name)
        input_tensor.reshape(ims[i].shape)
        input_tensor.copy_from_cpu(ims[i].copy())
    # 执行Predictor
    predictor.run()
    # 获取输出
    results = []
    # 获取输出
    output_names = predictor.get_output_names()
    for i, name in enumerate(output_names):
        output_tensor = predictor.get_output_handle(name)
        output_data = output_tensor.copy_to_cpu()
        results.append(output_data)

    draw_bbox_image(img, results[0], label_list, threshold=0.5)
    cv2.imshow("frame", img)
    cv2.waitKey()

总结一下过程中遇到的问题:

1)、在pycharm的工程中引入from paddle.inference import create_predictor,create_predictor会显示红下划线,可以忽略。

2)、如果不显示标签的名称,yml文件实际是可以不引入的。

3)、不知道是不是paddlex的版本问题,如果在config中直接配置model_dir,是不行的。

4)、如果没用安装tensorrt,config中的tensorrt必须注释掉。

5)、设置输入时,建议调试模式看看input_names = predictor.get_input_names()中,input_names到底是什么,我花了半天时间都是在解决这个输入的问题,尽管最终还是没搞清楚,希望有大佬解释一下,或者给个api地址我学习一下,不胜感激。

6)还是关于输入ims = [im_shape, data, scale_factor],这里面的im_shape为什么w和h是一样的?data是预处理后的图片,已经进行了缩放,为什么还要给scale_factor,有什么用?

7)、输出的results中,是多个预测框吗?看调试好像有其他的数据。

希望官方大佬和各位达人给点指导,先感谢了!!!

0
收藏
回复
全部评论(15)
时间顺序
余志良
#2 回复于2021-08

写的很好,感谢贡献

1
回复
y
yuyangup
#3 回复于2021-08
写的很好,感谢贡献

大佬啊,帮忙回复一下最后面的问题啊?求解答

0
回复
飞桨_Paddle3D
#4 回复于2021-08

> 3)不知道是不是paddlex的版本问题,如果在config中直接配置model_dir,是不行的。

A: 这个与paddlex的版本没有关系哈,是推理引擎的接口是这样子设计的

> 5)、设置输入时,建议调试模式看看input_names = predictor.get_input_names()中,input_names到底是什么,我花了半天时间都是在解决这个输入的问题,尽管最终还是没搞清楚,希望有大佬解释一下,或者给个api地址我学习一下,不胜感激。

A: input_names是在test/infer阶段网络所需的输入tensort的名字,这个可以看下导出之前的组网代码 https://github.com/PaddlePaddle/PaddleX/blob/develop/paddlex/cv/models/detector.py#L63-L74

> 6)还是关于输入ims = [im_shape, data, scale_factor],这里面的im_shape为什么w和h是一样的?data是预处理后的图片,已经进行了缩放,为什么还要给scale_factor,有什么用?

im_shape定义的是缩放后的w和h,因为YOLO系列要求输入的w和h是相同大小,所以这里的w和h是一样的

scale_factor用于在模型的后处理阶段将预测结果还原成相对原始图片大小

7)、输出的results中,是多个预测框吗?看调试好像有其他的数据。

返回值可参考导出前的predict API文档说明 https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/models/detection.md#predict 和相应的解析操作 https://github.com/PaddlePaddle/PaddleX/blob/develop/paddlex/cv/models/detector.py#L498 

0
回复
y
yuyangup
#5 回复于2021-08
> 3)不知道是不是paddlex的版本问题,如果在config中直接配置model_dir,是不行的。 A: 这个与paddlex的版本没有关系哈,是推理引擎的接口是这样子设计的 > 5)、设置输入时,建议调试模式看看input_names = predictor.get_input_names()中,input_names到底是什么,我花了半天时间都是在解决这个输入的问题,尽管最终还是没搞清楚,希望有大佬解释一下,或者给个api地址我学习一下,不胜感激。 A: input_names是在test/infer阶段网络所需的输入tensort的名字,这个可以看下导出之前的组网代码 https://github.com/PaddlePaddle/PaddleX/blob/develop/paddlex/cv/models/detector.py#L63-L74 > 6)还是关于输入ims = [im_shape, data, scale_factor],这里面的im_shape为什么w和h是一样的?data是预处理后的图片,已经进行了缩放,为什么还要给scale_factor,有什么用? im_shape定义的是缩放后的w和h,因为YOLO系列要求输入的w和h是相同大小,所以这里的w和h是一样的 scale_factor用于在模型的后处理阶段将预测结果还原成相对原始图片大小 7)、输出的results中,是多个预测框吗?看调试好像有其他的数据。 返回值可参考导出前的predict API文档说明 https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/apis/models/detection.md#predict 和相应的解析操作 https://github.com/PaddlePaddle/PaddleX/blob/develop/paddlex/cv/models/detector.py#L498 
展开

感谢大佬的回复!!!

我记录一下,以防看的时候github不能访问

input_names对应代码:

def _define_input_spec(self, image_shape):
        input_spec = [{
            "image": InputSpec(
                shape=image_shape, name='image', dtype='float32'),
            "im_shape": InputSpec(
                shape=[image_shape[0], 2], name='im_shape', dtype='float32'),
            "scale_factor": InputSpec(
                shape=[image_shape[0], 2],
                name='scale_factor',
                dtype='float32')
        }]
        return input_spec

predict说明:

predict
predict(self, img_file, transforms=None)
PPYOLOv2模型预测接口。需要注意的是,只有在训练过程中定义了eval_dataset,模型在保存时才会将预测时的图像处理流程保存在PPYOLOv2.test_transforms和PPYOLOv2.eval_transforms中。如未在训练时定义eval_dataset,那在调用预测predict接口时,用户需要再重新定义test_transforms传入给predict接口

参数

img_file (List[np.ndarray or str], str or np.ndarray): 预测图像或包含多张预测图像的列表,预测图像可以是路径或numpy数组(HWC排列,BGR格式)。
transforms (paddlex.transforms): 数据预处理操作。
返回值

list: 预测结果列表。如果输入为单张图像,列表中每个元素均为一个dict,键值包括'bbox', 'category', 'category_id', 'score',分别表示每个预测目标的框坐标信息、类别、类别id、置信度,其中框坐标信息为[xmin, ymin, w, h],即左上角x, y坐标和框的宽和高。如果输入为多张图像,如果输入为多张图像,返回由每张图像预测结果组成的列表。

predict结果后处理函数

def _postprocess(self, batch_pred):
        infer_result = {}
        if 'bbox' in batch_pred:
            bboxes = batch_pred['bbox']
            bbox_nums = batch_pred['bbox_num']
            det_res = []
            k = 0
            for i in range(len(bbox_nums)):
                det_nums = bbox_nums[i]
                for j in range(det_nums):
                    dt = bboxes[k]
                    k = k + 1
                    num_id, score, xmin, ymin, xmax, ymax = dt.tolist()
                    if int(num_id) < 0:
                        continue
                    category = self.labels[int(num_id)]
                    w = xmax - xmin
                    h = ymax - ymin
                    bbox = [xmin, ymin, w, h]
                    dt_res = {
                        'category_id': int(num_id),
                        'category': category,
                        'bbox': bbox,
                        'score': score
                    }
                    det_res.append(dt_res)
            infer_result['bbox'] = det_res

        if 'mask' in batch_pred:
            masks = batch_pred['mask']
            bboxes = batch_pred['bbox']
            mask_nums = batch_pred['bbox_num']
            seg_res = []
            k = 0
            for i in range(len(mask_nums)):
                det_nums = mask_nums[i]
                for j in range(det_nums):
                    mask = masks[k].astype(np.uint8)
                    score = float(bboxes[k][1])
                    label = int(bboxes[k][0])
                    k = k + 1
                    if label == -1:
                        continue
                    category = self.labels[int(label)]
                    import pycocotools.mask as mask_util
                    rle = mask_util.encode(
                        np.array(
                            mask[:, :, None], order="F", dtype="uint8"))[0]
                    if six.PY3:
                        if 'counts' in rle:
                            rle['counts'] = rle['counts'].decode("utf8")
                    sg_res = {
                        'category_id': int(label),
                        'category': category,
                        'mask': rle,
                        'score': score
                    }
                    seg_res.append(sg_res)
            infer_result['mask'] = seg_res

        bbox_num = batch_pred['bbox_num']
        results = []
        start = 0
        for num in bbox_num:
            end = start + num
            curr_res = infer_result['bbox'][start:end]
            if 'mask' in infer_result:
                mask_res = infer_result['mask'][start:end]
                for box, mask in zip(curr_res, mask_res):
                    box.update(mask)
            results.append(curr_res)
            start = end

        return results

 

0
回复
飞桨_Paddle3D
#6 回复于2021-08

嗯嗯,python deploy我们正在开发适配中

0
回复
A
AIStudio791351
#8 回复于2021-09

请教一下,这步部署好之后,下一步要怎么引用呢,训练好的检测和识别模型是不是需要分别部署呢?

0
回复
y
yuyangup
#9 回复于2021-09
请教一下,这步部署好之后,下一步要怎么引用呢,训练好的检测和识别模型是不是需要分别部署呢?

不太明白你的意思,部署好了,就是直接使用了,就是你如何给输入和处理输出的问题了,另外关于识别和检测模型是否要分别部署,这个我也搞不懂,具体你可以看看paddlex里的工业仪表识别示例,它是采用两个模型串联部署的

0
回复
JavaRoom
#10 回复于2021-09

输出的results中,是多个预测框吗?看调试好像有其他的数据。

这个应该是置信度问题,弄的低,就识别多。。。。框多。。。。。。。。。。。。

0
回复
y
yuyangup
#11 回复于2021-09
JavaRoom #10
输出的results中,是多个预测框吗?看调试好像有其他的数据。 这个应该是置信度问题,弄的低,就识别多。。。。框多。。。。。。。。。。。。

你知道有什么方法让这个框少一点吗?除了使用调高置信度的方法,因为没必要在一些极低的框上浪费计算时间,有人说要调训练时候的nms中的什么参数

0
回复
糖果屋的奇诺
#12 回复于2021-10

我也是这个卡了好几天,打印了get信息知道输入有这三个元素,但是苦于压根没有输入的格式说明,害得我又用paddledetertion又训练了一个模型,部署还是同样的问题。十分感谢!

1
回复
李长安
#13 回复于2021-12

优秀

0
回复
DeepGeGe
#14 回复于2021-12

这种部署主要是预处理和后处理很麻烦,中间的模型推理到时是一个固定的范式。

0
回复
被风吹过的田地
#15 回复于2022-01

楼主的第四步在哪里查阅的资料?就是4、设置输入这个处理。可否给个参考,使用Faster-Rcnn模型时输入还是有问题,报错。

0
回复
y
yuyangup
#16 回复于2022-01
楼主的第四步在哪里查阅的资料?就是4、设置输入这个处理。可否给个参考,使用Faster-Rcnn模型时输入还是有问题,报错。

第四步是针对yolo系列的,fasterrcnn的查看paddle官方的inference开源项目源码

0
回复
汐落夜微凉
#18 回复于2022-04

你好,为什么我直接使用paddlex --export_inference 导出的模型内并没有model.pdmodel和model.pdiparams文件,仅仅只有__model__、__params__和model.yml三个文件,还望答疑

0
回复
需求/bug反馈?一键提issue告诉我们
发现bug?如果您知道修复办法,欢迎提pr直接参与建设飞桨~
在@后输入用户全名并按空格结束,可艾特全站任一用户