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

1. 项目概述与准备
项目目标
开发一个能够实时检测图像中多种物体的目标检测程序,实现从输入图像到检测框和类别输出的完整推理流程。
完整工作流程
- 模型加载:将预编译的YOLOv5模型(.om文件)加载到昇腾设备
- 数据预处理:
- 图像读取与解码
- 尺寸调整至640×640像素(保持宽高比)
- 颜色通道归一化(0-255归一化到0-1)
- 数据格式转换(HWC→CHW)
- 内存管理:
- 设备端内存分配
- 主机到设备的数据传输
- 推理执行:调用昇腾计算单元执行模型推理
- 后处理:
- 设备到主机的数据传输
- 检测框解码与非极大值抑制(NMS)
- 绘制检测框并输出结果
详细准备工作
-
模型准备:
- 从昇腾ModelZoo获取预编译的YOLOv5模型
- 自定义模型转换:
atc --model=yolov5s.onnx --framework=5 --output=yolov5s --input_shape="images:1,3,640,640" --soc_version=Ascend310 -
测试数据:
- 准备包含多种物体的测试图像
- 支持格式:JPEG/PNG/BMP等
- 建议包含人、车辆、动物等常见类别
-
类别标签:
- 准备COCO类别标签文件(coco_labels.txt)
- 格式示例:
0: person 1: bicycle 2: car ... 79: toothbrush -
开发环境:
- 已安装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. 性能优化与扩展
性能优化技巧
- 异步处理:
aclrtStream stream;
aclrtCreateStream(&stream);
// 异步数据传输和推理
aclrtMemcpyAsync(devInput, inputSize, hostData, dataSize,
ACL_MEMCPY_HOST_TO_DEVICE, stream);
aclmdlExecuteAsync(modelDesc, inputDataset, outputDataset, stream);
// 多流并行处理多个图像
- 批处理优化:
// 修改输入维度支持批量
atc --model=yolov5s.onnx --input_shape="images:4,3,640,640" ...
// 批量推理
for (int i = 0; i < batch_size; ++i) {
// 准备批量数据
}
- DVPP加速预处理:
// 使用昇腾DVPP硬件加速图像处理
aclvdecChannelDesc* channelDesc = aclvdecCreateChannelDesc();
// 配置解码和缩放参数
功能扩展方向
-
多模型集成:
- 结合ResNet进行细粒度分类
- 集成OCR模型进行文字识别
-
实时视频分析:
- 实现30FPS实时检测
- 添加轨迹跟踪功能
-
边缘部署优化:
- 模型量化压缩
- 动态分辨率调整
5. 总结与进阶学习
通过本项目,你已经掌握:
- 昇腾平台上目标检测模型的部署流程
- YOLOv5模型的后处理与解析方法
- 基于AscendCL的高性能推理实现
进阶学习建议:
- 学习自定义算子开发,优化后处理性能
- 探索多芯片协同推理,提升吞吐量
- 研究模型蒸馏与量化,实现轻量化部署
- 学习使用MindX SDK简化开发流程
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)