当前位置: 首页 > 工具软件 > ncnn > 使用案例 >

ncnn

柳奇希
2023-12-01

1.图像预处理 ncnn::Mat

1.1 from_pixels_resize() 生成目标尺寸大小的网络输入Mat mat_pixel.cpp
双线性插值图像形变 resize_bilinear_c1/c2/c3/4 1通道/2通道/3通道/4通道 图像变形算法 mat_pixel_resize.cpp
像素图像 转换成ncnn::Mat Mat::from_pixels() >>> 不同类型 from_rgb() 像素数据指针rgb间隔 依次赋值给Mat的三个通道的指针
mat_pixel.cpp
1.2 substract_mean_normalize() 去均值并归一化图像 mat.cpp
有均值参数
创建 偏置层 ncnn::create_layer(ncnn::LayerType::Bias); 载入层参数 op->load_param(pd); 3通道
载入层权重数据 op->load_model(ncnn::ModelBinFromMatArray(weights)); -均值参数
运行层 op->forward_inplace(*this);
有归一化参数
创建 尺度层 ncnn::create_layer(ncnn::LayerType::Scale); 载入层参数 op->load_param(pd); 3通道
载入层权重数据 op->load_model(ncnn::ModelBinFromMatArray(weights)); 尺度参数
运行层 op->forward_inplace(*this);
有均值和归一化参数
创建 尺度层 ncnn::create_layer(ncnn::LayerType::Scale); 载入层参数 op->load_param(pd); 3通道
载入层权重数据 op->load_model(ncnn::ModelBinFromMatArray(weights)); -均值参数 和 尺度参数
运行层 op->forward_inplace(*this);

2.模型解析 ncnn::Net

2.1 Net::load_param 载入网络参数文件 proto net.cpp
文件头魔术数(版本?) 层类型 层名字 创建层 create_layer()/ net::create_custom_layer() 层输入blob数量 输出blob数量
读取输入blob 与层挂钩; 读取输出blob与层挂钩;解析层特殊参数(参数字典) paramDict::load_param(fp); 按照 id=参数/参数数组 解析
每一层 的 特殊参数不一样 https://github.com/Tencent/ncnn/wiki/operation-param-weight-table
层载入解析得到的层特殊参数 layer->load_param(pd) 每一层特有的参数

2.2 Net::load_model 载入网络模型文件 bin 权重数据 net.cpp
1.创建 ModelBinFromStdio 对象 提供载入参数的接口函数 ModelBinFromStdio::load() src/modelbin.cpp
根据 权重数据开始的一个四字节数据类型参数(float32/float16/int8等) 和 指定的参数数量 读取数据到 Mat 并返回Mat
2.根据load_param 获取到的网络层信息 便利每一层 载入每一层的模型数据 layer->load_model() 每一层特有函数
3.部分层需要 根据层实际参数 调整运行流水线 layer->create_pipeline 例如卷积层和全连接层
4.量化的网络需要融合 Net::fuse_network()

3.网络运行 ncnn::Extractor

3.1 创建网络提取器 Extractor Net::create_extractor 提供设置网络输入 获取网络输出 设置网络运行线程参数的接口
3.2 设置线程参数 Extractor::set_num_threads 设置网络输入 Extractor::input
3.3 提取网络输出 Extractor::extract 运行网络前向运行函数 net->forward_layer
会根据层类型(单输入单输出/其他) blob类型(可本地修改(在输入直接修改)/不可本地修改)执行每一次的前向运行函数
当输入blob为空时,会递归调用 网络前向运行函数 net->forward_layer 获取前置层的输出blob

示例:

`#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "net.h"

int main()
{
    cv::Mat img = cv::imread("image.ppm", CV_LOAD_IMAGE_GRAYSCALE);
    int w = img.cols;
    int h = img.rows;

    // subtract 128, norm to -1 ~ 1
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(img.data, ncnn::Mat::PIXEL_GRAY, w, h, 60, 60);
    float mean[1] = { 128.f };
    float norm[1] = { 1/128.f };
    in.substract_mean_normalize(mean, norm);

    ncnn::Net net;
    net.load_param("model.param");
    net.load_model("model.bin");

    ncnn::Extractor ex = net.create_extractor();
    ex.set_light_mode(true);
    ex.set_num_threads(4);

    ex.input("data", in);

    ncnn::Mat feat;
    ex.extract("output", feat);

    return 0;
}

ncnn::Extractor还可以设置一个轻模式省内存 即set_light_mode(true),原理是net中每个layer都会产生blob,除了最后的结果和多分支中间结果,大部分blob都不值得保留,开启轻模式可以在运算后自动回收,省下内存。但需要注意的是,一旦开启这个模式,我们就不能获得中间层的特征值了,因为中间层的内存在获得最终结果之前都被回收掉了。

先创建了ncnn::net,然后我们调用的ncnn::Extractor作为运算实例,因此运算实例是不受net限制的。换句话说,虽然我们只有一个net,但我们可以开多个ncnn::Extractor,这些实例都是单独完成特定网络的推理,互不影响

参考:官方wiki:https://github.com/Tencent/ncnn/wiki
官方example readme:https://github.com/Ewenwan/MVision/tree/master/CNN/HighPerformanceComputing/example
https://zhuanlan.zhihu.com/p/338121531?utm_source=qq&utm_medium=social&utm_oi=872955404320141312

相关阅读

相关文章

相关问答