ncnn c++测试
在模型转换完成后,紧接着就要真正在移动端上运行啦,不过在正式放到嵌入式设备上运行时(这一步也不属于我做),还是在PC上拿c++测试一下,确认没问题了才能说明模型的正确性
ncnn安装
先使用git命令拉取ncnn源码
git clone https://github.com/Tencent/ncnn
对ncnn进行编译、安装
cd ncnn
mkdir build && cd build
cmake ..
make -j
make install
执行完毕后,在$ncnn_dir/build/install/文件夹下会产生安装文件
- $ncnn_dir/build/install/lib/libncnn.a为链接库
- $ncnn_dir/build/install/include/为头文件包含路径
Cmake配置工程
在CMakeLists.txt中,加入如下代码,来添加ncnn的链接库和头文件路径
# 设置ncnn的链接库和头文件路径
include_directories($ncnn_dir/build/install/include/)
target_link_libraries($ncnn_dir/build/install/lib/libncnn.a)
由于本人模型输入为图片,因此还需要配置OpenCV,这里就不啰嗦了,直接参考如下链接安装:
Ubuntu16.04安装opencv for c++:https://blog.csdn.net/qq_33591712/article/details/83279982
其他Cmake基本配置,比如工程名、增加需要输出的可执行文件、链接OpenCV等等,在这里就不啰嗦了,参考如下链接
https://www.cnblogs.com/lidabo/p/7359422.html
ncnn模型文件net.params,net.bin文件解析
- params包含了网络结构
- bin包含了网络参数
- 对于使用该模型,最重要的是从这个网络结构中找到你要的输入输出节点名称,下面是一个网络结构的例子
7767517
60 63
Input data 0 1 data 0=3 1=180 2=550
Convolution ConvNd_1 1 1 data ConvNd_1 0=32 1=3 2=1 3=2 4=1 5=0 6=864
........................
....... 中间省略.........
........................
Dropout Dropout_2 1 1 Addmm_1 Dropout_2
InnerProduct Addmm_2 1 1 Dropout_2 Addmm_2 0=130 1=1 2=33280
Softmax Softmax_1 1 1 Addmm_2 Softmax_1 0=0
- 其中第一行的7767517是ncnn magic numger(幻数),
- 第二行的60 63。60为layer number(网络层数),63为blob number(参数块数)
- 剩余的为Input data 0 1 data 0=3 1=180 2=550,我们只关心中间的
data
,Softmax_1
等,这是网络的节点名称,不论是输入还是输出节点,都必须在代码中指定输入输出节点名称
C++代码
- 包含ncnn头文件
#include "net.h"
- 读取输入图片
string img_path = "$IMG_PATH";
cv::Mat img = cv::imread(img_path, CV_LOAD_IMAGE_COLOR);
cv::Mat img2;
//这里一定要检查你图片的大小和训练网络时的图片输入大小,要一样,否则到全连接层时会出现矩阵维数无法相乘
//input_width:网络输入图片宽度
//input_height:网络输入图片高度
cv::resize(img, img2, cv::Size(input_width, input_height));
- 加载ncnn模型和参数
// 加载模型和参数
ncnn::Net DeepNet;
DeepNet.load_param("$param_path");
DeepNet.load_model("$bin_path");
- 将读取的输入图片转化为ncnn::Mat input数据类型,通过ncnn自带的从图像像素转换函数
ncnn::Mat input = ncnn::Mat::from_pixels(img2.data, ncnn::Mat::PIXEL_BGR, img2.cols, img2.rows);
- 经输入Mat送入网络并定义输出节点获取结果
ncnn::Extractor extractor = DeepNet.create_extractor();
// 将'data'节点名称和ncnn::Mat input对应起来
extractor.input("data", input);
// 将'Addmm_1'节点名称和ncnn::Mat output对应起来
ncnn::Mat output;
extractor.extract("Addmm_1", output);
- 将ncnn::Mat类型的输出结果转化为std::vector<float>
std::vector<float> & cls_scores
// 网络输出为output,将output转化到cls_scores中输出
cls_scores.resize(output.cstep);
for(int j=0; j<output.cstep; j++)
{
const float* prob = (float*)output.data + output.c * j;
cls_scores[j] = prob[0];
}
//Debug打印输出
for(int j = 0;j < cls_scores.size();j++)
{
cout << cls_scores[j] ;
}
调试trick
- 对于输出节点,可以使用如下代码来打印节点的通道数和长宽,来判断该节点是否为你需要的那个输出节点(比如你要的输出为15121,那么你可以根据你自己的网络结构大概定位到哪部分,然后挨个使用这种方法打印输出,观察输出节点的维度来判断是否为你需要的输出节点)
cout << "output.c: " << output.c << endl;
cout << "output.w: " << output.w << endl;
cout << "output.h: " << output.h << endl;
网友评论