caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd

HighPerformanceComputing

高性能计算(High performance computing, 缩写HPC) 指通常使用很多处理器(作为单个机器的一部分) 或者某一集群中组织的几台计算机(作为单个计 算资源操作)的计算系统和环境。 有许多类型的HPC 系统,其范围从标准计算机的大型集群,到高度专用的硬件。 大多数基于集群的HPC系统使用高性能网络互连,比如那些来自 InfiniBand 或 Myrinet 的网络互连。 基本的网络拓扑和组织可以使用一个简单的总线拓扑, 在性能很高的环境中,网状网络系统在主机之间提供较短的潜伏期, 所以可改善总体网络性能和传输速率。

博文末尾支持二维码赞赏哦 _
浮点运算和代码优化, 并行计算, Optimizer软件
卷积计算优化
目前,卷积的计算大多采用间接计算的方式,主要有以下三种实现方式:1、im2col + GEMM。 caffe等很多框架中都使用了这种计算方式, 原因是将问题转化为矩阵乘法后可以方便的使用很多矩阵运算库(如MKL、openblas、Eigen等)。

openblas
GEMM 普通矩阵乘法(General Matrix Multiplication)
2、FFT变换。 时域卷积等于频域相乘,因此可将问题转化为简单的乘法问题。 3、Winograd。 这种不太熟悉,据说在GPU上效率更高。 NNPACK就是FFT和Winograd方法的结合。上面三种方法执行效率都还不错,但对内存占用比较高,因为需要存储中间结果或者临时辅助变量。1、Strassen 算法: 分析 CNN 的线性代数特性,增加加法减少乘法, 这样降低了卷积运算的计算的复杂度(o(n^3) -> o(n^2.81)), 但是这种方法不适合在硬件里面使用,这里就不做详细的介绍了。2、 MEC: 一种内存利用率高且速度较快的卷积计算方法

MEC: Memory-efficient Convolution for Deep Neural Network 论文
博客解析
openblas GEMM 矩阵乘法优化 caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

最原始3个for循环 (矩阵比较小的时候,速度还能快一些,当矩阵大了的时候,一定会跌下去,cache缓存问题):
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

矩阵分块,块复用,减少仿存,相当于减少内存访问:
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

操作寄存器,不是操作内存:
我可以申请一堆C 00,01这样的寄存器变量,在C语言中是register double,还有矩阵A的部分,也用寄存器变量。
当然B还是之前的方式,最后再写回C里面。
只是我们引入了寄存器变量,让更多的数据保存到寄存器里,而不是放到cache缓存里,来减轻cache的压力.
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

B矩阵仿存,使用指针访问,
一开始先把对应的指针位置指好,每次计算的时候只要指针连续移动就好,而不是每次读一个位置重新算一遍,这样速度就会快一些。
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

最里层循环展开:
在最里层循环,是不是可以展开成4次,在做这个的时候,我们可以降低整个循环这部分的开销,而且让它流水的情况更好。
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

通过使用寄存器变量,使用了指针,在做了一定的底层循环展开之后,达到了红色线的性能:
caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

之后可以使用更大的分块,在进行寄存器,指针,展开优化。
在深度神经网络中 特指提高卷积计算方式的方法
0、小米 mace

代码
Mobile AI Compute Engine (MACE) 是一个专为移动端异构计算平台优化的神经网络计算框架。1、OpenVINO Intel推出OpenVINO工具包,将计算机视觉带到物联网终端 OpenVINO(开放的视觉推理和神经网络优化)工具包 使开发人员能够在云上(如TensorFlow,MXNet和Caffe等流行款框架)构建和训练人工智能模型, 并将其部署到各种产品中。 Windows* Linux* (supports Ubuntu*, CentOS*, and Yocto Project*) Linux for FPGA 2、腾讯NCNN框架入门到应用

代码
3、FeatherCNN

代码
4、Tengine 高性能神经网络推理引擎

代码
5、百度MDL

代码
6、九言科技 绝影(Prestissimo)

代码
7、Google量化方法 r=S(q-Z)tfliteTensorFlow Lite

代码
8、英伟达 TensorRT , NVIDIA TensorRT是一种高性能神经网络推理(Inference)引擎

代码
英伟达 CUDA 和 TensorRT 代码实验
深度学习框架的并行优化方法小结
一 、 ncnn使用 ncnn_wiki 指南
在Ubuntu上安装使用NCNN 1. 下载编译源码
git clone https://github.com/Tencent/ncnn.git 下载完成后,需要对源码进行编译 修改CMakeLists.txt 文件 打开 一些文件的编译开关############################################## add_subdirectory(examples) # add_subdirectory(benchmark) add_subdirectory(src) if(NOT ANDROID AND NOT IOS) add_subdirectory(tools) endif()开始编译: cd ncnn mkdir build && cd build cmake .. make -j make install执行完毕后我们可以看到: Install the project... -- Install configuration: "release" -- Installing: /home/ruyiwei/code/ncnn/build/install/lib/libncnn.a -- Installing: /home/ruyiwei/code/ncnn/build/install/include/blob.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/cpu.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/layer.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/mat.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/net.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/opencv.h -- Installing: /home/ruyiwei/code/ncnn/build/install/include/platform.h 查看生成了什么工具: 我们进入 ncnn/build/tools 目录下,如下所示, 我们可以看到已经生成了 ncnn2mem可执行文件,以及 caffe/caffe2ncnn 和 mxnet/mxnet2ncnn 可执行文件 caffe2ncnn的 作用是将caffe模型生成ncnn 模型 .prototxt >>> .param.caffemodel >>> .bin; mxnet2ncnn 的作用是将 mxnet模型生成ncnn 模型; ncnn2mem 是对ncnn模型进行加密。 drwxrwxr-x 6 wanyouwen wanyouwen40966月 21 00:13 ./ drwxrwxr-x 6 wanyouwen wanyouwen40966月 21 00:14 ../ drwxrwxr-x 3 wanyouwen wanyouwen40966月 21 00:13 caffe/ drwxrwxr-x 3 wanyouwen wanyouwen40966月 21 00:13 CMakeFiles/ -rw-rw-r-- 1 wanyouwen wanyouwen16066月 21 00:13 cmake_install.cmake -rw-rw-r-- 1 wanyouwen wanyouwen71416月 21 00:13 Makefile drwxrwxr-x 3 wanyouwen wanyouwen40966月 21 00:13 mxnet/ -rwxrwxr-x 1 wanyouwen wanyouwen 4775386月 21 00:13 ncnn2mem* drwxrwxr-x 3 wanyouwen wanyouwen40966月 21 00:13 onnx

tensorflow2ncnn
而默认会生成 一个静态库build/src/libncnn.a 和一些可执行文件: build/examples/squeezenet 分类模型 build/examples/fasterrcn检测模型 build/examples/ssd/ssdsqueezenet 检测模型 build/examples/ssd/ssdmobilenet检测模型 可以使用squeezenet进行测试,这里这是一个图像分类模型。 把模型和参数复制过来: cp ../../examples/squeezen* . 进行检测: ./squeezenet cat.jpg >>> 283 = 0.377605 281 = 0.247314 282 = 0.100278 这里只是输出了类别的编码,没有输出类别的字符串 需要修改examples/squeezenet.c文件

// Tencent is pleased to support the open source community by making ncnn available. // https://opensource.org/licenses/BSD-3-Clause#include #include #include #include #include #include // putText() #include "net.h"#include #include // 计时 long getTimeUsec() {struct timeval t; gettimeofday(&t,0); return (long)((long)t.tv_sec*1000*1000 + t.tv_usec); }static int detect_squeezenet(const cv::Mat& bgr, std::vector& cls_scores) { ncnn::Net squeezenet; // 前向模型 squeezenet.load_param("squeezenet_v1.1.param"); // 模型框架 squeezenet.load_model("squeezenet_v1.1.bin"); // 权重参数// ncnn 用自己的数据结构 Mat 来存放输入和输出数据 输入图像的数据要转换为 Mat,依需要减去均值和乘系数 // 图片变形 ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 227, 227); // 各个通道均值 const float mean_vals[3] = {104.f, 117.f, 123.f}; in.substract_mean_normalize(mean_vals, 0); // 图像减去均值归一化ncnn::Extractor ex = squeezenet.create_extractor(); ex.set_light_mode(true); // 模型 提取器 ex.input("data", in); // 执行前向网络,获得计算结果 ncnn::Mat out; ex.extract("prob", out); //提取 prob层的输出cls_scores.resize(out.w); for (int j=0; j& cls_scores, int topk, std::vector& index_result, std::vector score_result) { // partial sort topk with index int size = cls_scores.size(); // 结果维度 std::vector< std::pair > vec; vec.resize(size); for (unsigned int i=0; i; i++) {// 成对 值:id 这里id对于类别 vec[i] = std::make_pair(cls_scores[i], i); } // 排序 std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(), std::greater< std::pair >()); // print topk and score for (int i=0; i& labels) { FILE* fp = fopen(path.c_str(), "r"); //读取文件while(!feof(fp)) { char str_b[1024]; //先读取 1024个字符 fgets(str_b, 1024, fp); std::string str_block(str_b); //转换成 string 方便操作if(str_block.length() > 0) { for (unsigned int i = 0; i labels; load_labels("synset_words.txt", labels); std::vector cls_scores; long time = getTimeUsec(); detect_squeezenet(m, cls_scores); time = getTimeUsec() - time; printf("detection time: %ld ms\n",time/1000); std::vector index; std::vector score; print_topk(cls_scores, 3, index, score); for(unsigned int i = 0; i < index.size(); i++) { cv::putText(m, labels[index[i]], cv::Point(50, 50+30*i), CV_FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(0, 100, 200), 2, 8); }cv::imshow("result", m); cv::imwrite("test_result.jpg", m); cv::waitKey(0); return 0; }

把模型和参数复制过来: cp ../../examples/squeezen* . 以及类别列表文件: cp ../../examples/synset_words.txt . 进行检测: ./squeezenet cat.jpg

2. caffe网络模型转换为 ncnn模型 示例
caffe下Alexnet网络模型转换为NCNN模型
我们在测试的过程中需要 .caffemodel文件(模型参数文件)以及 deploy.prototxt文件(模型框架结构) , 所以我们再将caffe模型转换为NCNN模型的时候, 同样也需要 .caffemodel以及deploy.prototxt这两个文件,为了方便,我们使用AlexNet为例讲解。

a. 下载 caffe 模型和参数
alexnet 的 deploy.prototxt 可以在这里下载 https://github.com/BVLC/caffe/tree/master/models/bvlc_alexnet alexnet 的 .caffemodel 可以在这里下载 http://dl.caffe.berkeleyvision.org/bvlc_alexnet.caffemodel

b. 转换
由于NCNN提供的转换工具只支持转换新版的caffe模型, 所以我们需要利用caffe自带的工具将旧版的caffe模型转换为新版的caffe模型后, 再将新版本的模型转换为NCNN模型.旧版本caffe模型->新版本caffe模型->NCNN模型。

c. 旧版caffe模型转新版caffe模型
转换ncnn网络和模型 caffe 自带了工具可以把老版本的 caffe 网络和模型转换为新版(ncnn的工具只认识新版upgrade_net_proto_text [老prototxt] [新prototxt] upgrade_net_proto_binary [老caffemodel] [新caffemodel]模型框架转换: ~/code/ncnn/build/tools$ ~/caffe/build/tools/upgrade_net_proto_text deploy.prototxt new_deplpy.prototxt 模型权重文件转换: ~/code/ncnn/build/tools$ ~/caffe/build/tools/upgrade_net_proto_binary bvlc_alexnet.caffemodel new_bvlc_alexnet.caffemodel上面的命令需要根据自己的caffe位置进行修改执行后,就可以生成新的caffe模型.因为我们每次检测一张图片,所以要对新生成的deploy.prototxt进行修改:第一个 dim 设为 1 一次输入的图片数量 layer { name: "data" type: "Input" top: "data" input_param { shape: { dim: 1 dim: 3 dim: 227 dim: 227 } } }

d. 新版caffe模型转ncnn模型
./caffe/caffe2ncnn new_deplpy.prototxt new_bvlc_alexnet.caffemodel alexnet.param alexnet.bincaffe2ncnn的 作用是将caffe模型生成ncnn 模型 .prototxt >>> .param.caffemodel >>> .bin;执行上面命令后就可以生成NCNN模型需要的param 与bin 文件.

e. 对模型参数加密
./ncnn2mem alexnet.param alexnet.bin alexnet.id.h alexnet.mem.h 注意 alexnet.id.h alexnet.mem.h 为定义的文件名 头文件名 后面后生成 最后 alexnet.bi >>>alexnet.param.bin小 alexnet.id.h小 alexnet.mem.h较大

f. 模型载入
对于加密文件的读取也和原来不同,在源码中,非加密param读取方式为: ncnn::Net net; net.load_param("alexnet.param"); net.load_model("alexnet.bin"); 加密param.bin读取方式为: ncnn::Net net; net.load_param_bin("alexnet.param.bin"); net.load_model("alexnet.bin");

mobileNET 分类网络示例 caffe 模型参数下载
2. 模型转换
先caffe old转换到新版本下 ./../../tools/caffe_tools/upgrade_net_proto_text mobilenet_deploy.prototxt mobilenet_new.prototxt ./../../tools/caffe_tools/upgrade_net_proto_binary mobilenet.caffemodel mobilenet_new.caffemodel caffe toncnn ./../../tools/caffe/caffe2ncnn mobilenet_new.prototxt mobilenet_new.caffemodel mobilenet-ncnn.param mobilenet-ncnn.bin

4. 修改检测源文件 新建 ncnn/examples/mobilenet.cpp
// Tencent is pleased to support the open source community by making ncnn available. // https://opensource.org/licenses/BSD-3-Clause#include #include #include #include #include #include // putText() #include "net.h"#include #include // 计时 long getTimeUsec() {struct timeval t; gettimeofday(&t,0); return (long)((long)t.tv_sec*1000*1000 + t.tv_usec); }static int detect_mobilenet(const cv::Mat& bgr, std::vector& cls_scores) { ncnn::Net mobilenet; // 前向模型 mobilenet.load_param("mobilenet-ncnn.param"); // 模型框架 mobilenet.load_model("mobilenet-ncnn.bin"); // 权重参数 // 图片变形网络输入尺寸为 224*224 之前squeezenet为227 ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 224, 224); // 各个通道均值 const float mean_vals[3] = {104.f, 117.f, 123.f}; in.substract_mean_normalize(mean_vals, 0); // 图像减去均值归一化ncnn::Extractor ex = mobilenet.create_extractor(); ex.set_light_mode(true); // 模型 提取器 ex.input("data", in); //数据输入层ncnn::Mat out; ex.extract("prob", out); //提取 prob层的输出cls_scores.resize(out.w); for (int j=0; j& cls_scores, int topk, std::vector& index_result, std::vector score_result) { // partial sort topk with index int size = cls_scores.size(); // 结果维度 std::vector< std::pair > vec; vec.resize(size); for (unsigned int i=0; i; i++) {// 成对 值:id 这里id对于类别 vec[i] = std::make_pair(cls_scores[i], i); } // 排序 std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(), std::greater< std::pair >()); // print topk and score for (int i=0; i& labels) { FILE* fp = fopen(path.c_str(), "r"); //读取文件while(!feof(fp)) { char str_b[1024]; //先读取 1024个字符 fgets(str_b, 1024, fp); std::string str_block(str_b); //转换成 string 方便操作if(str_block.length() > 0) { for (unsigned int i = 0; i labels; load_labels("synset_words.txt", labels); std::vector cls_scores; long time = getTimeUsec(); detect_mobilenet(m, cls_scores); time = getTimeUsec() - time; printf("detection time: %ld ms\n",time/1000); std::vector index; std::vector score; print_topk(cls_scores, 3, index, score); for(unsigned int i = 0; i < index.size(); i++) { cv::putText(m, labels[index[i]], cv::Point(50, 50+30*i), CV_FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(0, 100, 200), 2, 8); }cv::imshow("result", m); cv::imwrite("test_result.jpg", m); cv::waitKey(0); return 0; }

修改ncnn/examples/CMakeLists.txt 添加编译选项
add_executable(mobilenet mobilenet.cpp) target_link_libraries(mobilenet ncnn ${OpenCV_LIBS})

5. 编译&运行
./mobilenet cat.jpg

mobileNET-SSD 检测网络示例 1. 训练 使用caffe-ssd mobilenet-v1 训练网络 得到网络权重参数
这里训练的效果不太好,是基于mobilenet的权重进行训练的,得到 caffe版本的模型和权重文件: MN_ssd_33_deploy.prototxt MN_ssd_33_iter_26000.caffemodel这里的是直接训练好的 mobilenet-ssd 检测效果还可以 其实可以基于这个权重进行训练:

也可以直接从这里下载
2. 模型转换
caffe_old -> caffe_new: 注意这里的caffe版本为 caffe-ssd的版本 否则有些层的类型不认识

caffe-ssd
upgrade_net_proto_text [老prototxt] [新prototxt] upgrade_net_proto_binary [老caffemodel] [新caffemodel]模型框架转换: ~/code/ncnn/build/tools$ ../../tools/caffe_tools/upgrade_net_proto_text MN_ssd_33_deploy.prototxt ssdmobilenet.prototxt 模型权重文件转换: ~/code/ncnn/build/tools$ ../../tools/caffe_tools/upgrade_net_proto_binary MN_ssd_33_iter_26000.caffemodel ssdmobilenet.caffemodel 上面 对casse-ssd/build/tools 工具做了 符号链接 ln -s /home/wanyouwen/ewenwan/software/caffe-ssd/build/tools/ ./caffe_tools caffe -> ncnn ./../../tools/caffe/caffe2ncnn ssdmobilenet.prototxt ssdmobilenet.caffemodel mobilenet_ssd_voc_ncnn.param mobilenet_ssd_voc_ncnn.bin caffe2ncnn的 作用是将caffe模型生成ncnn 模型 .prototxt >>> .param.caffemodel >>> .bin;执行上面命令后就可以生成NCNN模型需要的param 与bin 文件.

3. 修改检测源文件
// Tencent is pleased to support the open source community by making ncnn available. //#include #include #include #include #include #include #include #include "net.h"#include #include // 计时 long getTimeUsec() {struct timeval t; gettimeofday(&t,0); return (long)((long)t.tv_sec*1000*1000 + t.tv_usec); }//定义一个结果 结构体 struct Object{ cv::Rect rec; //边框 int class_id; //类别id float prob; //概率 }; // voc const char* class_names[] = {"background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"}; static int detect_mobilenet(cv::Mat& raw_img, float show_threshold) { ncnn::Net ssdmobilenet; /* 模型下载 * model isconverted from https://github.com/chuanqi305/MobileNet-SSD * and can be downloaded from https://drive.google.com/open?id=0ByaKLD9QaPtucWk0Y0dha1VVY0U */ printf("loading net... \r\n"); // 原始图片尺寸 int img_h = raw_img.size().height; int img_w = raw_img.size().width; ssdmobilenet.load_param("mobilenet_ssd_voc_ncnn.param"); ssdmobilenet.load_model("mobilenet_ssd_voc_ncnn.bin"); int input_size = 300; // 改变图像尺寸 到网络的输入尺寸 ncnn::Mat in = ncnn::Mat::from_pixels_resize(raw_img.data, ncnn::Mat::PIXEL_BGR, raw_img.cols, raw_img.rows, input_size, input_size); // 去均值, 再归一化 const float mean_vals[3] = {127.5f, 127.5f, 127.5f}; const float norm_vals[3] = {1.0/127.5,1.0/127.5,1.0/127.5}; //in.substract_mean_normalize(mean_vals, norm_vals); // 上面给的模型in.substract_mean_normalize(mean_vals, 0); // 我自己训练的 没有进行归一化ncnn::Extractor ex = ssdmobilenet.create_extractor(); ex.set_light_mode(true); //ex.set_num_threads(4); //线程数量 ex.input("data", in); printf("begin detecting... \r\n"); long time = getTimeUsec(); ncnn::Mat out; ex.extract("detection_out", out); //网络输出time = getTimeUsec() - time; printf("detection time: %ld ms\n",time/1000); // 打印总结果 printf("%d %d %d\n", out.w, out.h, out.c); std::vector objects; // 获取结果 for (int iw=0; iw show_threshold)//按阈值显示 { cv::RNG rng(cvGetTickCount()); //cv::rectangle(raw_img, object.rec, cv::Scalar(255, 0, 0)); // 绿色 物体标注框 // 随机颜色框 cv::rectangle(raw_img, object.rec, cv::Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),1); std::ostringstream pro_str; pro_str<
4. 编译 make -j
5. 运行测试 ./ssdmobilenet person.jpg
这里如果没使用 新版本caffe格式可能会有错误: find_blob_index_by_name data_splitncnn_6 failed find_blob_index_by_name data_splitncnn_5 failed find_blob_index_by_name data_splitncnn_4 failed find_blob_index_by_name data_splitncnn_3 failed find_blob_index_by_name data_splitncnn_2 failed find_blob_index_by_name data_splitncnn_1 failed find_blob_index_by_name data_splitncnn_0 failed Segmentation fault

ssdsuqeezenet prototxt & caffemodel
// Tencent is pleased to support the open source community by making ncnn available. // #include #include #include #include #include #include "net.h"#include #include // 计时 long getTimeUsec() {struct timeval t; gettimeofday(&t,0); return (long)((long)t.tv_sec*1000*1000 + t.tv_usec); } ///struct Object { cv::Rect_ rect; int label; float prob; }; static int detect_squeezenet(const cv::Mat& bgr, std::vector& objects) { ncnn::Net squeezenet; // original pretrained model from https://github.com/chuanqi305/SqueezeNet-SSD // squeezenet_ssd_voc_deploy.prototxt // https://drive.google.com/open?id=0B3gersZ2cHIxdGpyZlZnbEQ5Snc printf("loading net... \r\n"); squeezenet.load_param("squeezenet_ssd_voc.param"); squeezenet.load_model("squeezenet_ssd_voc.bin"); const int target_size = 300; //网络输入尺寸 // 原始图片尺寸 int img_w = bgr.cols; int img_h = bgr.rows; // 改变图像尺寸 到网络的输入尺寸 ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, target_size, target_size); // 去均值, 再归一化 const float mean_vals[3] = {104.f, 117.f, 123.f}; in.substract_mean_normalize(mean_vals, 0); //注意这里未归一化ncnn::Extractor ex = squeezenet.create_extractor(); ex.set_light_mode(true); ex.set_num_threads(4); //线程数量ex.input("data", in); printf("begin detecting... \r\n"); long time = getTimeUsec(); ncnn::Mat out; ex.extract("detection_out",out); time = getTimeUsec() - time; printf("detection time: %ld ms\n",time/1000); // 打印总结果 // printf("%d %d %d\n", out.w, out.h, out.c); objects.clear(); for (int i=0; i& objects) { // voc static const char* class_names[] = {"background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"}; cv::Mat image = bgr.clone(); for (size_t i = 0; i < objects.size(); i++) { const Object& obj = objects[i]; fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob, obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height); cv::RNG rng(cvGetTickCount()); //cv::rectangle(image, obj.rect, cv::Scalar(255, 0, 0)); // 绿色 物体标注框 // 随机颜色框 cv::rectangle(image, obj.rect, cv::Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255))); char text[256]; sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100); int baseLine = 0; cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); int x = obj.rect.x; int y = obj.rect.y - label_size.height - baseLine; if (y < 0) y = 0; if (x + label_size.width > image.cols) x = image.cols - label_size.width; // 画文本矩形边框 cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)), cv::Scalar(255, 255, 255), CV_FILLED); // 添加文字 cv::putText(image, text, cv::Point(x, y + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0)); }cv::imshow("image", image); cv::waitKey(0); }int main(int argc, char** argv) { if(argc != 2){ printf("usage: ./ssdmobilenet *.jpg\r\n"); return -1; }// 图像地址 const char* imagepath = argv[1]; // 读取图像 cv::Mat m = cv::imread(imagepath, CV_LOAD_IMAGE_COLOR); if (m.empty()) { fprintf(stderr, "cv::imread %s failed\n", imagepath); return -1; }std::vector objects; detect_squeezenet(m, objects); draw_objects(m, objects); return 0; }
【caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd】caffe|神经网络高性能计算 卷积计算优化 openblas GEMM 矩阵乘法优化 ncnn mobileNet-ssd shueezeNet-ssd
文章图片

    推荐阅读