Erlo

基于U-Net网络的图像分割的MindStudio实践

2022-12-26 17:00:08 发布   86 浏览  
页面报错/反馈
收藏 点赞
摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的。

本文分享自华为云社区《【MindStudio训练营第一季】基于U-Net网络的图像分割的MindStudio实践》,作者:Tianyi_Li 。

1.U-Net网络介绍:

U-Net模型基于二维图像分割。在2015年ISBI细胞跟踪竞赛中,U-Net获得了许多最佳奖项。论文中提出了一种用于医学图像分割的网络模型和数据增强方法,有效利用标注数据来解决医学领域标注数据不足的问题。U型网络结构也用于提取上下文和位置信息。

[U-Net 论文]: Olaf Ronneberger, Philipp Fischer, Thomas Brox. “U-Net: Convolutional Networks for Biomedical Image Segmentation.” conditionally accepted at MICCAI 2015. 2015.

2.ECS运行说明

我们的操作基本都在root用户下执行。

首先,修改bash,具体命令和结果如下。

本项目支持MindStudio运行和终端运行。

(1)下载项目代码

下载链接:https://alexed.obs.cn-north-4.myhuaweicloud.com/unet_sdk.zip

将项目文件unet_sdk.zip上传至华为云ECS弹性云服务器/root/目录下,并解压;或者下载到本地电脑,用MindStudio打开。

将之前unet_hw960_bs1.air模型放到/unet_sdk/model/目录下。

项目文件结构

├── unet_sdk
    ├── README.md
├── data                          //数据集
│    ├── 1
 │   │   ├──image.png          //图片
 │   │   ├──mask.png          //标签
│   ...
    ├── model
    │   ├──air2om.sh                     // air模型转om脚本
    │   ├──xxx.air //air模型
    │   ├──xxx.om                       //om模型
    │   ├──aipp_unet_simple_opencv.cfg // aipp文件
    ├── pipeline         
    │   ├──unet_simple_opencv.pipeline // pipeline文件
    ├── main.py                       // 推理文件 
    ├── run.sh                        // 执行文件
    ├── requirements.txt                // 需要的三方库

(2) 模型转换

将unet_hw960_bs1.air模型转为昇腾AI处理器支持的.om格式离线模型,此处模型转换需要用到ATC工具。
昇腾张量编译器(Ascend Tensor Compiler,简称ATC)是昇腾CANN架构体系下的模型转换工具,它可以将开源框架的网络模型或Ascend IR定义的单算子描述文件(json格式)转换为昇腾AI处理器支持的.om格式离线模型。模型转换过程中可以实现算子调度的优化、权值数据重排、内存使用优化等,可以脱离设备完成模型的预处理。

ATC参数概览

(3)运行脚本

运行脚本:

cd unet_sdk/model/   # 切换至模型存储目录
atc --framework=1 --model=unet_hw960_bs1.air --output=unet_hw960_bs1 --input_format=NCHW --soc_version=Ascend310 --log=error --insert_op_conf=aipp_unet_simple_opencv.cfg
  • 注意air模型转om只支持静态batch,这里batchsize=1。

参数说明:

framework:原始框架类型。
model:原始模型文件路径与文件名。
output:转换后的离线模型的路径以及文件名。
input_format:输入数据格式。
soc_version:模型转换时指定芯片版本。
log:显示日志的级别。
insert_op_conf:插入算子的配置文件路径与文件名,这里使用AIPP预处理配置文件,用于图像数据预处理。

输出结果:

ATC run success,表示模型转换成功,得到unet_hw960_bs1.om模型。

模型转换成功之后,可以使用MindX SDK mxVision运行脚本,在Ascend 310上进行推理。

(4) MindX SDK mxVision 执行推理

MindX SDK文档请参考:https://support.huaweicloud.com/ug-vis-mindxsdk203/atlasmx_02_0051.html

MindX SDK执行推理的业务流程:

通过stream配置文件,Stream manager可识别需要构建的element以及element之间的连接关系,并启动业务流程。Stream manager对外提供接口,用于向stream发送数据和获取结果,帮助用户实现业务对接。

plugin表示业务流程中的基础模块,通过element的串接构建成一个stream。buffer用于内部挂载解码前后的视频、图像数据,是element之间传递的数据结构,同时也允许用户挂载元数据(Metadata),用于存放结构化数据(如目标检测结果)或过程数据(如缩放后的图像)。

MindX SDK基础概念介绍:

MindX SDK基础插件:

MindX SDK业务流程编排:

Stream配置文件以json格式编写,用户必须指定业务流名称、元件名称和插件名称,并根据需要,补充元件属性和下游元件名称信息。

以下表格为本实验pipeline/unet_simple_opencv.pipeline文件及其对应的名称及描述:

pipeline/unet_simple_opencv.pipeline文件内容如下,可根据实际开发情况进行修改。

{
 "unet_mindspore": { 
 "stream_config": {
 "deviceId": "0"
 },
 "appsrc0": {
 "props": {
 "blocksize": "4096000"
 },
 "factory": "appsrc",
 "next": "mxpi_imagedecoder0"
 },
 "mxpi_imagedecoder0": {
 "props": {
 "cvProcessor": "opencv",
 "outputDataFormat": "BGR"
 },
 "factory": "mxpi_imagedecoder",
 "next": "mxpi_imagecrop0"
 },
 "mxpi_imagecrop0": {
 "props": {
 "cvProcessor": "opencv",
 "dataSource": "ExternalObjects"
 },
 "factory": "mxpi_imagecrop",
 "next": "mxpi_imageresize0"
 },
 "mxpi_imageresize0": {
 "props": {
 "handleMethod": "opencv",
 "resizeType": "Resizer_Stretch",
 "resizeHeight": "960",
 "resizeWidth": "960"
 },
 "factory": "mxpi_imageresize",
 "next": "mxpi_tensorinfer0"
 },
 "mxpi_tensorinfer0": {
 "props": {
 "dataSource": "mxpi_imageresize0",
 "modelPath": "model/unet_hw960_bs1_AIPP.om"
 },
 "factory": "mxpi_tensorinfer",
 "next": "mxpi_dumpdata0"
 },
 "mxpi_dumpdata0": {
 "props": {
 "requiredMetaDataKeys": "mxpi_tensorinfer0"
 },
 "factory": "mxpi_dumpdata",
 "next": "appsink0"
 },
 "appsink0": {
 "props": {
 "blocksize": "4096000"
 },
 "factory": "appsink"
 }
 }
}

(5) 修改modelPath

打开pipeline/unet_simple_opencv.pipeline文件,将"mxpi_tensorinfer0"元件的属性"modelPath"(模型导入路径)修改为模型转换后保存的om模型"model/unet_hw960_bs1.om"。

修改结果:

"modelPath": "model/unet_hw960_bs1.om"

modelPath修改完成之后,保存pipeline/unet_simple_opencv.pipeline文件。

StreamManagerApi:StreamManagerApi文档请参考:
https://support.huaweicloud.com/ug-vis-mindxsdk203/atlasmx_02_0320.html
StreamManagerApi用于对Stream流程的基本管理:加载流程配置、创建流程、向流程发送数据、获得执行结果、销毁流程。

这里用到的StreamManagerApi有:

  • InitManager:初始化一个StreamManagerApi。
  • CreateMultipleStreams:根据指定的配置创建多个Stream。
  • SendData:向指定Stream上的输入元件发送数据(appsrc)。
  • GetResult:获得Stream上的输出元件的结果(appsink)
  • DestroyAllStreams:销毁所有的流数据。

main.py文件内容如下,可根据实际开发情况进行修改。

import argparse
import base64
import json
import os
import cv2
import numpy as np
from StreamManagerApi import *
import MxpiDataType_pb2 as MxpiDataType
x0 = 2200  # w:2200~4000; h:1000~2800
y0 = 1000
x1 = 4000
y1 = 2800
ori_w = x1 - x0
ori_h = y1 - y0
def _parse_arg():
    parser = argparse.ArgumentParser(description="SDK infer")
 parser.add_argument("-d", "--dataset", type=str, default="data/",
                        help="Specify the directory of dataset")
 parser.add_argument("-p", "--pipeline", type=str,
 default="pipeline/unet_simple_opencv.pipeline",
                        help="Specify the path of pipeline file")
 return parser.parse_args()
def _get_dataset(dataset_dir):
 img_ids = sorted(next(os.walk(dataset_dir))[1])
 for img_id in img_ids:
 img_path = os.path.join(dataset_dir, img_id)
 yield img_path
def _process_mask(mask_path):
    # 手动裁剪
    mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)[y0:y1, x0:x1]
 return mask
def _get_stream_manager(pipeline_path):
 stream_mgr_api = StreamManagerApi()
    ret = stream_mgr_api.InitManager()  #初始化stream
 if ret != 0:
 print(f"Failed to init Stream manager, ret={ret}")
 exit(1)
 with open(pipeline_path, 'rb') as f:
 pipeline_content = f.read()
    ret = stream_mgr_api.CreateMultipleStreams(pipeline_content)  # 创建stream
 if ret != 0:
 print(f"Failed to create stream, ret={ret}")
 exit(1)
 return stream_mgr_api
def _do_infer_image(stream_mgr_api, image_path):
 stream_name = b'unet_mindspore'  # 与pipeline中stream name一致
 data_input = MxDataInput()
 with open(image_path, 'rb') as f:
 data_input.data = f.read()
    # 插入抠图的功能,扣1800*1800大小
 roiVector = RoiBoxVector()
 roi = RoiBox()
    roi.x0 = x0
 roi.y0 = y0
    roi.x1 = x1
 roi.y1 = y1
 roiVector.push_back(roi)
 data_input.roiBoxs = roiVector
 unique_id = stream_mgr_api.SendData(stream_name, 0, data_input)  # 向指定Stream上的输入元件发送数据(appsrc)
 if unique_id  0.5).astype(np.int)
 mask_image = (np.arange(2) == mask_image[..., None]).astype(np.int)
 infer_image = np.squeeze(infer_image, axis=0)
    inter = np.dot(infer_image.flatten(), mask_image.flatten())
    union = np.dot(infer_image.flatten(), infer_image.flatten()) + 
        np.dot(mask_image.flatten(), mask_image.flatten())
 single_dice = 2 * float(inter) / float(union + 1e-6)
 single_iou = single_dice / (2 - single_dice)
 return single_dice, single_iou
def main(_args):
 dice_sum = 0.0
 iou_sum = 0.0
 cnt = 0
 stream_mgr_api = _get_stream_manager(_args.pipeline)
 for image_path in _get_dataset(_args.dataset):
 infer_image = _do_infer_image(stream_mgr_api, os.path.join(image_path, 'image.png'))  # 抠图并且reshape后的shape,1hw
 mask_image = _process_mask(os.path.join(image_path, 'mask.png'))  # 抠图后的shape, hw
        dice, iou = _calculate_accuracy(infer_image, mask_image)
 dice_sum += dice
 iou_sum += iou
 cnt += 1
 print(f"image: {image_path}, dice: {dice}, iou: {iou}")
 print(f"========== Cross Valid dice coeff is: {dice_sum / cnt}")
 print(f"========== Cross Valid IOU is: {iou_sum / cnt}")
 stream_mgr_api.DestroyAllStreams()  # 销毁stream
if __name__ == "__main__":
 args = _parse_arg()
 main(args)

run.sh文件内容如下,可根据实际开发情况进行修改。参考SDK软件包sample脚本,需要按照实际路径修改各个环境变量路径。

set -e
CUR_PATH=$(cd "$(dirname "$0")" || { warn "Failed to check path/to/run.sh" ; exit ; } ; pwd)
# Simple log helper functions
info() { echo -e "
登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认