按照我們對乙個模型推理過程的理解,一般模型推理過程都分為兩個部分,第乙個部分為模型載入和引數初始化,第二個部分為模型前向推理及一些後處理,得到檢測或識別的結果。
第一步將**分解成兩個部分:模型載入和前向推理。
第乙個部分**:
// 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...