MNN框架學習(二) 使用MNN部署模型

2021-09-27 13:26:32 字數 4759 閱讀 6337

按照我們對乙個模型推理過程的理解,一般模型推理過程都分為兩個部分,第乙個部分為模型載入和引數初始化,第二個部分為模型前向推理及一些後處理,得到檢測或識別的結果。

第一步將**分解成兩個部分:模型載入和前向推理。

第乙個部分**:

// load and config mnn model

auto revertor = std::unique_ptr(new revert(model_name.c_str()));

revertor->initialize();

auto modelbuffer = revertor->getbuffer();

const auto buffersize = revertor->getbuffersize();

auto net = std::shared_ptr(mnn::interpreter::createfrombuffer(modelbuffer, buffersize));

revertor.reset();

mnn::scheduleconfig config;

config.numthread = threads;

config.type = static_cast(forward);

mnn::backendconfig backendconfig;

config.backendconfig = &backendconfig;

auto session = net->createsession(config);

net->releasemodel();

第二個部分**:

std::vectordims;

auto nhwc_tensor = mnn::tensor::create(dims, null, mnn::tensor::tensorflow);

auto nhwc_data = nhwc_tensor->host();

auto nhwc_size = nhwc_tensor->size();

::memcpy(nhwc_data, image.data, nhwc_size);

std::string input_tensor = "data";

auto inputtensor = net->getsessioninput(session, nullptr);

inputtensor->copyfromhosttensor(nhwc_tensor);

// run network

net->runsession(session);

// get output data

std::string output_tensor_name0 = "conv5_fwd";

mnn::tensor *tensor_lmks = net->getsessionoutput(session, output_tensor_name0.c_str());

mnn::tensor tensor_lmks_host(tensor_lmks, tensor_lmks->getdimensiontype());

tensor_lmks->copytohosttensor(&tensor_lmks_host);

重組就是將**按照功能定義成對應介面,按照c++八大設計原則中講到,我們應該針對介面程式設計,而不是針對過程程式設計,所以需要定義介面,這裡定義了兩個介面——模型載入和提取關鍵點:

int loadmodel(const char* root_path);

int extractkeypoints(const cv::mat& img_face, std::vector* keypoints);

按照c++八大設計原則有:需要封裝變化點,使用封裝來建立物件之間的分界層,讓設計者在一側進行修改,而不會對另一側產生不良影響,從而實現層次間的松耦合,這裡學習了seetaface專案中使用impl類進行隔離,具體實現部分在類impl中:    

class pfld::impl 

~impl()

int loadmodel(const char* root_path);

int extractkeypoints(const cv::mat& img_face, std::vector* keypoints);

std::shared_ptrlandmarker_;

const int inputsize_ = 96;

int device_;

int precision_;

int power_;

int memory_;

mnn::session* session_ = nullptr;

mnn::tensor* input_tensor_ = nullptr;

bool initialized_;

};

同時將裡面一些變數命名進行重構,就可以獲知乙個比較清晰完整的mnn使用方法,對於介面loadmodel有:

int pfld::impl::loadmodel(const char* root_path) ;

input_tensor_ = mnn::tensor::create(dims, null, mnn::tensor::tensorflow);

initialized_ = true;

return 0;

}

這裡主要做的工作是模型載入及一些引數初始化,相對於原始版本的blazeface**,這裡將輸入tensor建立放在了loadmodel裡面,避免每次推理都需要為input_tensor_分配記憶體,對於具體推理extractkeypoints有:

int pfld::impl::extractkeypoints(const cv::mat& img_face, std::vector* keypoints) 

if (img_face.empty())

// image prepocess

cv::mat face_cpy = img_face.clone();

int width = face_cpy.cols;

int height = face_cpy.rows;

float scale_x = static_cast(width) / inputsize_;

float scale_y = static_cast(height) / inputsize_;

cv::mat face_resized;

cv::resize(face_cpy, face_resized, cv::size(inputsize_, inputsize_));

face_resized.convertto(face_resized, cv_32fc3);

face_resized = (face_resized - 123.0f) / 58.0f;

auto tensor_data = input_tensor_->host();

auto tensor_size = input_tensor_->size();

::memcpy(tensor_data, face_resized.data, tensor_size);

auto input_tensor = landmarker_->getsessioninput(session_, nullptr);

input_tensor->copyfromhosttensor(input_tensor_);

landmarker_->runsession(session_);

// get output

std::string output_tensor_name0 = "conv5_fwd";

mnn::tensor* tensor_landmarks = landmarker_->getsessionoutput(session_, output_tensor_name0.c_str());

mnn::tensor tensor_landmarks_host(tensor_landmarks, tensor_landmarks->getdimensiontype());

tensor_landmarks->copytohosttensor(&tensor_landmarks_host);

std::cout << "batch: " << tensor_landmarks->batch() << std::endl

<< "channels: " << tensor_landmarks->channel() << std::endl

<< "height: " << tensor_landmarks->height() << std::endl

<< "width: " << tensor_landmarks->width() << std::endl

<< "type: " << tensor_landmarks->getdimensiontype() << std::endl;

auto landmarks_dataptr = tensor_landmarks_host.host();

int num_of_points = 98;

for (int i = 0; i < num_of_points; ++i)

std::cout << "end extract keypoints." << std::endl;

return 0;

}

主要包括對輸入預處理(去均值、歸一化),設定模型輸入,模型前向推理及獲取輸出tensor幾個步驟。

MNN框架學習(三) 記憶體管理

不知道是不是借鑑了ncnn的 感覺差不多,具體可以參考大佬對於ncnn記憶體管理 的解析,具體見參考資料 1 講的非常清楚,這裡只是做乙個學習筆記。具體 為source core mnnmemoryutils.c檔案 使用malloc函式來進行記憶體分配 傳入待分配記憶體大小,及對齊記憶體大小 vo...

MNN 靜態庫的編譯及使用

編譯步驟 1 配置 android ndk 環境變數 android ndk 2 cd path to mnn 3 schema generate.sh 4 cd project android 編譯動態庫 armeabi v7a mkdir build 32 cd build 32 build 3...

WIN7 VS2015 編譯 阿里MNN框架

2019 12 31 今天看到更新了0.2.16,嘗試了下,順暢多了,mnnconvert.exe 也能順利執行。更簡單的流程 2 vs2015x64命令符下,轉mnn 0.2.1.6路徑 3 powershell executionpolicy bypass schema generate.ps1...