From 51f8511726e2086f8e31ea4f6baf277686a552cb Mon Sep 17 00:00:00 2001 From: Malanchi Date: Wed, 11 Dec 2024 17:01:37 +0800 Subject: [PATCH 1/4] =?UTF-8?q?MeterReader=E9=80=82=E9=85=8D310P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contrib/MeterReader/README.md | 279 ++++--------- .../evaluate/deeplabv3_val/seg_evaluate.py | 151 ------- .../MeterReader/evaluate/yolov5_val/det.py | 370 ------------------ .../MeterReader/evaluate/yolov5_val/match.py | 110 ------ .../evaluate/yolov5_val/yolo2voc.py | 73 ---- 5 files changed, 74 insertions(+), 909 deletions(-) delete mode 100644 contrib/MeterReader/evaluate/deeplabv3_val/seg_evaluate.py delete mode 100644 contrib/MeterReader/evaluate/yolov5_val/det.py delete mode 100644 contrib/MeterReader/evaluate/yolov5_val/match.py delete mode 100644 contrib/MeterReader/evaluate/yolov5_val/yolo2voc.py diff --git a/contrib/MeterReader/README.md b/contrib/MeterReader/README.md index aaac72278..d5cec12ac 100644 --- a/contrib/MeterReader/README.md +++ b/contrib/MeterReader/README.md @@ -1,55 +1,45 @@ # 工业指针型表计读数项-MeterReader ## 1 介绍 +### 1.1 简介 -在本系统中,目的是基于MindX SDK,在华为云昇腾平台上,开发端到端工业指针型表计读数的参考设计,实现对传统机械式指针表计的检测与自动读数功能,达到功能要求。 +在电力能源厂区需要定期监测表计读数,以保证设备正常运行及厂区安全。但厂区分布分散,人工巡检耗时长,无法实时监测表计,且部分工作环境危险导致人工巡检无法触达。针对上述问题,希望通过摄像头拍照后利用计算机智能读数的方式高效地完成此任务。 -### 1.1 支持的产品 +在本系统中,目的是基于MindX SDK,在华为云昇腾平台上,开发端到端工业指针型表计读数的参考设计,实现对传统机械式指针表计的检测与自动读数功能,达到功能要求。 -昇腾 310(推理) +注意事项: +1. 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 -### 1.2 支持的版本 +2. 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 -CANN:7.0.RC1 +3. 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 -SDK:mxVision 5.0.RC3(可通过cat SDK目录下的 version.info 查看) +### 1.2 支持的产品 -MindX SDK 安装前准备可参考[《用户指南》安装教程](https://gitee.com/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/quick_start/1-1%E5%AE%89%E8%A3%85SDK%E5%BC%80%E5%8F%91%E5%A5%97%E4%BB%B6.md) +本项目支持昇腾Atlas 300I pro、 Atlas 300V pro。 +### 1.3 支持的版本 -### 1.3 软件方案介绍 +本样例配套的MxVision版本、CANN版本、Driver/Firmware版本如下所示: -本系统识别的流程是:先将输入的图像送入流中解码和缩放大小,使用YOLOv5目标检测模型去检测图片中的表盘,结束流。将目标框裁剪下来,再送入流中解码和缩放大小,用DeepLabv3语义分割模型去得到工业表中的指针和刻度,对语义分割模型预测的结果进行读数后处理,找到指针指向的刻度,根据刻度的间隔和刻度根数计算表盘的读数。 +| MxVision版本 | CANN版本 | Driver/Firmware版本 | +| --------- | ------------------ | -------------- | +| 6.0.RC3 | 8.0.RC3 | 24.1.RC3 | -表2.1 系统方案中各模块功能: - -| 序号 | 子系统 | 功能描述 | -| :------------ | :---------- | :---------- | -| 1 | 图像输入 | 调用MindX SDK的appsrc输入图片| -| 2 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| -| 3 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到1024*576大小 | -| 4 | 工业表检测 | 调用MindX SDK的mxpi_tensorinfer,使用YOLOv5的检测模型,检测出图片中车辆| -| 5 | 保存工业表的图像 | 将YOLOv5检测到的工业表结果保存图片| -| 6 | 图像输入| 调用MindX SDK的appsrc输入检测到的工业表 | -| 7 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| -| 8 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到512*512大小 -| 9 | 指针刻度检测 | 调用MindX SDK的mxpi_tensorinfer,使用DeepLabv3语义分割模型,检测图像中的指针与刻度| -| 10 | 模型后处理 | 调用MindX mxpi_semanticsegpostprocessor,得到语义分割的结果| -| 11 | 读数后处理 | 开发mxpi_process3插件,读出工业表的数字| +### 1.4 三方依赖 +环境依赖软件和版本如下表: +| 软件名称 | 版本 | +| :-----------: |:---------:| +| Python | 3.9.2 | +| numpy | 1.24.0 | +| opencv-python | 4.10.0.84 | -### 1.4 代码目录结构与说明 +### 1.5 代码目录结构与说明 本工程名称为工业指针型表计读数,工程目录如下图所示: ``` ├── build.sh ├── README.md -├── evaluate - ├── deeplabv3_val #deeplabv3模型测试精度 - ├── seg_evaluate.py - ├── yolov5_val #yolov5模型测试精度 - ├── det.py #1.使用om模型检测测试数据,将得到的结果保存成yolo格式的txt文件 - ├── match.py #3.检测是否有的图像没有目标 - ├── yolo2voc.py #2.将得到的检测结果yolo数据格式转换成voc格式 ├── images ├── README_img ├── DeepLabv3_pipeline.png @@ -82,8 +72,28 @@ MindX SDK 安装前准备可参考[《用户指南》安装教程](https://gitee ├── postprocess.cpp ├── postprocess.h ``` +### 1.6 软件方案介绍 + +本系统识别的流程是:先将输入的图像送入流中解码和缩放大小,使用YOLOv5目标检测模型去检测图片中的表盘,结束流。将目标框裁剪下来,再送入流中解码和缩放大小,用DeepLabv3语义分割模型去得到工业表中的指针和刻度,对语义分割模型预测的结果进行读数后处理,找到指针指向的刻度,根据刻度的间隔和刻度根数计算表盘的读数。 + +表2.1 系统方案中各模块功能: + +| 序号 | 子系统 | 功能描述 | +| :------------ | :---------- | :---------- | +| 1 | 图像输入 | 调用MindX SDK的appsrc输入图片| +| 2 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| +| 3 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到1024*576大小 | +| 4 | 工业表检测 | 调用MindX SDK的mxpi_tensorinfer,使用YOLOv5的检测模型,检测出图片中车辆| +| 5 | 保存工业表的图像 | 将YOLOv5检测到的工业表结果保存图片| +| 6 | 图像输入| 调用MindX SDK的appsrc输入检测到的工业表 | +| 7 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| +| 8 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到512*512大小 +| 9 | 指针刻度检测 | 调用MindX SDK的mxpi_tensorinfer,使用DeepLabv3语义分割模型,检测图像中的指针与刻度| +| 10 | 模型后处理 | 调用MindX mxpi_semanticsegpostprocessor,得到语义分割的结果| +| 11 | 读数后处理 | 开发mxpi_process3插件,读出工业表的数字| + -### 1.5 技术实现流程图 +### 1.6 技术实现流程图
  1. 基础环境:Ascend 310、mxVision、Ascend-CANN-toolkit、Ascend Driver
  2. 模型转换: @@ -120,46 +130,21 @@ onnx模型转昇腾离线模型:DeepLabv3.onnx --> DeepLabv3.om
-### 1.6 特性及适用场景 - -在电力能源厂区需要定期监测表计读数,以保证设备正常运行及厂区安全。但厂区分布分散,人工巡检耗时长,无法实时监测表计,且部分工作环境危险导致人工巡检无法触达。针对上述问题,希望通过摄像头拍照后利用计算机智能读数的方式高效地完成此任务。 - -注意事项: -1. 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 - -2. 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 - -3. 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 - - -## 2 环境依赖 - -### 2.1 环境依赖软件和版本 -环境依赖软件和版本如下表: - -| 软件名称 | 版本 | -| :-----------: | :---------: | -| ubuntu | 18.04.1 LTS | -| MindX SDK | 5.0.RC3 | -| Python | 3.9.2 | -| CANN | 7.0.RC1 | -| numpy | 1.23.4 | -| opencv-python | 4.6.0 | - -MindX SDK开发套件部分可参考[MindX SDK开发套件安装指导](https://gitee.com/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/quick_start/1-1%E5%AE%89%E8%A3%85SDK%E5%BC%80%E5%8F%91%E5%A5%97%E4%BB%B6.md) - -### 2.2 导入基础环境 - +## 2 设置环境变量 ```bash -. /usr/local/Ascend/ascend-toolkit/set_env.sh -. ${SDK安装路径}/mxVision/set_env.sh +#设置CANN环境变量,ascend-toolkit-path为cann安装路径 +. ${ascend-toolkit-path}/set_env.sh + +#设置MindX SDK 环境变量,SDK-path为mxVision SDK 安装路径 +. ${SDK-path}/set_env.sh ``` -## 3 模型转换及依赖安装 + +## 3 准备模型 ### 3.1 模型转换 -使用模型转换工具 ATC 将 onnx 模型转换为 om 模型,模型转换工具相关介绍参考链接:[CANN 社区版](https://gitee.com/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99.md) 。 +**步骤1:** 模型下载 下载[onnx模型压缩包](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/MeterReader/models.zip)并解压 * 将压缩包中的"det.onnx"模型拷贝至"\${MeterReader代码根目录}/models/yolov5"目录下 @@ -173,12 +158,16 @@ MindX SDK开发套件部分可参考[MindX SDK开发套件安装指导](https:// -**YOLOv5模型转换** +**步骤2:**: YOLOv5模型转换 + +(1) 执行命令: 进入"\${MeterReader代码根目录}/models/yolov5"目录,执行以下命令将"det.onnx"模型转换成"det.om"模型: ```bash -atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg --soc_version=Ascend310 +atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg --soc_version=Ascend310P3 ``` +(2) 查看结果: + 出现以下语句表示命令执行成功,会在当前目录中得到"det.om"模型文件。 ``` @@ -188,14 +177,16 @@ atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg - -**DeepLabv3模型转换** +**步骤3:** DeepLabv3模型转换 -**(1)步骤一** +(1) 安装paddle2onnx 使用以下命令安装paddle2onnx依赖,[paddle2onnx安装参考链接](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/docs/zh/compile.md): ```bash pip3 install paddle2onnx ``` +(2) paddle模型转onnx模型 + 确保已下载[pdmodel模型压缩包](https://bj.bcebos.com/paddlex/examples2/meter_reader//meter_seg_model.tar.gz),将目录"meter_seg_model"中的文件解压至"${MeterReader代码根目录}/models/deeplabv3"目录下,进入"deeplabv3"目录,使用以下命令将"pdmodel"模型转换成"onnx"模型,[paddle2onnx模型转换参考链接](https://github.com/PaddlePaddle/Paddle2ONNX/): ```bash cd ${MeterReader代码根目录}/models/deeplabv3 @@ -206,14 +197,16 @@ pip3 install paddle2onnx --enable_dev_version True ``` -**(2)步骤二** +(3) onnx模型转om模型 进入"\${MeterReader代码根目录}/models/deeplabv3"目录,执行以下命令将"seg.onnx"模型转换成"seg.om"模型 ```bash cd ${MeterReader代码根目录}/models/deeplabv3 - atc --model=seg.onnx --framework=5 --output=seg --insert_op_conf=seg_aipp.cfg --input_shape="image:1,3,512,512" --input_format=NCHW --soc_version=Ascend310 + atc --model=seg.onnx --framework=5 --output=seg --insert_op_conf=seg_aipp.cfg --input_shape="image:1,3,512,512" --input_format=NCHW --soc_version=Ascend310P3 ``` +(4) 查看结果 + 出现以下语句表示命令执行成功,会在当前目录中得到seg.om模型文件。 ``` ATC start working now, please wait for a moment. @@ -223,17 +216,15 @@ pip3 install paddle2onnx ## 4 编译与运行 -示例步骤如下: - -**步骤1** 执行编译 +**步骤1:** 编译插件 -编译插件,在项目目录下执行如下命令 +在项目目录下执行如下命令 ```bash cd ${MeterReader代码根目录}/plugins/process3 . build.sh ``` -**步骤2** 修改pipeline文件中的参数地址 +**步骤2:** 修改pipeline文件中的参数地址 * 修改"${MeterReader代码根目录}/pipeline/yolov5/det.pipeline"第40行处文件的绝对路径,将pipeline中所需要用到的模型路径改为存放模型的绝对路径地址: ```python 40 "modelPath":"${MeterReader代码根目录}/models/yolov5/det.om" @@ -246,136 +237,14 @@ cd ${MeterReader代码根目录}/plugins/process3 39 "labelPath":"${MeterReader代码根目录}/pipeline/deeplabv3/deeplabv3.names", ``` -**步骤3** 运行及输出结果 +**步骤3:** 运行 -总体运行。输入带有预测表盘的jpg图片,在指定输出目录下输出得到带有预测表盘计数的png图片。 +输入带有预测表盘的jpg图片,在指定输出目录下输出得到带有预测表盘计数的png图片。 ```bash cd ${MeterReader代码根目录}/infer -python main.py --ifile ${输入图片路径} --odir ${输出图片目录} +python3 main.py --ifile ${输入图片路径} --odir ${输出图片目录} ``` -执行结束后,可在命令行内得到yolo模型得到的表盘文件路径,以及通过后续模型得到的预测表盘度数。并可在设定的${输出图片路径}中查看带有预测表盘计数的图片结果。最后展示的结果图片上用矩形框框出了图片中的表计并且标出了预测的表盘读数。 - - -**步骤4** 精度测试 - -分别对yolo模型与deeplabv3模型进行精度测试。 - -1、YOLOv5模型精度测试 - -步骤一:执行以下命令创建所需要的文件目录 -```bash -cd ${MeterReader代码根目录}/evaluate/yolov5_val/ -mkdir -p det_val_data/det_val_voc -mkdir -p det_val_data/meter_det -mkdir -p det_val_data/det_val_img -mkdir -p det_val_data/det_sdk_txt -mkdir -p det_val_data/det_sdk_voc -``` - -步骤二:准备标签文件及推理图片 - -下载[YOLOv5表计检测数据集](https://bj.bcebos.com/paddlex/examples/meter_reader/datasets/meter_det.tar.gz)并解压到任意目录后,将数据集目录中"test"和"train"目录下的所有图片汇总拷贝至"${MeterReader代码根目录}/evaluate/yolov5_val/det_val_data/meter_det"目录下。 - -我们提供了样例的[模型验证集标签文件](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/MeterReader/data.zip)以供下载测试。 - -完成下载并解压后,将"data/yolov5/det_val_voc"目录下的文件拷贝至"${MeterReader代码根目录}/evaluate/yolov5_val/det_val_data/det_val_voc"目录下。 - -然后根据拷贝后目录下样例的txt标签文件名称(txt命名格式为:文件名.txt)在"${MeterReader代码根目录}/evaluate/yolov5_val/det_val_data/meter_det"目录下找到对应名称的jpg图片并拷贝至同级"det_val_img"目录下。 - -步骤三:预处理数据集 - -执行以下命令后,将在"${MeterReader代码根目录}/evaluate/yolov5_val/det_val_data/det_sdk_txt"目录下生成当前"det.om"模型检测验证集数据的yolo格式结果,并以图片命名的txt格式保存: -```bash -cd ${MeterReader代码根目录}/evaluate/yolov5_val -python det.py -``` - -再执行以下命令,将上述得到的yolo数据格式转换成voc数据格式,并保存至"${MeterReader代码根目录}/evaluate/yolov5_val/det_val_data/det_sdk_voc"目录下: -```bash -python yolo2voc.py -``` - -最后执行以下命令,检测验证集中的数据是否有无目标文件: -```bash -python match.py -``` - -注意事项:运行脚本之前需要把det_sdk_txt和det_sdk_voc文件夹下的文件清空。 - -步骤四:精度推理 - -[登录](https://github.com/Cartucho/mAP/edit/master/main.py)并点击下载[mAP-master.zip](https://codeload.github.com/Cartucho/mAP/zip/refs/heads/master)代码压缩包,上传服务器解压后,将代码包中的"main.py"脚本拷贝至"${MeterReader代码根目录}/evaluate/yolov5_val/"目录下,按照以下步骤修改部分代码: - -* 修改main.py第47、48、50行处文件路径 - ```python - 47 GT_PATH = os.path.join(os.getcwd(), 'det_val_data', 'det_val_voc') - 48 DR_PATH = os.path.join(os.getcwd(), 'det_val_data', 'det_sdk_voc') - 49 # # if there are no images then no animation can be shown - 50 IMG_PATH = os.path.join(os.getcwd(), 'det_val_data', 'det_val_img') - ``` - -* 修改main.py原第64行代码 - ```python - 64 show_animation = False - 65 if not args.no_animation: - ``` - -* 在main.py原第243行添加代码 - ```python - 242 def draw_plot_func(dictionary, n_classes, window_title, plot_title, x_label, output_path, to_show, plot_color, true_p_bar): - 243 to_show = False - ``` - -使用下面命令运行脚本,计算得到det.om在验证集上的mAP。 -```bash -python main.py -``` - -经过测试,YOLOv5模型的mAP为100%。 - - -2、deeplabv3模型精度测试。 - -执行以下命令,创建所需文件目录: -``` -cd ${MeterReader代码根目录}/evaluate/deeplabv3_val/ -mkdir seg_val_img -cd seg_val_img -mkdir seg_test_img -mkdir seg_test_img_groundtruth -``` - -下载[语义分割模型验证集voc格式数据](https://bj.bcebos.com/paddlex/examples/meter_reader/datasets/meter_seg.tar.gz),解压至"${MeterReader代码根录}/evaluate/deeplabv3_val/seg_val_img"目录下。然后执行以下命令拷贝数据: -``` -cp -r ${MeterReader代码根目录}/evaluate/deeplabv3_val/seg_val_img/meter_seg/meter_seg/images/val/. ${MeterReader代码根目录}/evaluate/deeplabv3_val/seg_val_img/seg_test_img/ - -cp -r ${MeterReader代码根目录}/evaluate/deeplabv3_val/seg_val_img/meter_seg/meter_seg/annotations/val/. ${MeterReader代码根目录}/evaluate/deeplabv3_val/seg_val_img/seg_test_img_groundtruth/ -``` - -采用Miou指标进行精度评价。使用下面命令运行脚本: -```bash -cd ${MeterReader代码根目录}/evaluate/deeplabv3_val/ -python seg_evaluate.py -``` - -输出各个图的Miou指标,并求得平均值作为deeplabv3模型的精度指标。经测试,deeplabv3的模型的Miou为67%。 - - -## 5 软件依赖说明 - -无第三方软件依赖。 - - -## 6 常见问题 - -### 6.1 模型转换问题 - -当图像进入流后,输出到模型的图像格式为yuv,数据类型unit8,但是om模型的时候yolov5需要输入的图像格式为RGB。 - -**解决方案:** - -在转换模型时必须要在AIPP做色域转换,要不然模型输入不正确。 - - +**步骤4:** 查看结果 +执行结束后,可在命令行内得到yolo模型输出的表盘文件路径,以及通过后续模型得到的预测表盘度数。并可在设定的输出图片目录中查看带有预测表盘计数的图片结果。最后展示的结果图片上用矩形框框出了图片中的表计并且标出了预测的表盘读数。 \ No newline at end of file diff --git a/contrib/MeterReader/evaluate/deeplabv3_val/seg_evaluate.py b/contrib/MeterReader/evaluate/deeplabv3_val/seg_evaluate.py deleted file mode 100644 index 215411208..000000000 --- a/contrib/MeterReader/evaluate/deeplabv3_val/seg_evaluate.py +++ /dev/null @@ -1,151 +0,0 @@ -# !/usr/bin/env python -# coding=utf-8 - -# Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -import os -import stat -import cv2 -import numpy as np - -import MxpiDataType_pb2 as MxpiDataType -from StreamManagerApi import StreamManagerApi, MxDataInput, StringVector - - -cur_path = os.path.abspath(os.path.dirname(__file__)) -father_path = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -pipeline_path = os.path.join(father_path, 'pipeline', 'deeplabv3', 'seg.pipeline').replace('\\', '/') -DIRNAME = os.path.join(father_path, 'evaluate', 'deeplabv3_val', 'seg_val_img', 'seg_test_img').replace('\\', '/') -det_val_dir = os.path.join(father_path, 'evaluate', \ - 'deeplabv3_val', 'seg_val_img', 'seg_test_img_groundtruth').replace('\\', '/') - - -def miou_computer(miuo_img, miou_pred): - """ - miuo_img: 已标注的原图片 - img_pred: 预测出的图片 - """ - if (miuo_img.shape != miou_pred.shape): - print("两个图片形状不一致") - exit() - - unique_item_list = np.unique(miuo_img) - unique_item_dict = {} - for index in unique_item_list: - item = index - unique_item_dict[item] = index - num = len(np.unique(unique_item_list)) - - # 混淆矩阵 - _m = np.zeros((num + 1, num + 1)) - for i in range(miuo_img.shape[0]): - for j in range(miuo_img.shape[1]): - _mi = unique_item_dict.get(miuo_img[i][j]) - _mj = unique_item_dict.get(miou_pred[i][j]) - _m[_mi][_mj] += 1 - # 前num行相加,存在num+1列 【实际下标-1】 - _m[:num, num] = np.sum(_m[:num, :num], axis=1) - # 前num+1列相加,放在num+1行【实际下标-1】 - _m[num, :num + 1] = np.sum(_m[:num, :num + 1], axis=0) - - # 计算Miou值 - miou = 0 - for i in range(num): - miou += (_m[i][i]) / (_m[i][num] + _m[num][i] - _m[i][i]) - miou /= num - return miou - - -if __name__ == '__main__': - steammanager_api = StreamManagerApi() - # init stream manager - ret = steammanager_api.InitManager() - if ret != 0: - print("Failed to init Stream manager, ret=%s" % str(ret)) - exit() - - # create streams by pipeline config file - MODES = stat.S_IWUSR | stat.S_IRUSR - with os.fdopen(os.open(pipeline_path, os.O_RDONLY, MODES), 'rb') as f: - pipeline_str = f.read() - ret = steammanager_api.CreateMultipleStreams(pipeline_str) - if ret != 0: - print("Failed to create Stream, ret=%s" % str(ret)) - exit() - dataInput = MxDataInput() - # It is best to use absolute path - # 语义分割模型推理 - files = os.listdir(DIRNAME) - det_pred_dict = {} - for file in files: - FILENAME = DIRNAME + os.path.sep + file - print(file) - if os.path.exists(FILENAME) != 1: - print("The test image does not exist. Exit.") - exit() - with os.fdopen(os.open(FILENAME, os.O_RDONLY, MODES), 'rb') as f: - dataInput.data = f.read() - STEAMNAME = b'seg' - INPLUGINID = 0 - uniqueId = steammanager_api.SendData(STEAMNAME, INPLUGINID, dataInput) - if uniqueId < 0: - print("Failed to send data to stream.") - exit() - keys = [b"mxpi_semanticsegpostprocessor0"] - keyVec = StringVector() - for key in keys: - keyVec.push_back(key) - # ### Test - - infer = steammanager_api.GetResult(STEAMNAME, b'appsink0', keyVec) - if (infer.metadataVec.size() == 0): - print("Get no data from stream !") - exit() - infer_result = infer.metadataVec[0] - if infer_result.errorCode != 0: - print("GetResult error. errorCode=%d , errMsg=%s" % (infer_result.errorCode, infer_result.errMsg)) - exit() - result = MxpiDataType.MxpiImageMaskList() - result.ParseFromString(infer_result.serializedMetadata) - pred = np.frombuffer(result.imageMaskVec[0].dataStr - , dtype=np.uint8) - img_pred = pred.reshape((512, 512)) - - det_pred_dict[file.split(".")[0]] = img_pred - - # 获取标注数据 - MODES = stat.S_IWUSR | stat.S_IRUSR - files = os.listdir(det_val_dir) - det_val_dict = {} - for file in files: - FILENAME = det_val_dir + os.path.sep + file - - if os.path.exists(FILENAME) != 1: - print("The test image does not exist. Exit.") - exit() - temp = cv2.imread(FILENAME, 0) - img = cv2.resize(temp, (512, 512)) - det_val_dict[file.split(".")[0]] = img - - unique_keys = set(det_pred_dict.keys()) - unique_keys.intersection(set(det_val_dict.keys())) - _unique = list(unique_keys) - Miou_list = [] - for key in _unique: - Miou_list.append(miou_computer(det_val_dict.get(key), det_pred_dict.get(key))) - print("The average miou is=====================:", np.average(np.array(Miou_list))) - - steammanager_api.DestroyAllStreams() diff --git a/contrib/MeterReader/evaluate/yolov5_val/det.py b/contrib/MeterReader/evaluate/yolov5_val/det.py deleted file mode 100644 index 7b2b2cdef..000000000 --- a/contrib/MeterReader/evaluate/yolov5_val/det.py +++ /dev/null @@ -1,370 +0,0 @@ -# !/usr/bin/env python -# coding=utf-8 - -# Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import json -import os -import stat -import cv2 -import numpy as np -import MxpiDataType_pb2 as MxpiDataType -from StreamManagerApi import StreamManagerApi, MxDataInput, StringVector - -cur_path = os.path.abspath(os.path.dirname(__file__)) -father_path = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -pipeline_path = os.path.join(father_path, 'pipeline', 'yolov5', 'det.pipeline').replace('\\', '/') -FILEPATH = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_val_img').replace('\\', '/') -SAVE_PATH = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_img/').replace('\\', '/') -SAVE_TXT = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_txt/').replace('\\', '/') - -MODES = stat.S_IWUSR | stat.S_IRUSR -FLAGS = os.O_WRONLY | os.O_CREAT - - -class DetPostProcessors: - def __init__(self): - self.pred = None - self.source = None - self.conf_thres = 0.25 - self.iou_thres = 0.45 - self.classes = None - self.agnostic_nms = False - self.max_det = 1000 - self.multi_label = False - self.save_path = 'det_res.jpg' - self.save_txt = '' - - @staticmethod - def xyxy2xywh(x): - # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right - - y = np.copy(x) - y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center - y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center - y[:, 2] = x[:, 2] - x[:, 0] # width - y[:, 3] = x[:, 3] - x[:, 1] # height - - return y - - @staticmethod - def xywh2xyxy(x): - # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right - y = np.copy(x) - y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x - y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y - y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x - y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y - - return y - - @staticmethod - def box_iou(box1, box2): - """ - Return intersection-over-union (Jaccard index) of boxes. - Both sets of boxes are expected to be in (x1, y1, x2, y2) format. - Arguments: - box1 (Tensor[N, 4]) - box2 (Tensor[M, 4]) - Returns: - iou (Tensor[N, M]): the NxM matrix containing the pairwise - IoU values for every element in boxes1 and boxes2 - """ - - def box_area(box): - # box = 4xn - return (box[2] - box[0]) * (box[3] - box[1]) - - area1 = box_area(box1.T) - area2 = box_area(box2.T) - - # inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2) - inter = (np.min(box1[:, None, 2:], box2[:, 2:]) - np.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2) - return inter / (area1[:, None] + area2 - inter) # iou = inter / (area1 + area2 - inter) - - @staticmethod - def new_nms(bboxes, scores, threshold=0.5): - x1 = bboxes[:, 0] - y1 = bboxes[:, 1] - x2 = bboxes[:, 2] - y2 = bboxes[:, 3] - - areas = (x2 - x1) * (y2 - y1) - - # 从大到小对应的的索引 - order = scores.argsort()[::-1] - - # 记录输出的bbox - keep = [] - while order.size > 0: - i = order[0] - # 记录本轮最大的score对应的index - keep.append(i) - - if order.size == 1: - break - - # 计算当前bbox与剩余的bbox之间的IoU - # 计算IoU需要两个bbox中最大左上角的坐标点和最小右下角的坐标点 - # 即重合区域的左上角坐标点和右下角坐标点 - xx1 = np.maximum(x1[i], x1[order[1:]]) - yy1 = np.maximum(y1[i], y1[order[1:]]) - xx2 = np.minimum(x2[i], x2[order[1:]]) - yy2 = np.minimum(y2[i], y2[order[1:]]) - - # 如果两个bbox之间没有重合, 那么有可能出现负值 - w = np.maximum(0.0, (xx2 - xx1)) - h = np.maximum(0.0, (yy2 - yy1)) - inter = w * h - - iou = inter / (areas[i] + areas[order[1:]] - inter) - - # 删除IoU大于指定阈值的bbox(重合度高), 保留小于指定阈值的bbox - ids = np.where(iou <= threshold)[0] - # 因为ids表示剩余的bbox的索引长度 - # +1恢复到order的长度 - order = order[ids + 1] - keep = np.array(keep) - - return keep - - @staticmethod - def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None): - # Rescale coords (xyxy) from img1_shape to img0_shape - if ratio_pad is None: # calculate from img0_shape - gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new - pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding - else: - gain = ratio_pad[0][0] - pad = ratio_pad[1] - - coords[:, [0, 2]] -= pad[0] # x padding - coords[:, [1, 3]] -= pad[1] # y padding - coords[:, :4] /= gain - DetPostProcessors.clip_coords(coords, img0_shape) - return coords - - @staticmethod - def clip_coords(boxes, shape): - boxes[:, [0, 2]] = boxes[:, [0, 2]].clip(0, shape[1]) # x1, x2 - boxes[:, [1, 3]] = boxes[:, [1, 3]].clip(0, shape[0]) # y1, y2 - - @staticmethod - def non_max_suppression(prediction, conf_thres, iou_thres, classes, agnostic, multi_label, - labels=(), max_det=300): - - nc = prediction.shape[2] - 5 # number of classes - xc = prediction[..., 4] > conf_thres # candidates - - # Checks - assert 0 <= conf_thres <= 1, f'Invalid Confidence threshold {conf_thres}, valid values are between 0.0 and 1.0' - assert 0 <= iou_thres <= 1, f'Invalid IoU {iou_thres}, valid values are between 0.0 and 1.0' - - # Settings - min_wh, max_wh = 2, 7680 # (pixels) minimum and maximum box width and height - max_nms = 30000 # maximum number of boxes into torchvision.ops.nms() - redundant = True # require redundant detections - multi_label &= nc > 1 # multiple labels per box (adds 0.5ms/img) - merge = False # use merge-NMS - - output = [np.zeros((0, 6))] * prediction.shape[0] - for xi, x in enumerate(prediction): # image index, image inference - # Apply constraints - x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 # width-height - x = x[xc[xi]] # confidence - - # Cat apriori labels if autolabelling - if labels and len(labels[xi]): - lb = labels[xi] - # torch.zeros返回一个由标量0填充的张量,它的形状由size决定 - v = np.zeros((len(lb), nc + 5)) - v[:, :4] = lb[:, 1:5] # box - v[:, 4] = 1.0 # conf - v[range(len(lb)), lb[:, 0].long() + 5] = 1.0 # cls - x = np.concatenate((x, v), axis=0) - - # If none remain process next image - if not x.shape[0]: - continue - - # Compute conf - x[:, 5:] *= x[:, 4:5] # conf = obj_conf * cls_conf - box = DetPostProcessors.xywh2xyxy(x[:, :4]) - - # Detections matrix nx6 (xyxy, conf, cls) - if multi_label: - i, j = (x[:, 5:] > conf_thres).nonzero(as_tuple=False).T - x = np.concatenate((box[i], x[i, j + 5, None], j[:, None].float()), axis=1) - else: # best class only - - conf = np.max(x[:, 5:], axis=1) - j = np.argmax(x[:, 5:], axis=1) - conf = np.array(conf)[None].T - j = np.array(j)[None].T - - temp_x = np.concatenate((box, conf, j), axis=1) - x = np.squeeze(temp_x) - - # Filter by class - if classes is not None: - x = x[(x[:, 5:6] == np(classes)).any(1)] - - # Check shape - n = x.shape[0] # number of boxes - if not n: # no boxes - continue - elif n > max_nms: # excess boxes - x = x[x[:, 4].argsort(descending=True)[:max_nms]] # sort by confidence - - # Batched NMS - c = x[:, 5:6] * (0 if agnostic else max_wh) # classes - boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores - - i = DetPostProcessors.new_nms(boxes, scores) # NMS - - if i.shape[0] > max_det: # limit detections - i = i[:max_det] - if merge and (1 < n < 3E3): # Merge NMS (boxes merged using weighted mean) - # update boxes as boxes(i,4) = weights(i,n) * boxes(n,4) - iou = DetPostProcessors.box_iou(boxes[i], boxes) > iou_thres # iou matrix - weights = iou * scores[None] # box weights # merged boxes - x[i, :4] = np.matmul(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes - if redundant: - i = i[iou.sum(1) > 1] # require redundancy - - output[xi] = x[i] - - return output - - @staticmethod - def xyxy2xywh(x): - # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right - z = [0, 0, 0, 0] - gn = [1920, 1080, 1920, 1080] - z[0] = (x[0] + x[2]) / 2 # x center - z[1] = (x[1] + x[3]) / 2 # y center - z[2] = x[2] - x[0] # width - z[3] = x[3] - x[1] # height - - temp_xyxy = (np.array(z)[None]) / gn - xywh = np.squeeze(temp_xyxy, axis=0) - x1 = str(xywh[0])[:8] - x2 = str(xywh[1])[:8] - x3 = str(xywh[2])[:8] - x4 = str(xywh[3])[:8] - temp_line = "0" + " " + x1 + " " + x2 + " " + x3 + " " + x4 + " " - return temp_line - - def run(self): - # NMS 非极大值抑制 - pred_result_in = DetPostProcessors.non_max_suppression(self.pred, self.conf_thres, self.iou_thres, self.classes, - self.agnostic_nms, self.multi_label) - - line = '' - file_name = self.source.split("/")[-1][:-4] - # Process predictions - for i, det in enumerate(pred_result_in): # per image - im0 = cv2.imread(self.source) - if len(det): - # [1024, 576]是图片根据模型输入resize后的尺寸 - res = DetPostProcessors.scale_coords([1024, 576], det[:, :4], im0.shape).round() - - for meter in res: - x = [int(meter[0]), int(meter[1]), int(meter[2]), int(meter[3])] - temp_line = DetPostProcessors.xyxy2xywh(x) - - cv2.rectangle(im0, (int(meter[0]), int(meter[1])), (int(meter[2]), int(meter[3])), (0, 255, 0), 2) - res_img_path = (self.save_path + file_name + '.jpg').replace("\\", "/") - cv2.imwrite(res_img_path, im0) - line += temp_line + str(round(det[:, :5][-1][-1], 6)) + "\n" - - lable_path = (self.save_txt + "/" + file_name + '.txt').replace("\\", "/") - - with os.fdopen(os.open(lable_path, os.O_WRONLY | os.O_CREAT, MODES), 'w') as lable: - lable.write(line) - - -if __name__ == '__main__': - - steammanager_api = StreamManagerApi() - # init stream manager - ret = steammanager_api.InitManager() - if ret != 0: - print("Failed to init Stream manager, ret=%s" % str(ret)) - exit() - - # create streams by pipeline config file - MODES = stat.S_IWUSR | stat.S_IRUSR - with os.fdopen(os.open(pipeline_path, os.O_RDONLY, MODES), 'rb') as f: - pipeline_str = f.read() - ret = steammanager_api.CreateMultipleStreams(pipeline_str) - if ret != 0: - print("Failed to create Stream, ret=%s" % str(ret)) - exit() - dataInput = MxDataInput() - # It is best to use absolute path - - if os.path.exists(FILEPATH) != 1: - print("The test image does not exist. Exit.") - exit() - imgs = os.listdir(FILEPATH) - DetPostProcessors = DetPostProcessors() - for img in imgs: - img_path = os.path.join(FILEPATH, img) - with os.fdopen(os.open(img_path, os.O_RDONLY, MODES), 'rb') as f: - dataInput.data = f.read() - STEAMNAME = b'detection' - INPLUGINID = 0 - uniqueId = steammanager_api.SendData(STEAMNAME, INPLUGINID, dataInput) - if uniqueId < 0: - print("Failed to send data to stream.") - exit() - - keys = [b"mxpi_tensorinfer0"] - keyVec = StringVector() - for key in keys: - keyVec.push_back(key) - - # 从流中取出对应插件的输出数据 - infer = steammanager_api.GetResult(STEAMNAME, b'appsink0', keyVec) - if (infer.metadataVec.size() == 0): - print("Get no data from stream !") - exit() - print("result.metadata size: ", infer.metadataVec.size()) - infer_result = infer.metadataVec[0] - if infer_result.errorCode != 0: - print("GetResult error. errorCode=%d , errMsg=%s" % (infer_result.errorCode, infer_result.errMsg)) - exit() - - result = MxpiDataType.MxpiTensorPackageList() - result.ParseFromString(infer_result.serializedMetadata) - - pred_result = np.frombuffer(result.tensorPackageVec[0].tensorVec[0].dataStr, dtype=np.float32) - pred_result.resize(1, 36288, 6) - np.save(r"img_pred.npy", pred_result) - - pred_result = np.load(r"img_pred.npy") - - DetPostProcessors.source = img_path - DetPostProcessors.pred = pred_result - DetPostProcessors.save_path = SAVE_PATH - DetPostProcessors.save_txt = SAVE_TXT - DetPostProcessors.run() - - print("It is finish!") - - # destroy streams - steammanager_api.DestroyAllStreams() diff --git a/contrib/MeterReader/evaluate/yolov5_val/match.py b/contrib/MeterReader/evaluate/yolov5_val/match.py deleted file mode 100644 index 8f2c72459..000000000 --- a/contrib/MeterReader/evaluate/yolov5_val/match.py +++ /dev/null @@ -1,110 +0,0 @@ -# !/usr/bin/env python -# coding=utf-8 - -# Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import sys -import os -import glob - -''' -validate_voc_PATH:验证集的voc数据 -sdk_predict_voc_PATH:sdk预测的voc数据 -检查是否有的数据是无目标的 -''' -cur_path = os.path.abspath(os.path.dirname(__file__)) -father_path = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -validate_voc_PATH = os.path.join(father_path, \ - 'evaluate', 'yolov5_val', 'det_val_data', 'det_val_voc').replace('\\', '/') -sdk_predict_voc_PATH = os.path.join(father_path, \ - 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_voc').replace('\\', '/') - - -def equals(varo, vart): - return varo == vart - - -def equal_zero(veroi): - return veroi == 0 - - -def error(error_msg, other="", exit_flag=False): - if (not error_msg): - return - else: - print(error_msg, other) - if (exit_flag): - sys.exit() - - -BACKUP_FOLDER = 'backup_no_matches_found' - -os.chdir(validate_voc_PATH) -validate_voc_files = glob.glob('*.txt') -if equal_zero(len(validate_voc_files)): - error("Error: no .txt files found in", validate_voc_PATH, exit_flag=True) - -os.chdir(sdk_predict_voc_PATH) -sdk_predict_voc_files = glob.glob('*.txt') - -if equal_zero(sdk_predict_voc_files): - error("Error: no .txt files found in", sdk_predict_voc_PATH, exit_flag=True) - -validate_voc_files = set(validate_voc_files) -sdk_predict_voc_files = set(sdk_predict_voc_files) -print('total ground-truth files:', len(validate_voc_files)) -print('total detection-results files: ' + str(len(sdk_predict_voc_files)) + '\n') - -validate_voc_backup = validate_voc_files - sdk_predict_voc_files -sdk_predict_voc_backup = sdk_predict_voc_files - validate_voc_files - -# validate_voc - -if not validate_voc_backup: - error('No backup required for', validate_voc_PATH, exit_flag=False) -else: - os.chdir(validate_voc_PATH) - ## create the backup dir if it doesn't exist already - if not os.path.exists(BACKUP_FOLDER): - os.makedirs(BACKUP_FOLDER) - for file in validate_voc_backup: - os.rename(file, BACKUP_FOLDER + '/' + file) - -# sdk_predict_voc -if not sdk_predict_voc_backup: - error('No backup required for', sdk_predict_voc_PATH, exit_flag=False) -else: - os.chdir(sdk_predict_voc_PATH) - ## create the backup dir if it doesn't exist already - if not os.path.exists(BACKUP_FOLDER): - os.makedirs(BACKUP_FOLDER) - for file in sdk_predict_voc_backup: - os.rename(file, BACKUP_FOLDER + '/' + file) - -MSG = "msg" -if validate_voc_backup: - MSG = 'total ground-truth backup files:' - print(MSG + " " + str(len(validate_voc_backup))) -if sdk_predict_voc_backup: - MSG = 'total detection-results backup files:' - print(MSG + " " + str(len(sdk_predict_voc_backup))) - -intersection_files = validate_voc_files & sdk_predict_voc_files - -MSG = 'total intersected files:' -print(MSG, len(intersection_files)) -MSG = "Intersection completed!" -print(MSG) diff --git a/contrib/MeterReader/evaluate/yolov5_val/yolo2voc.py b/contrib/MeterReader/evaluate/yolov5_val/yolo2voc.py deleted file mode 100644 index 990040fb9..000000000 --- a/contrib/MeterReader/evaluate/yolov5_val/yolo2voc.py +++ /dev/null @@ -1,73 +0,0 @@ -# !/usr/bin/env python -# coding=utf-8 - -# Copyright(C) 2022. Huawei Technologies Co.,Ltd. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -''' -把第一步得到的txt文件中的数据格式转换成voc格式 -''' - -import os -import stat -import numpy as np -import cv2 - -cur_path = os.path.abspath(os.path.dirname(__file__)) -father_path = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -label_path = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_txt').replace('\\', '/') -image_path = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_img').replace('\\', '/') -sdk_voc_path = os.path.join(father_path, 'evaluate', 'yolov5_val', 'det_val_data', 'det_sdk_voc/').replace('\\', '/') - - -# Convert nx4 boxes from [x, y, w, h] normalized to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right -def xywhn2xyxy(boxes, width=800, height=800): - padw = 0 - padh = 0 - y = np.copy(boxes) - y[:, 0] = width * (boxes[:, 0] - boxes[:, 2] / 2) + padw # top left x - y[:, 1] = height * (boxes[:, 1] - boxes[:, 3] / 2) + padh # top left y - y[:, 2] = width * (boxes[:, 0] + boxes[:, 2] / 2) + padw # bottom right x - y[:, 3] = height * (boxes[:, 1] + boxes[:, 3] / 2) + padh # bottom right y - return y - - -if __name__ == '__main__': - folderlist = os.listdir(label_path) - for i in folderlist: - label_path_new = os.path.join(label_path, i) - with open(label_path_new, 'r') as f: - lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32) # predict_label - print(lb) - read_label = label_path_new.replace(".txt", ".jpg") - read_label_path = read_label.replace('det_sdk_txt', 'det_val_img').replace("\\", "/") - img = cv2.imread(read_label_path) - h, w = img.shape[:2] - lb[:, 1:] = xywhn2xyxy(lb[:, 1:], w, h) - - # 绘图 - for _, x in enumerate(lb): - class_label = int(x[0]) # class - cv2.rectangle(img, (round(x[1]), round(x[2])), (round(x[3]), round(x[4])), (0, 255, 0)) - cv2.putText(img, str(class_label), - (int(x[1]), int(x[2] - 2)), - fontFace=cv2.FONT_HERSHEY_SIMPLEX, - fontScale=1, - color=(0, 0, 255), - thickness=2) - with os.fdopen(os.open(sdk_voc_path + i, os.O_WRONLY | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), - 'a') as voc: - voc.write(str(x[0]) + ' ' + str(x[5]) - + ' ' + str(x[1]) + ' ' + str(x[2]) - + ' ' + str(x[3]) + ' ' + str(x[4]) + '\n') -- Gitee From 9cca9c29cd092b364abe8857a94c600d63bdc9cb Mon Sep 17 00:00:00 2001 From: Malanchi Date: Tue, 17 Dec 2024 10:08:52 +0800 Subject: [PATCH 2/4] =?UTF-8?q?MeterReader=E9=80=82=E9=85=8D310P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contrib/MeterReader/README.md | 19 ++----------------- tutorials/protocolSample/CMakeLists.txt | 5 +++-- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/contrib/MeterReader/README.md b/contrib/MeterReader/README.md index d5cec12ac..868cd7d21 100644 --- a/contrib/MeterReader/README.md +++ b/contrib/MeterReader/README.md @@ -93,23 +93,8 @@ | 11 | 读数后处理 | 开发mxpi_process3插件,读出工业表的数字| -### 1.6 技术实现流程图 +### 1.7 技术实现流程图
    -
  1. 基础环境:Ascend 310、mxVision、Ascend-CANN-toolkit、Ascend Driver -
  2. 模型转换: - -PyTorch模型转昇腾离线模型:yolov5.onnx --> yolov5.om - -onnx模型转昇腾离线模型:DeepLabv3.onnx --> DeepLabv3.om - -
  3. 业务流程编排与配置 - -
  4. yolov5后处理开发 - -
  5. mxpi_process3插件的后处理开发 - -
  6. python推理流程代码开发: - YOLOv5的pipeline流程图:

    @@ -118,7 +103,7 @@ onnx模型转昇腾离线模型:DeepLabv3.onnx --> DeepLabv3.om color: #999; padding: 2px;">图1. YOLOv5的pipeline流程图
    - DeepLabv3的pipeline流程图: +

    diff --git a/tutorials/protocolSample/CMakeLists.txt b/tutorials/protocolSample/CMakeLists.txt index 3c7b9dfdd..a47ac0a74 100644 --- a/tutorials/protocolSample/CMakeLists.txt +++ b/tutorials/protocolSample/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.16) project(pipeSample) set(CMAKE_CXX_STANDARD 14) @@ -16,7 +16,7 @@ endif() # Compile options add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) add_definitions(-Dgoogle=mindxsdk_private) -add_compile_options(-std=c++11 -fPIC -fstack-protector-all -Wall) +add_compile_options(-std=c++14 -fPIC -fstack-protector-all -Wall) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now,-z,noexecstack -pie") @@ -31,6 +31,7 @@ include_directories( link_directories( ${MX_SDK_HOME}/lib/ ${MX_SDK_HOME}/opensource/lib64 + ${MX_SDK_HOME}/opensource/lib ${MX_SDK_HOME}/opensource/lib64/ /usr/local/Ascend/ascend-toolkit/latest/acllib/lib64/ /usr/local/Ascend/driver/lib64/ -- Gitee From 24144b646818879afebeaa02380750866aad5c56 Mon Sep 17 00:00:00 2001 From: Malanchi Date: Tue, 17 Dec 2024 14:20:46 +0800 Subject: [PATCH 3/4] =?UTF-8?q?MeterReader=E9=80=82=E9=85=8D310P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contrib/MeterReader/README.md | 137 ++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/contrib/MeterReader/README.md b/contrib/MeterReader/README.md index 868cd7d21..33f7b31e4 100644 --- a/contrib/MeterReader/README.md +++ b/contrib/MeterReader/README.md @@ -8,11 +8,53 @@ 在本系统中,目的是基于MindX SDK,在华为云昇腾平台上,开发端到端工业指针型表计读数的参考设计,实现对传统机械式指针表计的检测与自动读数功能,达到功能要求。 注意事项: -1. 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 +* 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 -2. 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 +* 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 -3. 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 +* 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 + +实现方案: + +本系统识别的流程是:先将输入的图像送入流中解码和缩放大小,使用YOLOv5目标检测模型去检测图片中的表盘,结束流。将目标框裁剪下来,再送入流中解码和缩放大小,用DeepLabv3语义分割模型去得到工业表中的指针和刻度,对语义分割模型预测的结果进行读数后处理,找到指针指向的刻度,根据刻度的间隔和刻度根数计算表盘的读数。 + +表1.1 系统方案中各模块功能: + +| 序号 | 子系统 | 功能描述 | +| :------------ | :---------- | :---------- | +| 1 | 图像输入 | 调用MindX SDK的appsrc输入图片| +| 2 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| +| 3 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到1024*576大小 | +| 4 | 工业表检测 | 调用MindX SDK的mxpi_tensorinfer,使用YOLOv5的检测模型,检测出图片中车辆| +| 5 | 保存工业表的图像 | 将YOLOv5检测到的工业表结果保存图片| +| 6 | 图像输入| 调用MindX SDK的appsrc输入检测到的工业表 | +| 7 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| +| 8 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到512*512大小 +| 9 | 指针刻度检测 | 调用MindX SDK的mxpi_tensorinfer,使用DeepLabv3语义分割模型,检测图像中的指针与刻度| +| 10 | 模型后处理 | 调用MindX mxpi_semanticsegpostprocessor,得到语义分割的结果| +| 11 | 读数后处理 | 开发mxpi_process3插件,读出工业表的数字| + +技术实现流图: + +
      +
      + +
      +
      图1. YOLOv5的pipeline流程图
      +
      + +
      + +
      +
      图2. DeepLabv3的pipeline流程图
      +
      +
    ### 1.2 支持的产品 @@ -72,47 +114,6 @@ ├── postprocess.cpp ├── postprocess.h ``` -### 1.6 软件方案介绍 - -本系统识别的流程是:先将输入的图像送入流中解码和缩放大小,使用YOLOv5目标检测模型去检测图片中的表盘,结束流。将目标框裁剪下来,再送入流中解码和缩放大小,用DeepLabv3语义分割模型去得到工业表中的指针和刻度,对语义分割模型预测的结果进行读数后处理,找到指针指向的刻度,根据刻度的间隔和刻度根数计算表盘的读数。 - -表2.1 系统方案中各模块功能: - -| 序号 | 子系统 | 功能描述 | -| :------------ | :---------- | :---------- | -| 1 | 图像输入 | 调用MindX SDK的appsrc输入图片| -| 2 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| -| 3 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到1024*576大小 | -| 4 | 工业表检测 | 调用MindX SDK的mxpi_tensorinfer,使用YOLOv5的检测模型,检测出图片中车辆| -| 5 | 保存工业表的图像 | 将YOLOv5检测到的工业表结果保存图片| -| 6 | 图像输入| 调用MindX SDK的appsrc输入检测到的工业表 | -| 7 | 图像解码 | 调用MindX SDK的mxpi_imagedecoder输入图片| -| 8 | 图像放缩 | 调用MindX SDK的mxpi_imageresize,放缩到512*512大小 -| 9 | 指针刻度检测 | 调用MindX SDK的mxpi_tensorinfer,使用DeepLabv3语义分割模型,检测图像中的指针与刻度| -| 10 | 模型后处理 | 调用MindX mxpi_semanticsegpostprocessor,得到语义分割的结果| -| 11 | 读数后处理 | 开发mxpi_process3插件,读出工业表的数字| - - -### 1.7 技术实现流程图 -
      -
      - -
      -
      图1. YOLOv5的pipeline流程图
      -
      - -
      - -
      -
      图2. DeepLabv3的pipeline流程图
      -
      -
    ## 2 设置环境变量 @@ -129,30 +130,22 @@ ### 3.1 模型转换 +* YOLOV5模型转换 + **步骤1:** 模型下载 下载[onnx模型压缩包](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/MeterReader/models.zip)并解压 -* 将压缩包中的"det.onnx"模型拷贝至"\${MeterReader代码根目录}/models/yolov5"目录下 -* 将压缩包中的"seg.onnx"模型拷贝至"\${MeterReader代码根目录}/models/deeplabv3"目录下: - - 注:DeepLabv3模型提供了两种转换方式: - - * 若下载[onnx模型压缩包](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/MeterReader/models.zip),则执行上述步骤后,只需完成DeepLabv3模型转换的步骤二; - - * 若下载[pdmodel模型压缩包](https://bj.bcebos.com/paddlex/examples2/meter_reader//meter_seg_model.tar.gz),则无需执行上述步骤,参考并完成DeepLabv3模型转换的步骤一和步骤二; - - -**步骤2:**: YOLOv5模型转换 +将压缩包中的"det.onnx"模型拷贝至"\${MeterReader代码根目录}/models/yolov5"目录下。 -(1) 执行命令: +**步骤2:** 执行命令 进入"\${MeterReader代码根目录}/models/yolov5"目录,执行以下命令将"det.onnx"模型转换成"det.om"模型: ```bash atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg --soc_version=Ascend310P3 ``` -(2) 查看结果: +**步骤3:** 查看结果 出现以下语句表示命令执行成功,会在当前目录中得到"det.om"模型文件。 ``` @@ -162,15 +155,29 @@ atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg - -**步骤3:** DeepLabv3模型转换 +* DeepLabv3模型转换 -(1) 安装paddle2onnx +**步骤1:** 模型下载 + +将压缩包中的"seg.onnx"模型拷贝至"\${MeterReader代码根目录}/models/deeplabv3"目录下: + + 注:DeepLabv3模型提供了两种转换方式: + + * 若下载[onnx模型压缩包](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/MeterReader/models.zip),则执行上述步骤后,只需完成DeepLabv3模型转换的步骤四; + + * 若下载[pdmodel模型压缩包](https://bj.bcebos.com/paddlex/examples2/meter_reader//meter_seg_model.tar.gz),则参考并完成DeepLabv3模型转换的所有后续步骤; + + + +**步骤2:** 安装paddle2onnx 使用以下命令安装paddle2onnx依赖,[paddle2onnx安装参考链接](https://github.com/PaddlePaddle/Paddle2ONNX/blob/develop/docs/zh/compile.md): ```bash pip3 install paddle2onnx ``` -(2) paddle模型转onnx模型 + + +**步骤3:** paddle模型转onnx模型 确保已下载[pdmodel模型压缩包](https://bj.bcebos.com/paddlex/examples2/meter_reader//meter_seg_model.tar.gz),将目录"meter_seg_model"中的文件解压至"${MeterReader代码根目录}/models/deeplabv3"目录下,进入"deeplabv3"目录,使用以下命令将"pdmodel"模型转换成"onnx"模型,[paddle2onnx模型转换参考链接](https://github.com/PaddlePaddle/Paddle2ONNX/): ```bash @@ -182,15 +189,15 @@ pip3 install paddle2onnx --enable_dev_version True ``` -(3) onnx模型转om模型 +**步骤4:** onnx模型转om模型 -进入"\${MeterReader代码根目录}/models/deeplabv3"目录,执行以下命令将"seg.onnx"模型转换成"seg.om"模型 +进入"\${MeterReader代码根目录}/models/deeplabv3"目录,执行以下命令将"seg.onnx"模型转换成"seg.om"模型。 ```bash cd ${MeterReader代码根目录}/models/deeplabv3 atc --model=seg.onnx --framework=5 --output=seg --insert_op_conf=seg_aipp.cfg --input_shape="image:1,3,512,512" --input_format=NCHW --soc_version=Ascend310P3 ``` -(4) 查看结果 +**步骤5:** 查看结果 出现以下语句表示命令执行成功,会在当前目录中得到seg.om模型文件。 ``` @@ -203,7 +210,7 @@ pip3 install paddle2onnx **步骤1:** 编译插件 -在项目目录下执行如下命令 +在项目目录下执行如下命令: ```bash cd ${MeterReader代码根目录}/plugins/process3 . build.sh @@ -232,4 +239,4 @@ python3 main.py --ifile ${输入图片路径} --odir ${输出图片目录} **步骤4:** 查看结果 -执行结束后,可在命令行内得到yolo模型输出的表盘文件路径,以及通过后续模型得到的预测表盘度数。并可在设定的输出图片目录中查看带有预测表盘计数的图片结果。最后展示的结果图片上用矩形框框出了图片中的表计并且标出了预测的表盘读数。 \ No newline at end of file +执行结束后,可在命令行内得到yolo模型输出的表盘文件路径,以及通过后续模型得到的预测表盘度数。可在设定的输出图片目录中查看带有预测表盘计数的图片结果。最后展示的结果图片上用矩形框框出了图片中的表计并且标出了预测的表盘读数。 \ No newline at end of file -- Gitee From 6421c85ed2f833a903420d905246135f61dfafb7 Mon Sep 17 00:00:00 2001 From: Malanchi Date: Tue, 17 Dec 2024 14:24:08 +0800 Subject: [PATCH 4/4] =?UTF-8?q?MeterReader=E9=80=82=E9=85=8D310P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contrib/MeterReader/README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contrib/MeterReader/README.md b/contrib/MeterReader/README.md index 33f7b31e4..ec4f2025f 100644 --- a/contrib/MeterReader/README.md +++ b/contrib/MeterReader/README.md @@ -7,12 +7,7 @@ 在本系统中,目的是基于MindX SDK,在华为云昇腾平台上,开发端到端工业指针型表计读数的参考设计,实现对传统机械式指针表计的检测与自动读数功能,达到功能要求。 -注意事项: -* 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 -* 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 - -* 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 实现方案: @@ -56,6 +51,13 @@
+ +注意事项: +* 本系统中只使用了两种类型的表盘数据参与训练和测试。我们通过预测的刻度根数来判断表盘类型,第一种表盘的刻度根数为50,第二种表盘的刻度根数为32。因此,目前系统只能实现这两种针表计的检测和自动读数功能。 + +* 本系统要求拍摄图片角度正常,尽可能清晰。如果拍摄图片角度不正常,导致图片模糊,则很难正确读出表数。 + +* 本系统采用opencv进行图片处理,要求输入文件均为opencv可处理文件。 ### 1.2 支持的产品 本项目支持昇腾Atlas 300I pro、 Atlas 300V pro。 @@ -155,6 +157,8 @@ atc --model=det.onnx --framework=5 --output=det --insert_op_conf=det_aipp.cfg - + + * DeepLabv3模型转换 **步骤1:** 模型下载 -- Gitee