美文网首页
0#01 解析mobile_ssd样例代码

0#01 解析mobile_ssd样例代码

作者: dogo_L1L | 来源:发表于2018-12-18 17:46 被阅读0次

    1.预备知识
    2.样例程序介绍
    3.运行代码
    3.1.能在笔记本上成功运行 mobilenet_ssd 样例代码。
    3.2.理解 Tengine 的工作方式。
    3.3.能在 RK3399 上运行该程序。
    3.4.能在 RK3399 上运行该程序并使用 GPU 加速

    1.预备知识

           CLion:一款优秀的C语言开发工具。主要是有免费的社区版本,并且能支持cmake文件的编译。最大的福利就是支持Ubuntu安装。
           环境安装:0#00 Tengine 的安装

    2.样例程序介绍

           样例程序位于tengine目录下," tengine安装路径/examples/mobilenet_ssd/"
           我们使用 CLion 打开该项目。
    1.选中mobilenet_ssd文件夹
    2.右键->open with(打开方式)->Other Applications->View All Applications->CLion(使用CLion打开该文件夹。)
           打开该文件的目的:
    1.能在笔记本上成功运行 mobilenet_ssd 样例代码。
    2.理解 Tengine 的工作方式。
    3.能在 RK3399 上运行该程序。
    4.能在 RK3399 上运行该程序并使用 GPU 加速
    注:当前阶段并不去理解 SSD 的工作方式,因为官方已经为我们提供训练好的 caffe 模型( prototxt + caffemodel )


    从百度云中下载 caffe 模型 Tengine model zoo (密码: 57vb)
    找到:

    • MobileNetSSD_deploy.caffemodel
    • MobileNetSSD_deploy.prototxt
      将其存放在 tengine路径下/models/

    3.运行代码

    TODO:1.能在笔记本上成功运行 mobilenet_ssd 样例代码。

           如果直接使用 CLion 对该程序进行编译,会产生报错信息,类似缺少(找不到)头文件。

    /usr/bin/cmake --build /home/lee/Documents/tengine/examples/mobilenet_ssd/cmake-build-debug --target MSSD -- -j4
    ......(省略)
    /common.cpp:4:27: fatal error: tengine_c_api.h: 没有那个文件或目录
    compilation terminated.
    CMakeFiles/MSSD.dir/build.make:86: recipe for target 'CMakeFiles/MSSD.dir/home/lee/Documents/tengine/examples/common/common.cpp.o' failed
    make[3]: *** [CMakeFiles/MSSD.dir/home/lee/Documents/tengine/examples/common/common.cpp.o] Error 1
    make[3]: *** Waiting for unfinished jobs....
    /home/lee/Documents/tengine/examples/mobilenet_ssd/mssd.cpp:32:27: fatal error: tengine_c_api.h: 没有那个文件或目录
    compilation terminated.
    CMakeFiles/MSSD.dir/build.make:62: recipe for target 'CMakeFiles/MSSD.dir/mssd.cpp.o' failed
    ......(省略)
    

    原因在官方给的 gpu_cpu_mssd.md 中提到:

    cd example/mobilenet_ssd
    cmake -DTENGINE_DIR=/home/firefly/tengine .
    make 
    

    所以问题出在 -DTENGINE_DIR 指定 Tengine 的安装路径。
    解决办法:
    在 CMAKE 文件中加入该行。

    cmake_minimum_required (VERSION 2.8)
    project(MSSD)
    add_definitions(-std=c++11)
    # TODO:设置 TENGINE_DIR 的路径
    set( TENGINE_DIR /home/lee/Documents/tengine )
    set( INSTALL_DIR ${TENGINE_DIR}/install/)
    set( TENGINE_LIBS tengine)
    

           那为什么不使用官方的方法,在CMAKE中加入一个选项呢?
    这是我的一些尝试得到的经验:
    1.不方便,每次 CMAKE 加入该行,有时我们使用的 tengine 路径和官方不一样,CMAKE 不会报错,修改的时候,要删除一些 CAMKE 产生的内容,才能重新 CMAKE。
    2.无法利用 CLion 提供的优势,CLion 每次 CMAKE 会产生一个类似 build 的文件夹,方便管理,不需要再手动删除了。
    3.直接编写,能为后续报错信息提供一些思路,有时候使用 ”-DTENGINE_DIR“ 进行指定路径 启动的减号是有中/英文字符区别的,会产生莫名奇妙的错误。
           再次编译cmake文件就会没有报错信息了,注意:使用的 Tengine 路径需要修改为适合自己环境特征的。

    clion.png

           运行该程序:

    image.png

           所以我们已经完全能够 运行 该 样例 了。
    任务1:完成。

    TODO:2.理解 Tengine 的工作方式。

           任务1:只是单纯的运行程序,测试结果,和 example 差不多,类似于 我们安装的 Tengine 是否能够正常工作。
           任务2:已知 MobileNet_SSD 能正常工作的前提下,进行代码的分析。
           如果对于 Tengine 一无所知的话,可以利用 "MobileNet_SSD"进行理解,并且可以充分的利用 CLion 这个工具,进行函数查找,OAID 为 Tengine 编写的 "tengine_c_api.h" 文件提供了丰富的注释信息,我们可以将其改为中文,以便确认我们使用了哪些函数。
           我的经历总结如下:

    TODO:1. 调用 init_tengine() 对 tengine进行初始化
    TODO:2. 调用 create_graph 将 训练好的模型 导入到 图(网络)中
    TODO:3.1通过 get_graph_input_tensor 获取输入的 tensor
    TODO:3.2用 set_tensor_shape 设置输入Tensor的shape
    TODO: 4. 调用 prerun_graph 函数预启动图(类似 malloc,申请资源)
    TODO: 5.1.向 input_data 写入输入的数据,
    TODO: 5.2.并调用 set_tensor_buffer 把数据转移到输入Tensor上
    TODO: 6. 调用 run_graph 运行图(做一次前向传播)
    TODO: 7.1 向 input_data 写入输入的数据
    TODO: 8. 调用 run_graph 运行图(做一次前向传播)
    TODO: 9.1.调用 get_graph_output_tensor 获取输出Tensor
    TODO: 9.2.并用 get_tensor_shape 取得 tensor 的shape
    TODO: 9.3 利用get_tensor_buffer 取得输出 tensor 的内容
    
    最后在退出程序前依次释放各个申请的动态空间
    
    TODO: 9.4 释放 out_tensor 所占空间
    TODO: 3.3 释放 input_tensor 所占空间
    TODO: 2.2 调用 destroy_graph() 来释放资源
    TODO: 1.1 释放 tengine
    

           很明显,我们使用 tengine 只是用来进行前向计算(forward),并不需要后向反馈(backward)。
           所以程序很有模板的感觉,这也为使用 tengine 降低了难度。
           一些总结:tengine 和我常常使用的 PyTorch 进行前向推导并不太相同,

    假设 模型为 Net,输入为 input,输出为 output。
    PyTorch:output = Net(input)
    tengine:
    1. 导入模型:
    load_model(Net,,"caffe", proto文件路径, model_file文件路径)
    2.给模型输入:
    get_graph_input_tensor()
    3.获取模型输出
    get_graph_output_tensor()
    有点面向对象的操作。
    

    接下来开始阅读示例代码(main函数):

    1.定义默认参数变量名称

        // root_path 为 Tengine 的文件路径
        const std::string root_path = get_root_path();
        // proto_file 为我们设置的 prototxt 文件路径
        std::string proto_file;
        // model_file 为我们设置的 model 文件路径
        std::string model_file;
        // image_file 为我们设置的 image 文件路径
        std::string image_file;
        // 设置保存的 生成图片 路径名称
        std::string save_name="save.jpg";
        // device 使用设备默认为空(用于graph)
        const char * device=nullptr;
    

    2.处理命令行的输入处理

        int res;
        // 与命令行输入相关, 指定可输入参数(-p -m -i -hd)
    /*
    *   -p      prototxt 文件路径       默认:"models/MobileNetSSD_deploy.prototxt"
    *   -m      model 文件路径          默认:"models/MobileNetSSD_deploy.caffemodel"
    *   -i      image 文件路径          默认:"tests/images/ssd_dog.jpg"
    *   -h      help 信息
    *   -d      device 设置            默认:为空
    */
    
        while( ( res=getopt(argc,argv,"p:m:i:hd:"))!= -1)
        {
            switch(res)
            {
                case 'p':
                    proto_file=optarg;
                    break;
                case 'm':
                    model_file=optarg;
                    break;
                case 'i':
                    image_file=optarg;
                    break;
                case 'd':
                    device=optarg;
                    break;
                case 'h':
                    std::cout << "[Usage]: " << argv[0] << " [-h]\n"
                              << "   [-p proto_file] [-m model_file] [-i image_file]\n";
                    return 0;
                default:
                    break;
            }
        }
    

    3.设置 命令行参数默认信息

        // prototxt 默认路径
        if(proto_file.empty())
        {
            proto_file = root_path + DEF_PROTO;
            std::cout << "proto file not specified,using " << proto_file << " by default\n";
        }
        // caffemodel 默认路径
        if(model_file.empty())
        {
            model_file = root_path + DEF_MODEL;
            std::cout << "model file not specified,using " << model_file << " by default\n";
        }
        // 使用图片的  默认路径
        if(image_file.empty())
        {
            image_file = root_path + DEF_IMAGE;
            std::cout << "image file not specified,using " << image_file << " by default\n";
        }
    

    开始使用 OAID 提供的 tengine_c_api.h。(建议配合 ctrl+鼠标点开对应的函数名称,进行查看作用)
    4.完成TODO1,2

        // TODO:1. 调用 init_tengine() 对 tengine进行初始化
        if(init_tengine() < 0)
        {
            std::cout << " init tengine failed\n";
            return 1;
        }
        // 检查库文件是否高于0.9
        if(request_tengine_version("0.9") != 1)
        {
            std::cout << " request tengine version failed\n";
            return 1;
        }
        // 确认 模型文件存在
        if(!check_file_exist(proto_file) or (!check_file_exist(model_file) or !check_file_exist(image_file)))
        {
            return 1;
        }
        // TODO:2. 调用 create_graph 将 训练好的模型 导入到 图(网络)中
        graph_t graph = create_graph(nullptr, "caffe", proto_file.c_str(), model_file.c_str());
    
        // 判断是否创建 graph 成功
        if(graph == nullptr)
        {
            std::cout << "Create graph failed\n";
            std::cout << " ,errno: " << get_tengine_errno() << "\n";
            return 1;
        }
        // 设置 运行 的设备
        if(device != nullptr)
        {
            set_graph_device(graph, device);
        }
    

    5.设置我们处理图片的信息
           在 SSD 论文中,对于图形的输入需要先进行 reshape。

        // 输入图片信息
        // img 的高(height)
        int img_h = 300;
        // img 的宽(width)
        int img_w = 300;
        // img 的大小(size)
        int img_size = img_h * img_w * 3;
        // 为输入图片申请空间,有mallloc,所以需要释放(free)
        float *input_data = (float *)malloc(sizeof(float) * img_size);
    
        int node_idx=0;
        int tensor_idx=0;
    

    6.对于模型输入的预处理,为了得到输入的入口

        // TODO:1. 调用 init_tengine() 对 tengine进行初始化
        if(init_tengine() < 0)
        {
            std::cout << " init tengine failed\n";
            return 1;
        }
        // 检查库文件是否高于0.9
        if(request_tengine_version("0.9") != 1)
        {
            std::cout << " request tengine version failed\n";
            return 1;
        }
        // 确认 模型文件存在
        if(!check_file_exist(proto_file) or (!check_file_exist(model_file) or !check_file_exist(image_file)))
        {
            return 1;
        }
        // TODO:2. 调用 create_graph 将 训练好的模型 导入到 图(网络)中
        graph_t graph = create_graph(nullptr, "caffe", proto_file.c_str(), model_file.c_str());
    
        // 判断是否创建 graph 成功
        if(graph == nullptr)
        {
            std::cout << "Create graph failed\n";
            std::cout << " ,errno: " << get_tengine_errno() << "\n";
            return 1;
        }
        // 设置 运行 的设备
        if(device != nullptr)
        {
            set_graph_device(graph, device);
        }
    

    7.多次重复,获取平均值(设置重复属性)

        // 设置重复次数,求平均值
        int repeat_count = 1;
        // getenv 获取环境变量
        const char* repeat = std::getenv("REPEAT_COUNT");
    
        if(repeat)
            /* strtoul: (str to ul)将字符类型转为unsigned long类型,
             * 10:十进制   NULL:结束符
             */
            repeat_count = std::strtoul(repeat, NULL, 10);
    

    8.实际输入属性

        //TODO: 5.1.向 input_data 写入输入的数据,
        // 将 图片 的路径名转化为 图片内容的矩阵,并赋值给 input_data
        get_input_data_ssd(image_file, input_data, img_h, img_w);
        //TODO: 5.2.并调用 set_tensor_buffer 把数据转移到输入Tensor上
        set_tensor_buffer(input_tensor, input_data, img_size * 4);
    

    9.实际运行

        //TODO: 6. 调用 run_graph 运行图(做一次前向传播)
        ret = run_graph(graph, 1);
        if(ret != 0)
        {
            std::cout << "Run graph failed, errno: " << get_tengine_errno() << "\n";
            return 1;
        }
    

    10.利用重复获取平均运行时间

        struct timeval t0, t1;
        float total_time = 0.f;
        for(int i = 0; i < repeat_count; i++)
        {
            //TODO: 7.1 向 input_data 写入输入的数据,
            get_input_data_ssd(image_file, input_data, img_h, img_w);
            // 获取当前值,并赋值给 t0(开始值)
            gettimeofday(&t0, NULL);
            //TODO: 8. 调用 run_graph 运行图(做一次前向传播)
            run_graph(graph, 1);
            gettimeofday(&t1, NULL);
            float mytime = ( float )((t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec)) / 1000;
            total_time += mytime;
        }
        std::cout << "--------------------------------------\n";
        std::cout << "repeat " << repeat_count << " times, avg time per run is " << total_time / repeat_count << " ms\n";
    
    

    11.获取输出信息

        //TODO: 9.1.调用 get_graph_output_tensor 获取输出Tensor
        tensor_t out_tensor = get_graph_output_tensor(graph, 0, 0);    //"detection_out");
        /*
     * out_dim[4]中元素的含义
     * [0]:批次:1张图
     * [1]:检测到目标个数:3个目标
     * [2]:outdata 的 Box 6 个信息:
     *              0. 属于的类别(下标)
     *              1. 属于该类别的score
     *              2. 左上角点(x)相对于宽的百分比
     *              3. 左上角点(y)相对于高的百分比
     *              4. 右上角点(x)相对于宽的百分比
     *              5. 右上角点(y)相对于高的百分比
     * [3]:1 一行
     */
        int out_dim[4];
        //TODO: 9.2.并用 get_tensor_shape 取得 tensor 的shape
        ret = get_tensor_shape(out_tensor, out_dim, 4);
        if(ret <= 0)
        {
            std::cout << "get tensor shape failed, errno: " << get_tengine_errno() << "\n";
            return 1;
        }
        //TODO:9.3 利用get_tensor_buffer 取得输出 tensor 的内容
        float* outdata = ( float* )get_tensor_buffer(out_tensor);
    

    12.利用 输出 和 OpenCV 进行画图

        int num = out_dim[1];
        //设置阈值,是否为检测目标
        float show_threshold = 0.5;
        // 通过 outdata 对 image 进行绘制边框和label信息,并保存
        post_process_ssd(image_file, show_threshold, outdata, num, save_name);
    

    12.程序结束,记得释放内存和资源

        //TODO: 9.4 释放 out_tensor 所占空间
        release_graph_tensor(out_tensor);
        //TODO: 3.1 释放 input_tensor 所占空间
        release_graph_tensor(input_tensor);
        // 释放 graph 执行所占用的资源
        ret = postrun_graph(graph);
        if(ret != 0)
        {
            std::cout << "Postrun graph failed, errno: " << get_tengine_errno() << "\n";
            return 1;
        }
        free(input_data);
        //TODO: 2.2 调用 destroy_graph() 来释放资源
        destroy_graph(graph);
        //TODO: 1.1 释放 tengine
        release_tengine();
    

    任务2:完成

    TODO:3.能在 RK3399 上运行该程序。

            RK3399上 tengine 的路径可能会与 笔记本电脑 不同。使用不同的 github 网址,下载下来的 tengine 内容虽然一样,但是 文件名称可能会不一样。比如 tengine-master,tengine,Tengine。(这些都是小细节)
            并且 RK3399 上没有 CLion,所以我们需要手动进行 cmake。
            过程如下:(建议修改前进行 备份 )
    1.修改 "CMakeLists.txt" 文件

    # 设置 tengine 路径
    set( TENGINE_DIR /home/firefly/tengine )
    

    2.创建 build 文件,目的是为了放置 cmake 文件设置错误,删除文件的时候,不确定要删除哪些文件。

    1.在 mobilenet_ssd 目录下创建 build 文件
    cd tengine/examples/mobilenet_ssd
    mkdir build
    cd build
    

    2.进行 cmake,前一级目录( cmake 目的是为了生成 make文件)

    cmake ..
    返回信息
    ""
    -- The C compiler identification is GNU 5.4.0
    -- The CXX compiler identification is GNU 5.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Found OpenCV: /usr/local (found version "3.4.2") 
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/lee/Documents/tengine/examples/mobilenet_ssd/build
    ""
    

    3.进行make

    make
    返回信息
    ""
    Scanning dependencies of target MSSD
    [ 33%] Building CXX object CMakeFiles/MSSD.dir/mssd.cpp.o
    [ 66%] Building CXX object CMakeFiles/MSSD.dir/home/lee/Documents/tengine/examples/common/common.cpp.o
    [100%] Linking CXX executable MSSD
    [100%] Built target MSSD
    ""
    

    4.执行可执行文件

    firefly@firefly:~/Tengine/examples/mobilenet_ssd/build$ ./MSSD 
    /home/firefly/Tengine/examples/mobilenet_ssd/build/MSSD
    proto file not specified,using /home/firefly/Tengine/models/MobileNetSSD_deploy.prototxt by default
    model file not specified,using /home/firefly/Tengine/models/MobileNetSSD_deploy.caffemodel by default
    image file not specified,using /home/firefly/Tengine/tests/images/ssd_dog.jpg by default
    tensor: detection_out created by node: detection_out is not consumed
    add the node: detection_out into output list
    load model done!
    --------------------------------------
    repeat 1 times, avg time per run is 197.33 ms
    detect result num: 3 
    dog :100%
    BOX:( 138.509 , 209.394 ),( 324.57 , 541.314 )
    car :100%
    BOX:( 467.315 , 72.8045 ),( 687.269 , 171.128 )
    bicycle :100%
    BOX:( 107.395 , 140.657 ),( 574.212 , 415.188 )
    ======================================
    [DETECTED IMAGE SAVED]: [save.jpg](https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxcheckurl?requrl=http%3A%2F%2Fsave.jpg&skey=%40crypt_f9d3b6a2_596aae6997f00ed042e8dccce68cd50a&deviceid=e183140456749306&pass_ticket=OlSyJ0Ia1IhtysLjA0CCvzP6XXavJ0bmawua8PAWQ1GErsg5SDT81873sBxClMpl&opcode=2&scene=1&username=@f7db13e1c47e0fed7e506d9d48739f7e)
    ======================================
    Release Graph Executor for graph graph
    Release workspace default resource
    

    打开图片进行检查。(图片在 mobilenet_ssd/build/save.jpg 目录下)


    save.jpg

    任务3:完成

    TODO:4.能在 RK3399 上运行该程序并使用 GPU 加速。

            在 RK3399 上使用 GPU ,在 OAID 上有相关的教程gpu_cpu_mssd
            需要注意的是如果之前已经编译过 tengine ,不能直接使用GPU。需要重新编译(但是我还是失败了)。

    1.清除 make 信息
    make clean
    2.删除 与 驱动相关的文件
    rm -r build/driver
    

    我使用GPU时,重新下载 tengine。(即删除原有的 tengine 目录)
    下面我从 0 开始讲解如何 设置 RK3399 的 GPU 使用。
    1.从 github 中拷贝 ComputeLibrary

    git clone https://github.com/ARM-software/ComputeLibrary.git  
    git checkout v18.05
    

    2.编译 ComputeLibrary

    apt-get install scons
    scons Werror=1 -j4 debug=0 asserts=1 neon=0 opencl=1 embed_kernels=1 os=linux arch=arm64-v8a
    

    3.修改 tengine 的 makefile.config 文件

    1.使用Arm64
    # Set the target arch 
    CONFIG_ARCH_ARM64=y
    2.使用GPU
    # Enable GPU support by Arm Computing Library
    CONFIG_ACL_GPU=y
    3.不使用BLAS
    # Use BLAS as the operator implementation
    # CONFIG_ARCH_BLAS=y
    4.指定ACL库的路径(注意路径是否正确)
    # Set the path of ACL 
    ACL_ROOT=/home/firefly/ComputeLibrary
    5.使用caffe模型
    # Enable other serializers 
    CONFIG_CAFFE_SERIALIZER=y
    

    4.进行 make

    make
    

    5.进行安装

    make install
    

    6.检查

    ./build/tests/bin/bench_sqz -d acl_opencl
    

            对我们的 样例 进行修改。
    1.修改 "CMakeLists.txt" 文件

    cd examples/mobilenet_ssd
    leafpad CMakeLists.txt
    1.加入 tengine 路径
    # 设置 tengine 路径
    set( TENGINE_DIR /home/firefly/tengine )
    

    2.添加 build 文件

    mkdir build
    cd build
    

    3.进行 cmake 编译

    cmake ..
    

    4.进行 make 编译

    make
    

    5.设置使用 GPU 参数(将GPU频率设置为 800000000)

    sudo su
    echo "performance" >/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/governor
    cat /sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/cur_freq
    
    命令行输入以下信息(设置临时环境变量)
    # 
    export GPU_CONCAT=0
    # 使能GPU fp16
    export ACL_FP16=1
    # 设置重复次数
    export REPEAT_COUNT=5
    # 运行
    taskset 0x1 ./MSSD -d acl_opencl
    

    显示如下:

    firefly@firefly:~/tengine/examples/mobilenet_ssd/build$ export GPU_CONCAT=0
    firefly@firefly:~/tengine/examples/mobilenet_ssd/build$ export ACL_FP16=1
    firefly@firefly:~/tengine/examples/mobilenet_ssd/build$ export REPEAT_COUNT=5
    firefly@firefly:~/tengine/examples/mobilenet_ssd/build$ taskset 0x1 ./MSSD -d acl_opencl
    /home/firefly/tengine/examples/mobilenet_ssd/build/MSSD
    proto file not specified,using /home/firefly/tengine/models/MobileNetSSD_deploy.prototxt by default
    model file not specified,using /home/firefly/tengine/models/MobileNetSSD_deploy.caffemodel by default
    image file not specified,using /home/firefly/tengine/tests/images/ssd_dog.jpg by default
    ACL Graph Initialized
    Driver: ACLGraph probed 1 devices
    tensor: detection_out created by node: detection_out is not consumed
    add the node: detection_out into output list
    load model done!
    --------------------------------------
    repeat 5 times, avg time per run is 165.676 ms
    detect result num: 3 
    dog :100%
    BOX:( 138.419 , 209.091 ),( 324.504 , 541.568 )
    car :100%
    BOX:( 467.356 , 72.9224 ),( 687.269 , 171.123 )
    bicycle :100%
    BOX:( 107.053 , 140.221 ),( 574.472 , 415.248 )
    ======================================
    [DETECTED IMAGE SAVED]: [save.jpg]
    ======================================
    Release Graph Executor for graph graph
    Release workspace default resource
    
    

    完成任务4。


    附比较 RK3399 和 笔记本 时间上的差距

    1.RK3399 不使用 GPU 的情况下:

    repeat 1 times, avg time per run is 197.33 ms
    

    2.RK3399 使用 GPU的情况下:

    repeat 5 times, avg time per run is 165.676 ms
    

    3.笔记本使用 BLAS 库的情况下

    repeat 1 times, avg time per run is 133.117 ms
    

    GPU 的使用,对RK3399有明显的加速。
    与官方给的 mssd 。


    github中官方对mssd的结果

    相关文章

      网友评论

          本文标题:0#01 解析mobile_ssd样例代码

          本文链接:https://www.haomeiwen.com/subject/krrxkqtx.html