昇腾AI实战:基于AscendCL的YOLOv5目标检测应用开发【华为根技术】

举报
柠檬🍋 发表于 2025/12/21 14:29:17 2025/12/21
【摘要】 昇腾AI实战:基于AscendCL的YOLOv5目标检测应用开发本文将详细介绍如何在昇腾芯片上使用AscendCL开发一个完整的目标检测应用,基于预训练的YOLOv5模型实现高效的目标检测推理流程。 1. 项目概述与准备 项目目标开发一个能够实时检测图像中多种物体的目标检测程序,实现从输入图像到检测框和类别输出的完整推理流程。 完整工作流程模型加载:将预编译的YOLOv5模型(.om文件)...

昇腾AI实战:基于AscendCL的YOLOv5目标检测应用开发

本文将详细介绍如何在昇腾芯片上使用AscendCL开发一个完整的目标检测应用,基于预训练的YOLOv5模型实现高效的目标检测推理流程。
image.png

1. 项目概述与准备

项目目标

开发一个能够实时检测图像中多种物体的目标检测程序,实现从输入图像到检测框和类别输出的完整推理流程。

完整工作流程

  1. 模型加载:将预编译的YOLOv5模型(.om文件)加载到昇腾设备
  2. 数据预处理
    • 图像读取与解码
    • 尺寸调整至640×640像素(保持宽高比)
    • 颜色通道归一化(0-255归一化到0-1)
    • 数据格式转换(HWC→CHW)
  3. 内存管理
    • 设备端内存分配
    • 主机到设备的数据传输
  4. 推理执行:调用昇腾计算单元执行模型推理
  5. 后处理
    • 设备到主机的数据传输
    • 检测框解码与非极大值抑制(NMS)
    • 绘制检测框并输出结果

详细准备工作

  1. 模型准备

    • 从昇腾ModelZoo获取预编译的YOLOv5模型
    • 自定义模型转换:
    atc --model=yolov5s.onnx --framework=5 --output=yolov5s --input_shape="images:1,3,640,640" --soc_version=Ascend310
    
  2. 测试数据

    • 准备包含多种物体的测试图像
    • 支持格式:JPEG/PNG/BMP等
    • 建议包含人、车辆、动物等常见类别
  3. 类别标签

    • 准备COCO类别标签文件(coco_labels.txt)
    • 格式示例:
    0: person
    1: bicycle
    2: car
    ...
    79: toothbrush
    
  4. 开发环境

    • 已安装Ascend-CANN-Toolkit开发套件(版本≥5.0.2)
    • 配置好AscendCL开发环境
    • 安装OpenCV(版本≥4.0)

2. 核心代码模块详解

模块一:模型加载与初始化

// YOLOv5模型加载
size_t modelSize = 0;
void* modelPtr = nullptr;
aclError ret = aclmdlLoadFromFileWithMem("yolov5s.om", &modelSize, &modelPtr);
if (ret != ACL_ERROR_NONE) {
    printf("Failed to load YOLOv5 model, error code: %d\n", ret);
    return -1;
}

// 创建模型描述
uint32_t modelId = 0;
aclmdlDesc* modelDesc = aclmdlCreateDesc();
ret = aclmdlGetDesc(modelDesc, modelId);

// 获取输入输出信息
aclmdlIODims inputDims;
ret = aclmdlGetInputDims(modelDesc, 0, &inputDims);
printf("Input dimensions: [%d, %d, %d, %d]\n", 
       inputDims.dims[0], inputDims.dims[1], 
       inputDims.dims[2], inputDims.dims[3]);

// YOLOv5有3个输出层
size_t outputNum = aclmdlGetNumOutputs(modelDesc);
printf("YOLOv5 has %zu output layers\n", outputNum);

模块二:数据预处理

// 保持宽高比的resize
cv::Mat letterbox(const cv::Mat& src, int target_width, int target_height) {
    int src_width = src.cols;
    int src_height = src.rows;
    
    float scale = std::min((float)target_width/src_width, 
                          (float)target_height/src_height);
    
    int new_width = src_width * scale;
    int new_height = src_height * scale;
    
    cv::Mat resized;
    cv::resize(src, resized, cv::Size(new_width, new_height));
    
    cv::Mat padded = cv::Mat::zeros(target_height, target_width, CV_8UC3);
    resized.copyTo(padded(cv::Rect((target_width-new_width)/2, 
                                  (target_height-new_height)/2, 
                                  new_width, new_height)));
    return padded;
}

// 预处理流程
cv::Mat image = cv::imread("test.jpg");
cv::Mat processed = letterbox(image, 640, 640);

// 转换为RGB并归一化
cv::cvtColor(processed, processed, cv::COLOR_BGR2RGB);
processed.convertTo(processed, CV_32FC3, 1.0/255.0);

// 转换为CHW格式
std::vector<cv::Mat> channels(3);
cv::split(processed, channels);
std::vector<float> inputData;
for (int c = 0; c < 3; ++c) {
    inputData.insert(inputData.end(), 
                    (float*)channels[c].data, 
                    (float*)channels[c].data + 640*640);
}

模块三:推理执行

// 创建输入输出数据集
aclmdlDataset* inputDataset = aclmdlCreateDataset();
aclmdlDataset* outputDataset = aclmdlCreateDataset();

// 分配设备内存并传输数据
size_t inputSize = aclmdlGetInputSizeByIndex(modelDesc, 0);
void* devInput = nullptr;
aclrtMalloc(&devInput, inputSize, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMemcpy(devInput, inputSize, inputData.data(), 
            inputData.size()*sizeof(float), ACL_MEMCPY_HOST_TO_DEVICE);

// 封装输入
aclDataBuffer* inputBuffer = aclCreateDataBuffer(devInput, inputSize);
aclmdlAddDatasetBuffer(inputDataset, inputBuffer);

// 准备输出缓冲区(YOLOv5有3个输出)
for (size_t i = 0; i < outputNum; ++i) {
    size_t outputSize = aclmdlGetOutputSizeByIndex(modelDesc, i);
    void* devOutput = nullptr;
    aclrtMalloc(&devOutput, outputSize, ACL_MEM_MALLOC_NORMAL_ONLY);
    aclDataBuffer* outputBuffer = aclCreateDataBuffer(devOutput, outputSize);
    aclmdlAddDatasetBuffer(outputDataset, outputBuffer);
}

// 执行推理
auto start = std::chrono::high_resolution_clock::now();
ret = aclmdlExecute(modelDesc, inputDataset, outputDataset);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
printf("Inference time: %.2f ms\n", elapsed.count() * 1000);

模块四:后处理与结果可视化

struct Detection {
    int class_id;
    float confidence;
    cv::Rect bbox;
};

// NMS非极大值抑制
void non_max_suppression(std::vector<Detection>& detections, float iou_threshold) {
    std::sort(detections.begin(), detections.end(),
             [](const Detection& a, const Detection& b) {
                 return a.confidence > b.confidence;
             });
    
    std::vector<Detection> filtered;
    for (size_t i = 0; i < detections.size(); ++i) {
        bool keep = true;
        for (size_t j = 0; j < filtered.size(); ++j) {
            float iou = calculate_iou(detections[i].bbox, filtered[j].bbox);
            if (iou > iou_threshold) {
                keep = false;
                break;
            }
        }
        if (keep) filtered.push_back(detections[i]);
    }
    detections = filtered;
}

// 解析YOLOv5输出
std::vector<Detection> parse_outputs(aclmdlDataset* outputDataset, 
                                    float conf_threshold = 0.25) {
    std::vector<Detection> detections;
    
    for (size_t i = 0; i < 3; ++i) {  // 3个输出层
        aclDataBuffer* buffer = aclmdlGetDatasetBuffer(outputDataset, i);
        void* data = aclGetDataBufferAddr(buffer);
        size_t size = aclGetDataBufferSize(buffer);
        
        // 传输到主机
        std::vector<float> hostOutput(size / sizeof(float));
        aclrtMemcpy(hostOutput.data(), size, data, size, ACL_MEMCPY_DEVICE_TO_HOST);
        
        // 解析检测结果(简化示例)
        // 实际需要根据YOLOv5输出格式进行解析
        int num_anchors = 3;
        int grid_size = 80;  // 示例值
        
        for (int a = 0; a < num_anchors; ++a) {
            for (int y = 0; y < grid_size; ++y) {
                for (int x = 0; x < grid_size; ++x) {
                    // 解析每个位置的检测结果
                    // 包括:x, y, w, h, confidence, class probabilities
                }
            }
        }
    }
    
    // 应用NMS
    non_max_suppression(detections, 0.45);
    
    return detections;
}

// 绘制检测结果
void draw_detections(cv::Mat& image, const std::vector<Detection>& detections,
                    const std::vector<std::string>& class_names) {
    std::vector<cv::Scalar> colors = {
        cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0), 
        cv::Scalar(0, 0, 255), cv::Scalar(255, 255, 0),
        cv::Scalar(255, 0, 255)
    };
    
    for (const auto& det : detections) {
        if (det.confidence < 0.25) continue;
        
        cv::Scalar color = colors[det.class_id % colors.size()];
        cv::rectangle(image, det.bbox, color, 2);
        
        std::string label = cv::format("%s %.2f", 
                                      class_names[det.class_id].c_str(), 
                                      det.confidence);
        
        cv::putText(image, label, cv::Point(det.bbox.x, det.bbox.y - 5),
                   cv::FONT_HERSHEY_SIMPLEX, 0.5, color, 1);
    }
}

3. 完整编译与运行指南

CMakeLists.txt配置

cmake_minimum_required(VERSION 3.12)
project(YOLOv5_Inference)

set(CMAKE_CXX_STANDARD 11)

# 查找OpenCV
find_package(OpenCV REQUIRED)

# 设置昇腾库路径
set(ASCEND_DIR /usr/local/Ascend/ascend-toolkit/latest)
include_directories(${ASCEND_DIR}/include)
link_directories(${ASCEND_DIR}/lib64)

# 添加可执行文件
add_executable(yolov5_inference 
    src/main.cpp
    src/detection.cpp
    src/postprocess.cpp)

# 链接库
target_link_libraries(yolov5_inference 
    ${OpenCV_LIBS}
    ascendcl
    pthread)

编译与运行

# 编译
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)

# 运行单张图像
./yolov5_inference test.jpg

# 运行视频文件
./yolov5_inference video.mp4

# 摄像头实时检测
./yolov5_inference 0  # 0表示默认摄像头

预期输出示例

YOLOv5 model loaded successfully
Input dimensions: [1, 3, 640, 640]
Preprocessing time: 1.23 ms
Inference time: 4.56 ms
Postprocessing time: 0.89 ms

Detection results:
person: 0.92 at [120, 80, 200, 400]
car: 0.88 at [300, 150, 500, 280]
dog: 0.76 at [50, 200, 180, 350]
Total objects detected: 3

4. 性能优化与扩展

性能优化技巧

  1. 异步处理
aclrtStream stream;
aclrtCreateStream(&stream);

// 异步数据传输和推理
aclrtMemcpyAsync(devInput, inputSize, hostData, dataSize, 
                ACL_MEMCPY_HOST_TO_DEVICE, stream);
aclmdlExecuteAsync(modelDesc, inputDataset, outputDataset, stream);

// 多流并行处理多个图像
  1. 批处理优化
// 修改输入维度支持批量
atc --model=yolov5s.onnx --input_shape="images:4,3,640,640" ...

// 批量推理
for (int i = 0; i < batch_size; ++i) {
    // 准备批量数据
}
  1. DVPP加速预处理
// 使用昇腾DVPP硬件加速图像处理
aclvdecChannelDesc* channelDesc = aclvdecCreateChannelDesc();
// 配置解码和缩放参数

功能扩展方向

  1. 多模型集成

    • 结合ResNet进行细粒度分类
    • 集成OCR模型进行文字识别
  2. 实时视频分析

    • 实现30FPS实时检测
    • 添加轨迹跟踪功能
  3. 边缘部署优化

    • 模型量化压缩
    • 动态分辨率调整

5. 总结与进阶学习

通过本项目,你已经掌握:

  1. 昇腾平台上目标检测模型的部署流程
  2. YOLOv5模型的后处理与解析方法
  3. 基于AscendCL的高性能推理实现

进阶学习建议

  1. 学习自定义算子开发,优化后处理性能
  2. 探索多芯片协同推理,提升吞吐量
  3. 研究模型蒸馏与量化,实现轻量化部署
  4. 学习使用MindX SDK简化开发流程
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。