教你用Ascend310P实现一个简单的抠图操作
前置知识及案例介绍
本次的抠图案例需要小伙伴们有以下前置知识:linux基础命令。你没有听错,只要你有常见的的linux命令的储备(包括但不限于目录切换,vim基础操作),你就能轻松完成这个项目啦!
本次的抠图案例采用华为码云(gitee)上官方提供的案例,没有码云的小伙伴需注册账号,然后将工程文件下载下来(建议直接网上下载完整个sample包放到自己的磁盘中,然后利用mobaXterm便捷的文件传输功能完成相应任务,体验感极其流畅) 这里是本次项目的gitee链接:samples: CANN Samples - Gitee.com
开始前的注意事项
1.最好直接从小助手那里获取远程服务器,省去环境安装问题(这是卡s最多小白的一步),小助手的联系方式在活动主帖中就能找到。
2.本次原案例是实现八个抠图,后面我们可以将它改为单抠图,抠你想抠的地方!
3.以下操作均在你已经配好了环境变量及相应环境,本次演示的配置如下:
操作系统及架构:Ubuntu 18.04 x86_64
编译器:aarch64-linux-gnu-g++
芯片:Ascend 310p
python及依赖的库:python3.7.5
已在环境上部署昇腾AI软件栈(小助手提供的)
4.个人环境变量配置参考
这里简略介绍下环境变量里的DDK_PATH和NPU_HOST_PATH
NPU_HOST_PATH用来找lib库
DDK_PATH用来找头文件,在项目里的源代码你会看到很多头文件比如很多小伙伴会遇到的报错‘acl/acl.h’报错就是因为DDK没配好。
5.建议纯小白朋友一定要练习阅读文档,身为小白的我虽然仍然是小白,但是通过这次活动锻炼到了自己查文档解决问题的能力,在这里附上社区版文档:https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/51RC2alpha005/overview/index.html(有不懂的词直接cv查找)
抠图工程步骤
1.编译目录创建及编译
首先我们在batchcrop目录下(后文统称样例目录)创建目录mkdir -p build/intermediate/host 用于存放编译文件
切换至刚创建的host目录,执行如下命令:
cmake ../../../src -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE
然后执行make命令,生成的可执行文件main在“样例目录/out“目录下。
2.获取输入图片
在 https://c7xcode.obs.cn-north-4.myhuaweicloud.com/models/aclsample/dvpp_vpc_1920x1080_nv12.yuv 网页获取本次样例图片,并在样例目录下新建data目录,将照片从windows系统上托进ubuntu中,或者你也可以直接在data目录下wget https://c7xcode.obs.cn-north-4.myhuaweicloud.com/models/aclsample/dvpp_vpc_1920x1080_nv12.yuv
3.运行应用
切换到main所在的目录(在样例目录/out),然后运行,若没权限则设置为可执行即可,运行成功的信息如下:
执行成功后,在屏幕上的关键提示信息示例如下。
[INFO] aclInit success, ret = 0.
[INFO] open device 0 success
[INFO] create context success
[INFO] create stream success
[INFO] dvpp init resource success
[INFO] open file = ./dvpp_vpc_1920x1080_nv12.yuv success.
[INFO] start set inputDesc success.
[INFO] write out to file ./cropName0 success.
[INFO] write out to file ./cropName1 success.
[INFO] write out to file ./cropName2 success.
[INFO] write out to file ./cropName3 success.
[INFO] write out to file ./cropName4 success.
[INFO] write out to file ./cropName5 success.
[INFO] write out to file ./cropName6 success.
[INFO] write out to file ./cropName7 success.
[INFO] ProcessBatchCrop success.
[INFO] ProcessBatchCrop success.
[INFO] DestroyBatchCropResource start
[INFO] DestroyBatchCropResource end
[INFO] SampleProcess DestroyResource start.
[INFO] end to destroy stream
[INFO] end to destroy context
[INFO] 0 deviceID
[INFO] end to reset device is 0
[INFO] SampleProcess DestroyResource success.
[INFO] end to finalize acl
......
执行可执行文件成功后,同时会在main文件同级的目录下生成结果文件,便于后期查看。
cropName0、cropName1、cropName2、cropName3、cropName4、cropName5、cropName6、cropName7这八张图片是从输入图片dvpp_vpc_1920×1980_nv12.yuv中抠出的子图。
4.样例改造
若我们只想获得一个子图,并且希望能达到随意抠任何想抠的位置,该如何实现呢?
这里我以抠中心位置为例,教大家如何修改样例代码
首先我们要熟悉样例中的目录结构,可以看到抠图核心实现功能的文件名为dvpp_process.h,用于实现数据预处理相关函数。
留意第174-208,以及307-332 (注意:代码行号可能会随着样例的更新而更新)
void DvppProcess::InitCropArea()
{
Area area;
area.cropLeftOffset = 660;
area.cropTopOffset = 140;
batchCropArea_.push_back(area);
/*
area.cropLeftOffset = 30;
area.cropTopOffset = 200;
batchCropArea_.push_back(area);
area.cropLeftOffset = 100;
area.cropTopOffset = 300;
batchCropArea_.push_back(area);
area.cropLeftOffset = 200;
area.cropTopOffset = 400;
batchCropArea_.push_back(area);
area.cropLeftOffset = 0;
area.cropTopOffset = 600;
batchCropArea_.push_back(area);
area.cropLeftOffset = 230;
area.cropTopOffset = 350;
batchCropArea_.push_back(area);
area.cropLeftOffset = 600;
area.cropTopOffset = 188;
batchCropArea_.push_back(area);
area.cropLeftOffset = 500;
area.cropTopOffset = 600;
batchCropArea_.push_back(area);
*/
Result DvppProcess::ProcessBatchCrop()
{
INFO_LOG("ProcessBatchCrop start.");
const uint32_t oddNum = 1;
const uint32_t cropSizeWidth = 600;
const uint32_t cropSizeHeight = 800;
uint32_t cropRightOffset = 0;
uint32_t cropBottomOffset = 0;
InitCropArea(); // init batchCropArea crop position
size_t length = batchCropArea_.size();
if (length < outputBatchSize_) {
outputBatchSize_ = length;
}
std::unique_ptr<acldvppRoiConfig *[]>
cropArea(new(std::nothrow) acldvppRoiConfig *[outputBatchSize_ * sizeof(acldvppRoiConfig *)]);
for (uint32_t i = 0; i < outputBatchSize_; i++) {
cropRightOffset = batchCropArea_<i>.cropLeftOffset + cropSizeWidth - oddNum;
cropBottomOffset = batchCropArea_<i>.cropTopOffset + cropSizeHeight - oddNum;
cropArea<i> = acldvppCreateRoiConfig(batchCropArea_<i>.cropLeftOffset, cropRightOffset,
batchCropArea_<i>.cropTopOffset, cropBottomOffset);
if (cropArea<i> == nullptr) {
ERROR_LOG("acldvppCreateRoiConfig cropArea_ failed");
return FAILED;
}
}
首先观察174行的initcroparea,初始化抠图区域,可以看到此处有八个push_back操作放在了所有需要抠图的一个数据结构batchcroparea中,正好对应了我们本次原样例里生成的8个子图,那么我们是不是注释掉其中的7个就能达到实现抠一个子图的效果呢?我们先将他注释掉。
接下来就是抠中心区域,看向第二段代码块,我们观察到这里有几个关键参数,一个是cropsizewidth/height,用于表示抠多大的子图,一个是crop left/right/bottom/top offset,字面上理解就是偏移量的意思.
你可能已经猜到了,这个抠中心子图的方法其实只要会小学加减法+能找到这段代码就完成了,为什么这么说呢?
本次图片尺寸1920*1080,假设我们要抠的子图宽600,高800,那就意味着子图的上下边和原图有540-400 = 140的距离,那么我们就将area.cropTopOffset设置为140,同理leftoffset设置为140,代码就改完了(注:只要会一点基础的英文就能读清楚代码块的含义)
保存更新后的dvpp_process.cpp文件,重新建立build1文件,重复上述步骤,你就能得到一张名为crop0的中心子图啦!