赞
踩
目录
1. 下载地址
Download Visual Studio Code - Mac, Linux, Windows
2. 安装中要注意的内容
1. chinese中文
如果在这里安装不了的话,可以使用离线安装的方式,首先进入官网下载离线插件包:Extensions for Visual Studio family of products | Visual Studio Marketplace
然后在vscode中这么操作,选择刚刚下载的VSIX文件即可完成安装
2. 安装远程工具Remote
设置要连接的主机,然后两次回车
重启vscode
选择linux然后再输入密码
进入文件夹
data数据是从上面的crop中拿过来的
- cmake_minimum_required(VERSION 3.22)
-
- # 设置变量PROJECT_NAME的值为crop
- set(PROJECT_NAME "crop")
- # 设置项目名称为crop
- project(${PROJECT_NAME})
- # 添加dvpp需要的宏定义,相当于在代码中: #define ENABLE_DVPP_INTERFACE
- add_definitions(-DENABLE_DVPP_INTERFACE)
- # 设置头文件目录
- include_directories(
- $ENV{INSTALL_DIR}/runtime/include/
- ./include
- )
-
- # 设置动态库目录
- link_directories(
- $ENV{INSTALL_DIR}/runtime/lib64/stub
- )
-
- # 查找所有CMakeLists.txt所在目录下,src目录中的.c和.cpp文件
- file(GLOB_RECURSE CPP_FILES
- ${PROJECT_SOURCE_DIR}/src/*.c
- ${PROJECT_SOURCE_DIR}/src/*.cpp
- )
- # 编译输出的文件名为: crop 依赖src目录下的所有.c和.cpp结尾的文件
- add_executable(${PROJECT_NAME}
- ${CPP_FILES}
- )
- target_link_libraries(${PROJECT_NAME}
- ascendcl
- acl_dvpp
- stdc++
- )

CMake或者cpp没有高亮显示就上传vsix文件到香橙派
然后在vscode中安装
提示要增加一行内容,这行内容指定了CMake需要的最小版本
然后./crop就能运行程程序了
- #include <iostream>
- #include <memory>
- #include <dirent.h>
- #include <fstream>
- #include "acl/acl.h"
- #include "acl/ops/acl_dvpp.h"
-
- using namespace std;
-
- typedef struct PicDesc {
- string picName;
- int left;
- int top;
- int width;
- int height;
- } PicDesc;
-
-
- aclrtContext g_context;
- aclrtStream g_stream;
- aclrtRunMode g_runMode;
- acldvppChannelDesc * g_dvppChannelDesc;
-
-
- char * ReadBinFile(string fileName, uint32_t &fileSize)
- {
- ifstream binFile(fileName, ifstream::binary);
- if (binFile.is_open() == false)
- {
- printf("Open file %s failed.\n", fileName.c_str());
- return nullptr;
- }
-
- binFile.seekg(0, binFile.end);
- uint32_t binFileBufferLen = binFile.tellg();
- if (binFileBufferLen == 0)
- {
- printf("Binfile is empty, filename is %s.\n", fileName.c_str());
- binFile.close();
- return nullptr;
- }
-
- binFile.seekg(0, binFile.beg);
-
- char * binFileBufferData = new(nothrow) char[binFileBufferLen];
- if (binFileBufferData == nullptr) {
- printf("Malloc binFileBufferData failed.\n");
- binFile.close();
- return nullptr;
- }
- binFile.read(binFileBufferData, binFileBufferLen);
- binFile.close();
- fileSize = binFileBufferLen;
- return binFileBufferData;
- }
-
- uint32_t AlignmentHelper(uint32_t origSize, uint32_t alignment)
- {
- if (alignment == 0) {
- return 0;
- }
- uint32_t alignmentH = alignment - 1;
- return (origSize + alignmentH) / alignment * alignment;
- }
-
- uint32_t SaveDvppOutputData(const char *fileName, const void *devPtr, uint32_t dataSize)
- {
- FILE * outFileFp = fopen(fileName, "wb+");
- if (g_runMode == ACL_HOST) {
- void * hostPtr = nullptr;
- aclrtMallocHost(&hostPtr, dataSize);
- aclrtMemcpy(hostPtr, dataSize, devPtr, dataSize, ACL_MEMCPY_DEVICE_TO_HOST);
- fwrite(hostPtr, sizeof(char), dataSize, outFileFp);
- (void)aclrtFreeHost(hostPtr);
- } else {
- fwrite(devPtr, sizeof(char), dataSize, outFileFp);
- }
- fflush(outFileFp);
- fclose(outFileFp);
- return 0;
- }
-
- int main()
- {
- /*
- * 初始化device、context、stream和dvppChannelDesc,并获取运行模式
- */
- aclInit("./src/acl.json");
- aclrtSetDevice(0);
- aclrtCreateContext(&g_context, 0);
- aclrtCreateStream(&g_stream);
- g_dvppChannelDesc = acldvppCreateChannelDesc(); // 创建图像数据处理通道时的通道描述信息 g_dvppChannelDesc is acldvppChannelDesc type
- acldvppCreateChannel(g_dvppChannelDesc); // 创建图像数据处理通道
- aclrtGetRunMode(&g_runMode);
-
-
- /*
- * 读取输入和输出
- */
- // 下面设置输入图片的信息,图片的left和top参数用不到
- PicDesc inPicDesc = { "./data/wood_rabbit_1024_1068_nv12.yuv", 0, 0, 1024, 1068 };
- // 下面设置输出图片的信息
- PicDesc outPicDesc = { "./output/dvpp_rabbit_224_224_nv12.yuv", 350, 280, 224, 224 };
-
-
- /*
- * 根据输入读取文件到NPU显存中
- */
- uint32_t buffSize = 0;
- // inPicDesc.picName:要读取的文件名 buffSize:得到的文件大小 inputBuff:得到的文件的内容
- char * inputBuff = ReadBinFile(inPicDesc.picName, buffSize);
-
- void * inputBufferDev = nullptr;
- acldvppMalloc(&inputBufferDev, buffSize); // 在NPU上分配显存
- if (g_runMode == ACL_HOST) // 如果本段C++代码是运行在CPU上
- { // 把数据从内存条拷贝到NPU的显存中
- aclrtMemcpy(inputBufferDev, buffSize, inputBuff, buffSize, ACL_MEMCPY_HOST_TO_DEVICE);
- }
- else // 如果本段C++代码是运行在NPU中的CPU上
- { // 把数据从NPU显存拷贝到NPU显存中
- aclrtMemcpy(inputBufferDev, buffSize, inputBuff, buffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);
- }
- delete[] inputBuff;
-
-
- /*
- * 设置要裁剪输入图片的区域
- */
- uint32_t outputWidth = AlignmentHelper(outPicDesc.width, 16); // 输入图片宽度按照16字节对其,例如输入17,得到的outputWidth就是32
- uint32_t outputHeight = AlignmentHelper(outPicDesc.height, 2); // 输入图片高度按照2字节对其,例如输入13,得到的outputHeight就是2
- uint32_t cropLeftOffset = outPicDesc.left; // must 偶数
- uint32_t cropRightOffset = cropLeftOffset + outputWidth - 1; // must 奇数
- uint32_t cropTopOffset = outPicDesc.top; // must 偶数
- uint32_t cropBottomOffset = cropTopOffset + outputHeight - 1; // must 奇数
- acldvppRoiConfig * cropArea_ = acldvppCreateRoiConfig(cropLeftOffset, cropRightOffset, cropTopOffset, cropBottomOffset);
-
-
- /*
- * 创建输入图片描述符,并填写输入图片信息
- */
- uint32_t inputWidthStride = AlignmentHelper(inPicDesc.width, 16);
- uint32_t inputHeightStride = AlignmentHelper(inPicDesc.height, 2);
- uint32_t inputBufferSize = inputWidthStride * inputHeightStride * 3 / 2;
-
- acldvppPicDesc * vpcInputDesc_ = acldvppCreatePicDesc(); // 创建入参数描述符
- acldvppSetPicDescData(vpcInputDesc_, inputBufferDev); // 原图片存放位置的指针
- acldvppSetPicDescFormat(vpcInputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420); // 原图片的格式
- acldvppSetPicDescWidth(vpcInputDesc_, inPicDesc.width); // 原图片宽度
- acldvppSetPicDescHeight(vpcInputDesc_, inPicDesc.height); // 原图片高度
- acldvppSetPicDescWidthStride(vpcInputDesc_, inputWidthStride); // 原图片对齐后的宽度
- acldvppSetPicDescHeightStride(vpcInputDesc_, inputHeightStride); // 原图片对齐后的高度
- acldvppSetPicDescSize(vpcInputDesc_, inputBufferSize); // 输入图片的图片大小
-
-
- /*
- * 创建输出图片描述符,并填写输出图片信息
- */
- void * outputBufferDev = nullptr;
- uint32_t outputBufferSize = outputWidth * outputHeight * 3 / 2;
- acldvppMalloc(&outputBufferDev, outputBufferSize); // 在NPU上给输出图片分配内存
-
- acldvppPicDesc * vpcOutputDesc_ = acldvppCreatePicDesc();
- acldvppSetPicDescData(vpcOutputDesc_, outputBufferDev); // 输出图片存放位置指针
- acldvppSetPicDescFormat(vpcOutputDesc_, PIXEL_FORMAT_YUV_SEMIPLANAR_420); // 输出图片的格式
- cout << outPicDesc.width << " " << outPicDesc.height << " " << outputWidth << " " << outputHeight << endl;
- acldvppSetPicDescWidth(vpcOutputDesc_, outPicDesc.width); // 输出图片的宽
- acldvppSetPicDescHeight(vpcOutputDesc_, outPicDesc.height); // 输出图片的高
- acldvppSetPicDescWidthStride(vpcOutputDesc_, outputWidth); // 输出图片对齐后的宽
- acldvppSetPicDescHeightStride(vpcOutputDesc_, outputHeight); // 输出图片对齐后的高
- acldvppSetPicDescSize(vpcOutputDesc_, outputBufferSize);
-
- /*
- * 图片的处理和保存
- */
- // 由g_stream这个流水线来运行处理程序
- acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);
- // 等待g_stream流水线完成
- aclrtSynchronizeStream(g_stream);
-
- // 保存图片
- SaveDvppOutputData(outPicDesc.picName.c_str(), outputBufferDev, outputBufferSize);
-
-
- /*
- * 释放占用的资源
- */
- acldvppFree(outputBufferDev);
- acldvppDestroyRoiConfig(cropArea_);
- acldvppDestroyPicDesc(vpcInputDesc_);
- acldvppDestroyPicDesc(vpcOutputDesc_);
- aclrtDestroyStream(g_stream);
- aclrtDestroyContext(g_context);
- aclrtResetDevice(0);
- aclFinalize();
-
- return 0;
- }

下图代表裁剪成功了
注意:在使用上面代码的时候,我修改了其他的尺寸,输出的图片就是不正确的,所以我现在也没完全搞懂库函数要求传入的尺寸。但是后续我会持续更新,分享我的学习心得
按 F1 或 Ctrl+Shift+p 在弹出的备选选项中选择 C/C++:Edit Configurations (JSON),自动打开c_cpp_properties.json配置文件
在.vscode中添加头文件搜索路径的json,然后配置路径
下面的内容全是我个人的理解,如果有佬,感谢在评论区指正,我会及时修改文章
88行:aclInit("./src/acl.json");
● 一个进程内只能调用一次aclInit接口。使用AscendCL接口开发应用时,必须先调用aclInit接口,否则可能会导致后续系统内部资源初始化出错,进而导致其它业务异常。
● 输入参数是一个json格式的文件。json里面可以写啥,这个可以看官方文档:
aclInit-系统配置-AscendCL API(C&C++)-应用开发接口-CANN社区版8.0.RC2.alpha001开发文档-昇腾社区 (hiascend.com)
目前我是在json里面就写了一对花括号:{}
● 在进程的最后,要成对的使用 aclFinalize(); 函数,做收尾工作
89行:aclrtSetDevice(0);
● 我的理解就是类似于下图(下图是我的猜想,并不来自于官方),一个板子上面有3块昇腾310B的芯片。上面的代码就是设置本线程(一个进程有一个主线程)使用索引为0的昇腾芯片
● 查看官方的device操作发现,aclrtGetDeviceCount函数可以获取Device的数量,我觉得如果有3个昇腾芯片,可能返回的是3。目前香橙派上面只有一个昇腾310B芯片,返回的就是1
● 在线程的最后,要成对的使用aclrtResetDevice(0);函数,做收尾工作
90行:aclrtCreateContext(&g_context, 0);
● 香橙派上是昇腾310B芯片,昇腾310B芯片device中默认存在1个context,本程序中可创建也可不创建context
● 猜测context可能不是一个实体的内容,而是stream集合的一个概念,几个stream组成一个context
● 本程序就是在昇腾设备0上创建了一个context。创建好了以后,本线程就默认使用新创建的context。当前线程在同一时刻内只能使用其中一个Context
● 在线程的最后,要成对的使用aclrtDestroyContext(g_context);函数,做收尾工作
91行:aclrtCreateStream(&g_stream);
● 用于给当前context创建一个stream。上图昇腾310B芯片硬件资源最多支持1024个stream
● 一个stream可以用于执行一个任务,本案例中使用这个stream执行了图片裁剪指定区域的任务。多个stream可以用于同时执行多个任务
● 在线程的最后,要成对的使用aclrtDestroyStream(g_stream);函数,做收尾工作
92、93行:g_dvppChannelDesc = acldvppCreateChannelDesc();
acldvppCreateChannel(g_dvppChannelDesc);
● 这个是图片处理函数必须要的一个参数,在这里提前准备一下
acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);
94行:aclrtGetRunMode(&g_runMode);
● 获取当前程序的运行模式
ACL_DEVICE:昇腾AI软件栈运行在Device的Control CPU或板端环境上
ACL_HOST:昇腾AI软件栈运行在Host CPU上
如果是ACL_DEVICE,就代表当前程序是在AI CPU上运行的;如果是ACL_HOST,就代表当前程序是在CPU上运行的,如下图红色圆圈中写的那样
● 111行,通过ReadBinFile函数读取数据到内存中。如果程序是运行在CPU上,那么读取到的数据就放在运行内存上;如果程序是运行在昇腾芯片的AI CPU上,那么读取到的数据就放在昇腾芯片的内存,也就是显存上
● 114行,在显存上分配一段文件大小的内存。
● 115行,做判断。如果程序是运行在CPU上,就要把数据从运行内存上搬运到显存上;如果程序是运行在昇腾芯片的AI CPU上,那么就把数据从显存搬运到显存上(这里也可以不搬运,主要是为了后面写库的时候使用)
在运行内存上对应的就是HOST,到显存上对应的就是TO_DEVICE;在显存上对应的就是DEVICE,到显存上对应的就是TO_DEVICE。同理也可以HOST_TO_HOST,DEVICE_TO_HOST。
● 补充一下:如果程序运行在昇腾AI CPU上,想要在运行内存中分配内存,就需要使用aclrtMallocHost来申请内存
上面分别生成了cropArea_,用于告诉下面的函数,输入图片要裁剪的范围
acldvppVpcCropAsync(g_dvppChannelDesc, vpcInputDesc_, vpcOutputDesc_, cropArea_, g_stream);
因为我对芯片对齐等不是很了解,也不清楚这里到底怎么实现随便裁剪,这里我就不分析了,等后面学习完了再来补充,有知道怎么使用的佬也可以在评论区帮助我一下
● 143行,为什么输入的图片大小是这么计算的呢
首先是对齐后图片的宽高如下图,前面这部分很好理解:
● 后面的*3,是因为存储图片的是YUV 3个通道,这样就需要 对齐后的宽高*3得到需要的字节数,
● 那么为什么要除2呢?这是因为YUV本来YUV三个通道都应该有4个信息,也就是8bit来表示Y,8bit来表示U,8bit来表示V。YUV420图片,有8bit来表示Y,只有4bit来表述U,0bit来表示V,字节数自然就少了一半,所以要/2
在图片处理的过程中,程序可以异步的去执行了,但是176行,我们任然阻塞的去等待图片处理结果。等到图片处理结束,就将输出的图片指针和大小传入181行的函数中,函数会将图片保存到picName指定的位置去
做完上面这些任务,就释放掉占用的资源
把其他历程中的关键内容抽取出来,写成像上面那种形式的程序,多动手练习。自己依次分析函数的功能等。
上面代码过程分为这几步:初始化->读取要处理的图片->创建输入图片描述符->创建输出图片描述符->acl处理图片->把输出的图片保存->释放占用的资源
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。