【CANN訓練營-模型部署入門】【CANN訓練營0基礎贏滿分秘籍】Yolov5模型部署例程學習
Ascend訓練營:yolov5目標檢測代碼C++版初探(310開發(fā)板運行)
一、交叉編譯環(huán)境安裝
以下都是在已經安裝好CANN的情況下進行下一步的操作:
(CANN安裝參考:Ascend訓練營請參考CANN安裝指南)
可以從https://gitee.com/ascend/samples下載華為昇騰的代碼樣例
下載好后,可參考cplus文件夾下的分設環(huán)境安裝教程(編譯環(huán)境為虛擬機,運行環(huán)境為昇騰310)samples-master/cplusplus/environment/separate_environmental_guidance_CN.md

其中安裝opencv的庫時,從開發(fā)板拷回so等庫時,編譯如果還是出現(xiàn)了找不到opencv相關庫頭文件的情況,可以修改src/cmakelist.txt中的include_directories選項,并將上述opencv的動態(tài)庫和頭文件分別拷貝到非usr/lib下的別的文件夾,像如下這樣:

修改后,進入scripts文件夾輸入:
bash sample_build.sh
開始編譯,因為是交叉編譯,實際代碼運行位置為310板卡為arm板卡,因此編譯也應該選擇arm,如是x86環(huán)境也可以選擇x86

編譯完成后顯示:

則為編譯成功
二、代碼初探
1.警告消除:
代碼中有幾個警告可以消除:
(1).

可以刪除在main.c下的此變量!

(2).
warning: ‘uint32_t aclGetDataBufferSize(const aclDataBuffer*)’ is deprecated: aclGetDataBufferSize is deprecated, use aclGetDataBufferSizeV2 instead [-Wdeprecated-declarations]
這個警告是因為此版本的getsize函數有更新的V2版本,使用新的api即可
size_t bufferSize = aclGetDataBufferSizeV2(dataBuffer);
(3).
warning: comparison between signed and unsigned integer expressions [-Wsign-compare] for (int i = 0; i < detectionResults.size(); ++i) {
此警告是類型不同導致的,可以統(tǒng)一為int類型就可以了。
至此代碼warning修改完成。
2.代碼簡單解析
main.c的主函數代碼:
if((argc < 3) || (argv[1] == nullptr)){
? ? ERROR_LOG("Please input: ./main <image_path>`<saveImg>`(0/1)");
? ? return FAILED;
}
char image_names[5000][100];
string image_dir = string(argv[1]);
int fnum = readFileList(argv[1],image_names);
string image_path;
// string imgFormat = image_path.substr(image_path.size()-3,image_path.size());
// if ((imgFormat !="jpg") && (imgFormat !="png")){
//? ? ?ERROR_LOG("Only support JPG or PNG img!!");
//? ? ?return FAILED;
// }
string imgFormat = string("jpg");
ObjectDetect detect(modelPath,modelWidth,modelHeight);
aclError ret = detect.Init();
if (ret != ACL_SUCCESS) {
? ? ERROR_LOG("Init resource failed %d", ret);
? ? return FAILED;
}
struct? timeval tstart,tend;
double timeuse;
gettimeofday(&tstart,NULL);
aclmdlDataset* inferenceOutput;
for(int i =0;i<fnum;i++)
{
? ? // printf("run %d times\n",i);
? ? image_path = image_dir+image_names[i];
? ? Image image;
? ? image.imgFormat = imgFormat;
? ? image.modelWidth = modelWidth;
? ? image.modelHeight = modelHeight;
? ? ret = detect.Preprocess(image_path, image);
? ? if (ret != ACL_SUCCESS) {
? ? ? ? ERROR_LOG("Preprocess failed %d", ret);
? ? ? ? return FAILED;
}
? ? ret = detect.Inference(inferenceOutput, image);
? ? if (ret != ACL_SUCCESS) {
? ? ? ? ERROR_LOG("Inference model inference output data failed");
? ? ? ? return FAILED;
? ? }
ret = detect.Postprocess(image, inferenceOutput, image_path);
? ? if (ret != ACL_SUCCESS) {
? ? ? ? ERROR_LOG("Process model inference output data failed");
? ? ? ? return FAILED;
? ? }
}
gettimeofday(&tend,NULL);
timeuse = 1000000*(tend.tv_sec - tstart.tv_sec) +
(tend.tv_usec - tstart.tv_usec);
INFO_LOG("whole time: %f ms",timeuse/1000);
detect.DestroyResource();
return SUCCESS;
從上述代碼中可得出,該例程的設計分為
1. 初始化部分
2. 預處理部分
3. 推理部分
4. 后處理部分
1.初始化部分
初始化dvpp部件(包括acl、上下文,設備,運行模式等)
模型加載:
具體操作流程為申請一段空間,然后調用?aclmdlLoadFromFileWithMem
按照名稱加載模型,并獲取該加載模型的ID,其中模型的名稱是由main.c下的命名空間里去指定的,這里我們可以做一下簡單優(yōu)化,如果我們有多個模型加載,參照昇騰的C++版本API手冊,我們可知多模型加載只是需要反復調用加載函數即可,我們可以以.為界限提取om的名稱,然后加載該模型,最終會獲得一個模型ID,我們可以用容器或者結構體去存儲這些信息,這樣方便隨時使用任意模型進行推理
模型執(zhí)行所需信息初始化(設備、輸入輸出流等)
參考華為文檔準備信息

保存一下預設的預處理圖片的圖片信息(寬、高等)
2.預處理部分
預處理部分的主要思想是運用dvpp部件的接口,高速的將相關格式的圖片轉換為YUV420SP格式的圖片
這里使用的是JPG和PNG的圖片,輸出圖像在?acldvppSetPicDescFormat
?在此函數中設置為?PIXEL_FORMAT_YUV_SEMIPLANAR_420
?模式,并且通過dvpp接口都回來jpg圖片的大小按照需要resize成需要的大小
如果只是普通的灰度圖怎么處理呢?
這里在實際應用中,可能我的圖片就只是一張普通的灰度圖(僅含Y分量),因此我們可以直接在Y分量的數據后面直接添加大小為Y分量圖片大小的1/2的0x80填充UV分量,就不需要解碼等操作了,可以直接用來推理
3.推理部分
參照應用手冊中的流程,只需要準備好輸入輸出的數據結構,在調用?aclmdlExecute
?執(zhí)行推理并獲得結果即可

4.后處理部分
后處理部分主要是運用?aclmdlGetDatasetBuffer
將模型的結果讀取出來并按照模型設定的內容獲取并存儲需要的信息
注意?。。?!
所有申請的通道、設備一定要記得!銷毀!?。?!否則內存泄漏會造成嚴重的問題
ps:該文僅是為了記錄CANN訓練營的學習過程所用,不參與任何商業(yè)用途