NCNN推理

1.前言

        ncnn 是一个高性能的神经网络前向计算框架,专门针对移动设备和嵌入式设备设计。它由腾讯优图实验室开发,旨在提供高效的神经网络推理能力,特别是在资源受限的环境中,如智能手机和嵌入式系统。

        ncnn 被广泛应用于移动端和嵌入式设备上的各种深度学习应用,包括但不限于:图像分类/目标检测/语义分割/人脸识别/图像生成与处理

2.NCNN的CMakeLists.txt编写

        ncnn的头文件,链接文件,静态链接库根据源码编译以后的路径修改(如果是编译好的,直接指定路径)

# ncnn的头文件
include_directories(/home/ubuntu/ncnn/build/install/include/ncnn)
# ncnn的链接文件
link_directories(/home/ubuntu/ncnn/build/install/lib)
# 生成可执行文件
add_executable(ncnnTest main.cpp)
# 链接ncnn静态链接库
target_link_libraries(ncnnTest ncnn ${OpenCV_LIBS} /home/ubuntu/ncnn/build/install/lib/libncnn.a)

 3.模型推理

        3.1 导入标准库

//包括 OpenCV 的图像处理和显示功能,以及用于加载神经网络模型的头文件和标准输入输出流。
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include "net.h"
#include "iostream"

         3.2 数据前处理

ncnn::Mat::from_pixels_resize(original_img.data, ncnn::Mat::PIXEL_BGR2RGB, w, h, w, h);
const float mean_vals[3] = {180.795f,97.155f, 57.12f};
const float norm_vals[3] = {0.127f, 0.079f, 0.043f};
in.substract_mean_normalize(mean_vals, norm_vals);

        (1)关于减去均值和除方差的操作,pytorch里面是(0~1范围)

            mean = (0.709, 0.381, 0.224)
            std = (0.127, 0.079, 0.043)

            ncnn的处理中,由于opencv读取的是uint8的格式,所以mean需要乘以255,std没有变化

        (2)  ncnn的数据枚举,根据自己的实际使用,本例使用的是PIXEL_BGR2RGB

               其中的四个shape参数中:

               1)第一个w, h:input数据的宽和高 

                2)第二个w, h:可以实现resize的功能,由于本例模型不用resize(例如448×448),所以直接重复填写,但是这4个参数必须要填写

enum PixelType
{
    PIXEL_CONVERT_SHIFT = 16,
    PIXEL_FORMAT_MASK = 0x0000ffff,
    PIXEL_CONVERT_MASK = 0xffff0000,

    PIXEL_RGB = 1,
    PIXEL_BGR = 2,
    PIXEL_GRAY = 3,
    PIXEL_RGBA = 4,
    PIXEL_BGRA = 5,

    PIXEL_RGB2BGR = PIXEL_RGB | (PIXEL_BGR << PIXEL_CONVERT_SHIFT),
    PIXEL_RGB2GRAY = PIXEL_RGB | (PIXEL_GRAY << PIXEL_CONVERT_SHIFT),
    PIXEL_RGB2RGBA = PIXEL_RGB | (PIXEL_RGBA << PIXEL_CONVERT_SHIFT),
    PIXEL_RGB2BGRA = PIXEL_RGB | (PIXEL_BGRA << PIXEL_CONVERT_SHIFT),

    PIXEL_BGR2RGB = PIXEL_BGR | (PIXEL_RGB << PIXEL_CONVERT_SHIFT),
    PIXEL_BGR2GRAY = PIXEL_BGR | (PIXEL_GRAY << PIXEL_CONVERT_SHIFT),
    PIXEL_BGR2RGBA = PIXEL_BGR | (PIXEL_RGBA << PIXEL_CONVERT_SHIFT),
    PIXEL_BGR2BGRA = PIXEL_BGR | (PIXEL_BGRA << PIXEL_CONVERT_SHIFT),

    PIXEL_GRAY2RGB = PIXEL_GRAY | (PIXEL_RGB << PIXEL_CONVERT_SHIFT),
    PIXEL_GRAY2BGR = PIXEL_GRAY | (PIXEL_BGR << PIXEL_CONVERT_SHIFT),
    PIXEL_GRAY2RGBA = PIXEL_GRAY | (PIXEL_RGBA << PIXEL_CONVERT_SHIFT),
    PIXEL_GRAY2BGRA = PIXEL_GRAY | (PIXEL_BGRA << PIXEL_CONVERT_SHIFT),

    PIXEL_RGBA2RGB = PIXEL_RGBA | (PIXEL_RGB << PIXEL_CONVERT_SHIFT),
    PIXEL_RGBA2BGR = PIXEL_RGBA | (PIXEL_BGR << PIXEL_CONVERT_SHIFT),
    PIXEL_RGBA2GRAY = PIXEL_RGBA | (PIXEL_GRAY << PIXEL_CONVERT_SHIFT),
    PIXEL_RGBA2BGRA = PIXEL_RGBA | (PIXEL_BGRA << PIXEL_CONVERT_SHIFT),

    PIXEL_BGRA2RGB = PIXEL_BGRA | (PIXEL_RGB << PIXEL_CONVERT_SHIFT),
    PIXEL_BGRA2BGR = PIXEL_BGRA | (PIXEL_BGR << PIXEL_CONVERT_SHIFT),
    PIXEL_BGRA2GRAY = PIXEL_BGRA | (PIXEL_GRAY << PIXEL_CONVERT_SHIFT),
    PIXEL_BGRA2RGBA = PIXEL_BGRA | (PIXEL_RGBA << PIXEL_CONVERT_SHIFT),
};

const char* img_path = "/home/ubuntu/ncnn_test/img/01_test.tif";
const char* roi_mask_path = "/home/ubuntu/ncnn_test/img/01_test_mask.png";
const char* param_path =  "/home/ubuntu/ncnn_test/model/unet.param";
const char* bin_path =  "/home/ubuntu/ncnn_test/model/unet.bin";



// 加载灰度格式的ROI掩码图像
cv::Mat roi_img =  imread(roi_mask_path,  cv::IMREAD_GRAYSCALE);
if (roi_img.empty()) {
    cout << "Image not found: " << roi_mask_path << endl;
    return -1;
}


// 加载原始彩色图像,如果加载失败则输出错误信息并退出程序
cv::Mat original_img =  cv::imread(img_path);
if (original_img.empty()) {
    cout << "Image not found: " << img_path << endl;
    return -1;
}


// 将原始图像数据转换为 ncnn::Mat 格式,并进行均值减法和归一化处理, in为输入数据
int w = original_img.cols;
int h = original_img.rows;
ncnn::Mat in = ncnn::Mat::from_pixels_resize(original_img.data, ncnn::Mat::PIXEL_BGR2RGB, w, h, w, h);
const float mean_vals[3] = {180.795f,97.155f, 57.12f};
const float norm_vals[3] = {0.127f, 0.079f, 0.043f};
in.substract_mean_normalize(mean_vals, norm_vals);


        3.3 模型推理

                 输入数据in是前处理的数据,out是模型输出数据,输入节点和输出节点名根据onnx模型可以查看(onnx转ncnn时不会修改节点名)

// 创建网络提取器对象并进行推理
ncnn::Extractor ex = net.create_extractor();
ncnn::Mat out;
ex.input("input.1", in);
ex.extract("437", out);
// 输出的nchw
// cout << out.d << " " << out.c << " " << out.h << " " <<  out.w <<  endl;

// 遍历输出数据
/*
for (int c = 0; c < output_data.c; c++) {
    const float* data = output_data.channel(c);
    for (int h = 0; h < output_data.h; h++) {
        for (int w = 0; w < output_data.w; w++) {
        float value = data[h * output_data.w + w];
        std::cout << "Output [" << c << "][" << h << "][" << w << "] = " << value << std::endl;
        }
    }
}else {
std::cerr << "Error extracting output" << std::endl;
}
*/

  3.4 数据后处理

        根据自己的后处理修改

	ncnn::Mat out_flattened = out.reshape(out.w * out.h * out.c);
    float* output_data = (float*)out_flattened.data;
    
    cv::Mat result_img = cv::Mat::zeros(roi_img.rows, roi_img.cols, CV_8UC1);
    int index = 0;
    for (int row = 0; row < roi_img.rows; row++) {
        for (int col = 0; col < roi_img.cols; col++) {
            float channel_1 = *(output_data + index);
            float channel_2 = *(output_data + index + roi_img.rows * roi_img.cols);
            uchar roi_pixel = roi_img.at<uchar>(row, col);
            // 如果第一个通道值小于第二个通道值,并且 ROI 像素不是黑色
            if (channel_1 < channel_2 && roi_pixel != 0) {
                result_img.at<uchar>(row, col) = 255;  // 设置为白色
            } else {
                result_img.at<uchar>(row, col) = 0;    // 设置为黑色
            }
            index++;
        }
    }

4.完整代码和推理结果

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include "net.h"
#include "iostream"

using namespace std;




int main()
{
    // 定义输入图像路径、ROI掩码路径、ncnn模型参数路径和二进制权重文件路径。
    const char* img_path = "/home/ubuntu/ncnn_test/img/01_test.tif";
    const char* roi_mask_path = "/home/ubuntu/ncnn_test/img/01_test_mask.png";
    const char* param_path =  "/home/ubuntu/ncnn_test/model/unet.param";
    const char* bin_path =  "/home/ubuntu/ncnn_test/model/unet.bin";


    // 加载灰度格式的ROI掩码图像
    cv::Mat roi_img =  imread(roi_mask_path,  cv::IMREAD_GRAYSCALE);
    if (roi_img.empty()) {
        cout << "Image not found: " << roi_mask_path << endl;
        return -1;
    }


    // 加载原始彩色图像,如果加载失败则输出错误信息并退出程序
    cv::Mat original_img =  cv::imread(img_path);
    if (original_img.empty()) {
        cout << "Image not found: " << img_path << endl;
        return -1;
    }


	int w = original_img.cols;
	int h = original_img.rows;

    // 将原始图像数据转换为 ncnn::Mat 格式,并进行均值减法和归一化处理, in为输入数据
	ncnn::Mat in = ncnn::Mat::from_pixels_resize(original_img.data, ncnn::Mat::PIXEL_BGR2RGB, w, h, w, h);
    const float mean_vals[3] = {180.795f,97.155f, 57.12f};
    const float norm_vals[3] = {0.127f, 0.079f, 0.043f};
    in.substract_mean_normalize(mean_vals, norm_vals);


	ncnn::Net net;
	// 加载模型参数
	if (net.load_param(param_path)) {
	fprintf(stderr, "Load param failed.\n");
	return -1;
	}

    // 加载模型二进制权重
	if (net.load_model(bin_path)) {
	fprintf(stderr, "Load model failed.\n");
	return -1;
	}

    // 创建网络提取器对象并进行推理
	ncnn::Extractor ex = net.create_extractor();
	ncnn::Mat out;
	ex.input("input.1", in);
	ex.extract("437", out);

	ncnn::Mat out_flattened = out.reshape(out.w * out.h * out.c);
    float* output_data = (float*)out_flattened.data;

    cv::Mat result_img = cv::Mat::zeros(roi_img.rows, roi_img.cols, CV_8UC1);
    int index = 0;
    for (int row = 0; row < roi_img.rows; row++) {
        for (int col = 0; col < roi_img.cols; col++) {
            float channel_1 = *(output_data + index);
            float channel_2 = *(output_data + index + roi_img.rows * roi_img.cols);
            uchar roi_pixel = roi_img.at<uchar>(row, col);
            // 如果第一个通道值小于第二个通道值,并且 ROI 像素不是黑色
            if (channel_1 < channel_2 && roi_pixel != 0) {
                result_img.at<uchar>(row, col) = 255;  // 设置为白色
            } else {
                result_img.at<uchar>(row, col) = 0;    // 设置为黑色
            }
            index++;
        }
    }


    // 保存结果图像
    cv::imwrite("result.jpg", result_img);


	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆珝

您的打赏是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值