# 图像分割

本示例主要通过deeplabv3模型说明如何使用MIGraphX Python API进行图像分割模型的推理，包括预处理、模型推理。

## 模型简介

本示例采用了经典的deeplabv3模型进行图像分割，将deeplabv3_resnet101.onnx文件保存在Resource/Models文件夹下。模型结构如下图所示, 链接：https://netron.app/, 该模型的输入shape为[batch_size,3,513,513]，输出shape为[batch_size,1,513,513]，数据排布为NCHW。

<img src="./Images/deeplabv3_01.jpg" style="zoom:80%;" align=middle>

## 预处理

在将数据输入到模型之前，需要对图像做如下预处理操作：

​      1.尺度变换，将图像resize到513x513大小

​      2.数据排布，将数据从HWC转换为CHW，再将维度转换为NCHW

​      3.归一化，将数据归一化到[0.0, 1.0]之间

本示例代码通过如下方式实现预处理操作：

```python
def Preprocessing(pil_img, newW, newH):
    assert newW > 0 and newH > 0, 'Scale is too small'  
    img_nd = cv2.cvtColor(pil_img, cv2.COLOR_BGR2RGB)    # BGR转换为RGB
    img_nd = cv2.resize(img_nd, (newW, newH))            # 将图像尺寸修改为256x256
    
    if len(img_nd.shape) == 2:
        img_nd = np.expand_dims(img_nd, axis=2)

    img_trans = img_nd.transpose((2, 0, 1))              # HWC转换为CHW
    img_trans = np.expand_dims(img_trans, 0)             # CHW扩展为NCHW
    img_trans = np.ascontiguousarray(img_trans)          # 保证内存连续存储
    img_trans = img_trans.astype(np.float32)             # 转换成浮点型数据
    if img_trans.max() > 1:                             
        img = img_trans / 255.0                          # 保证数据处于0-1之间的浮点数
    return img
```

## 推理

完成图像预处理后，就可以执行推理，得到推理结果。

```python
    # 加载模型
    model = migraphx.parse_onnx("../Resource/Models/deeplabv3_resnet101.onnx", map_input_dims=maxInput)

    # 获取模型输入/输出节点信息
    inputs = model.get_inputs()
    outputs = model.get_outputs()

    # 编译模型
    model.compile(migraphx.get_target("gpu"), device_id=0)      # device_id: 设置GPU设备，默认为0号设备

    # 图像预处理
    img = cv2.imread("../Resource/Images/000001.jpg")
    input_img = Preprocessing(img, 513, 513)

    print(inputs)
    # 模型推理
    mask = model.run({'images':input_img})      
    result = mask[0]                                            # 得到第一个输出节点的结果
    # 对通道维度进行softmax
    softmax_result = softmax(result)                            
    # 计算通道维度最大值对应的索引（即类别索引）      
    max_indices = np.argmax(softmax_result, axis=1)             # 等价于 np.argmax(arr, axis=1, keepdims=False)
    # 使用预设颜色
    color_map = np.array([
        [0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],       # 0-3类
        [255, 255, 0], [255, 0, 255], [0, 255, 255], [128, 0, 0],  # 4-7类
        [0, 128, 0], [0, 0, 128], [128, 128, 0], [128, 0, 128],  # 8-11类
        [0, 128, 128], [192, 192, 192], [128, 128, 128], [64, 0, 0],  # 12-15类
        [0, 64, 0], [0, 0, 64], [64, 64, 0], [64, 0, 64],       # 16-19类
        [0, 64, 64]                                             # 20类
    ], dtype=np.uint8)

    flat_index = max_indices[0]                                 # 取第0批的数据 
    # 将二维的类别索引图直接转换为三维的 RGB 彩色图像
    rgb_image = color_map[flat_index]
    cv2.imwrite("Result.jpg", rgb_image)                         # 保存图像分割结果

```

1.Preprocessing函数返回预处理后的数据（numpy类型），然后通过model.run({'images':input_img})得到推理结果，因为只有输入一张图片，所以通过mask[0]获取第一个输出节点的数据即可。

2.模型得到的推理结果并不能直接作为分割结果。首先，需要计算softmax值，计算不同通道同一[H,W]位置的softmax值，找出概率最高的通道。其次，根据不同的通道索引转换为三维的 RGB 彩色图像。最终，保存结果得到分割图像。

注：本次采用的模型权重onnx文件是通过使用PASCAL VOC 2012数据集来训练的。因此，“现实世界“图像的分割结果不完美是意料之中的。为了获得更好的结果，建议对现实世界示例数据集上的模型进行微调。

