树莓派下部署NCNN
安装树莓派
树莓派的烧录,网上的教程有很多,这里就不赘述了,比如该博客树莓派(4B)镜像烧录及环境配置保姆级教程
安装依赖
首先需要安装下git和cmake依赖、其他依赖。
sudo apt-get install git cmake
sudo apt-get install -y gfortran
sudo apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install -y libgflags-dev libgoogle-glog-dev liblmdb-dev libatlas-base-dev
这里有可能下载不成功,报503的错误,那需要换apt-get的源:
选输入下面命令查看你的树莓派对应的系统版本,比如“buster”
lsb_release -a
之后执行脚本
sudo vim /etc/apt/sources.list
将里面的两行注释#,加上下面两句换成清华源
deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main contrib non-free rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main contrib non-free rpi
主要是这个网址换掉原来的那个网址,其他保持不变
最后更新一下
sudo apt update
有可能所有些没更新,要你用旧的,但是问题不大.
只要上面的依赖都能成功安装,那就没问题了
编译ncnn
下载ncnn源码
https://github.com/Tencent/ncnn.git
cd ncnn
编译源码,在ncnn目录下
mkdir build
cd build
cmake ../
make -j4
make install
部署resnet18
这里只要需要用到精简化后的onnx文件,可以参考我的上一篇博客在windows下使用ncnn部署加速神经网络(以resnet18为例)_TTao9的博客-CSDN博客里面会教怎么导出精简化后的onnx文件,之后跟上一篇博客一样,进入编译后的build文件夹下的\install\bin下,其实可以单独吧这个文件夹下的exe都复制到另外一个文件夹,制作成ncnn转换工具
之后打开ncnn的转换工具,具体路径在编译完的ncnn工程目录下的\install\bin下,其实可以单独吧这个文件夹下的exe都复制到另外一个文件夹,制作成ncnn转换工具。将onnx文件复制到该文件夹下,运行
./onnx2ncnn resnet18_imagenet_sim.onnx resnet18_imagenet_sim.param resnet18_imagenet_sim.bin
运行之后文件夹下会多出resnet18_imagenet_sim.param和resnet18_imagenet_sim.bin文件,之后将这两个文件放到ncnn工程下的ncnn/examples文件夹下,并且新建一个resnet18_imagenet_sim.cpp,输入以下的代码:
#include "net.h"
#include "benchmark.h"
#include <algorithm>
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#endif
#include <stdio.h>
#include <vector>
static int detect_resnet18(const cv::Mat& bgr, std::vector<float>& cls_scores)
{
ncnn::Net resnet18;
resnet18.opt.use_vulkan_compute = true;
// the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
if (resnet18.load_param("resnet18_imagenet_sim.param"))
exit(-1);
if (resnet18.load_model("resnet18_imagenet_sim.bin"))
exit(-1);
ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 256, 256);
double dStart = ncnn::get_current_time();
// const float mean_vals[3] = {104.f, 117.f, 123.f};
// in.substract_mean_normalize(mean_vals, 0);
const float mean_vals[3] = { 0.485f * 255.f, 0.456f * 255.f, 0.406f * 255.f };
const float norm_vals[3] = { 1 / 0.229f / 255.f, 1 / 0.224f / 255.f, 1 / 0.225f / 255.f };
in.substract_mean_normalize(mean_vals, norm_vals);
ncnn::Extractor ex = resnet18.create_extractor();
ex.set_light_mode(true);
// ex.input("data", in);
ex.input("input", in);
ncnn::Mat out;
// ex.extract("prob", out);
ex.extract("output", out);
printf("output_size: %d, %d, %d \n", out.w, out.h, out.c);
out = out.reshape(out.w * out.h * out.c);
cls_scores.resize(out.w);
for (int j = 0; j < out.w; j++)
{
cls_scores[j] = out[j];
}
int top_class = 0;
float max_score = 0.f;
for (size_t i = 0; i < cls_scores.size(); i++)
{
float s = cls_scores[i];
if (s > max_score)
{
top_class = i;
max_score = s;
}
}
double dEnd = ncnn::get_current_time();
printf("%d score: %f spend time: %.2f ms\n", top_class, max_score, (dEnd - dStart));
return 0;
}
static int print_topk(const std::vector<float>& cls_scores, int topk)
{
// partial sort topk with index
int size = cls_scores.size();
std::vector<std::pair<float, int> > vec;
vec.resize(size);
for (int i = 0; i < size; i++)
{
vec[i] = std::make_pair(cls_scores[i], i);
}
std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(),
std::greater<std::pair<float, int> >());
// print topk and score
for (int i = 0; i < topk; i++)
{
float score = vec[i].first;
int index = vec[i].second;
fprintf(stderr, "%d = %f\n", index, score);
}
return 0;
}
int main(int argc, char** argv)
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
return -1;
}
const char* imagepath = argv[1];
cv::Mat m = cv::imread(imagepath, 1);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}
std::vector<float> cls_scores;
detect_resnet18(m, cls_scores);
print_topk(cls_scores, 5);
return 0;
}
这个cpp是模仿ncnn给的示例写的,所以在example文件夹下的CMakeLists.txt还需要将这个文件链接到cmake中,添加这一行就行
进入到ncnn下的build文件夹,在终端输入
cmake -DCMAKE_BUILD_TYPE=Release -DNCNN_VULKAN=OFF -DNCNN_BUILD_EXAMPLES=ON ..
make -j4
这里的DNCNN_VULKAN是关闭vulkan,因为树莓派的vulkan驱动不稳定,不一定的用上加速,DNCNN_BUILD_EXAMPLES是对例子进行编译,默认是OFF,make -j4是编译的进程,可以按照自己的树莓派配置来修改大小
编译后会在ncnn/build_example/examples下生成resnet18_imagenet_sim可执行文件,
将这个可执行文件连同上面的resnet18_imagenet_sim.param和resnet18_imagenet_sim.bin文件和待测试的图片,复制到同一个文件夹,
终端出现
就是成功了。