美文网首页
0#02 将 MSSD 制作成视频流

0#02 将 MSSD 制作成视频流

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

    1.预备知识

            本章还是不讨论 SSD 是什么,只要能够理解 SSD 的目的是目标检测。
            前提基础知识:解析mobile_ssd样例代码
            上一章中具体的讲解了 "mobilenet_SSD"(下称MSSD)运行过程,为示例代码添上了注释。但是也很明显只是对一张图片进行检测。为了更近一步,我把 这个样例代码略做修剪,对摄像头的视频流图片进行检测。
            但是如何使用视频流呢?
    这里给出一段简单的 OpenCV 程序,该程序是 OpenCV 的样例程序,位于 "opencv-3.4.2/samples/cpp/example_cmake",同样使用 CLion 打开。

    #include "opencv2/core.hpp"
    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/videoio.hpp"
    #include <iostream>
    // 使用 opencv 的命名空间
    using namespace cv;
    using namespace std;
    
    void drawText(Mat & image);
    
    int main()
    {
        cout << "Built with OpenCV " << CV_VERSION << endl;
        Mat image;
        // 设置使用摄像头(0)
        VideoCapture capture;
        capture.open(0);
        // 如果摄像头存在
        if(capture.isOpened())
        {
            cout << "Capture is opened" << endl;
          
            for(;;)
            {
                capture >> image;
                if(image.empty())
                    break;
                drawText(image);
                // img 图像大小 480,640
                imshow("Sample", image);
                // 注意 imshow 后面必须跟一个 waitKey(),否则无法显示图片
                if(waitKey(10) >= 0)
                    break;
            }
        }
        // 如果摄像头不存在(可以省略)
        else
        {
            cout << "No capture" << endl;
            image = Mat::zeros(480, 640, CV_8UC1);
            drawText(image);
            imshow("Sample", image);
            waitKey(0);
        }
        return 0;
    }
    
    void drawText(Mat & image)
    {
        putText(image, "Hello OpenCV",
                Point(20, 50),
                FONT_HERSHEY_COMPLEX, 1, // font face and scale
                Scalar(255, 255, 255), // white
                1, LINE_AA); // line thickness and type
    }
    

            该程序是一个简单的视频流程序,所以一个简单的想法,将该程序与 MSSD 进行结合。由 视频流 提供 图片(image),由 MSSD 对图片进行检测。所以我们需要
    TODO:1.知道在 MSSD中那部分是实际的对图像进行处理的地方。
    TODO:2.知道如何在 MSSD 中使用视频流。
    TODO:3.将 MSSD 的图片地址,改为视频流的图片(CV::Mat)。

    2.执行任务

    1.知道在 MSSD中那部分是实际的对图像进行处理的地方。

            比较容易找到的线索是,样例中 MSSD 使用的是一个图片路径,以图片路径为线索进行查找。

    TODO: 1. 调用 init_tengine_library  函数初始化, 只能被调用一次
    TODO: 2. 调用 load_model    载入训练好的模型
    TODO: 3. 调用 create_runtime_graph 函数创建图(类似 pytorch 的 net)
    TODO: 3.1 需要调用 check_graph_valid 来确定返回的句柄
    TODO: 4.1 调用 get_graph_input_tensor 获取输入Tensor
    TODO: 4.2 并用 check_tensor_valid 确认返回值是否合法
    TODO: 4.3 并用 set_tensor_shape 设置输入Tensor的shape
    TODO: 5. 调用 prerun_graph 函数预启动图(类似 malloc,申请资源)
    TODO: 7.1.向 input_data 写入输入的数据,
    TODO: 7.2.并调用 set_tensor_buffer 把数据转移到输入Tensor上
    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 取得缓冲区上的数据
    TODO: 10. 最后在退出程序前依次释放各个申请的动态空间
    TODO: 9.4 释放 out_tensor 所占空间
    TODO: 4.4 释放 input_tensor 所占空间
    TODO: 3.2 调用 destroy_runtime_graph() 来释放资源
    TODO: 2.1 请调用 remove_model() to 释放导入的 model
    

    参考具体代码发现

    开始使用
    TODO: 7.1.向 input_data 写入输入的数据,
    get_input_data_ssd(image_file, input_data, img_h,  img_w);
    ......(省略)
    结束使用
    TODO: 9.3.并用 get_tensor_buffer 取得缓冲区上的数据
    中使用到
        // 通过 outdata 对 image 进行绘制边框和label信息,并保存
        post_process_ssd(image_file,show_threshold, outdata, num,save_name);
    

    所以摄像头图片检索的for(;;)要加在这两行之间。

    2.知道如何在 MSSD 中使用视频流。

    在MSSD中使用摄像头,我们也要为OpenCV的样例代码做一些微调。比如
    1.去掉前面的提示信息
    2.去掉摄像头不存在的情况
    3.去掉 "void drawText(Mat & image)" 部分

    int main()
    {
        .....(MSSD 代码)
        Mat image;
        // 设置使用摄像头(0)
        VideoCapture capture;
        capture.open(0);
        // 如果摄像头存在
        if(capture.isOpened())
        {  
            for(;;)
            {
                capture >> image;
                if(image.empty())
                    break;
    
                ......(将图片交给 MSSD 处理)
                
                // img 图像大小 480,640
                imshow("Sample", image);、
                if(waitKey(10) >= 0)
                    break;
            }
        }
        ......(MSSD 代码)
        return 0;
    }
    

    3.将 MSSD 的图片地址,改为视频流的图片(CV::Mat)。

    实际上使用到 image_file 只有两处,并且都是在函数中。

    以下两个函数
    void get_input_data_ssd(std::string& image_file, float* input_data, int img_h,  int img_w)
    void post_process_ssd(std::string& image_file,float threshold,float* outdata,int num,std::string& save_name)
    

    所以我们要将函数稍作修改,对 "void get_input_data_ssd" 的修改。(实际只修改了两个地方)

    //void get_input_data_ssd(std::string& image_file, float* input_data, int img_h,  int img_w)(修改第一处)
    void get_input_data_ssd(cv::Mat image, float* input_data, int img_h,  int img_w)
    {
        // 读取数据, img 为图形的内容
        //cv::Mat img = cv::imread(image_file);(修改第二处)
        cv::Mat img = image.clone();
        // 如果 img 为空
        if (img.empty())
        {
            std::cerr << "Failed to read image file " << image << ".\n";
            return;
        }
        // 将 img 进行 reshape
        cv::resize(img, img, cv::Size(img_h, img_w));
        // 转化数据类型 CV_32FC3:
        img.convertTo(img, CV_32FC3);
        float *img_data = (float *)img.data;
        int hw = img_h * img_w;
    
        /*类似数据归一化
         * 127.5 = 255/2
         * 0.007843 =1/127.5
         */
        float mean[3]={127.5,127.5,127.5};
        for (int h = 0; h < img_h; h++)
        {
            for (int w = 0; w < img_w; w++)
            {
                for (int c = 0; c < 3; c++)
                {
                    input_data[c * hw + h * img_w + w] = 0.007843* (*img_data - mean[c]);
                    img_data++;
                }
            }
        }
    }
    

    对 "post_process_ssd" 的修改

    //(修改第一处)
    //void post_process_ssd(std::string& image_file,float threshold,float* outdata,int num,std::string& save_name)
    cv::Mat post_process_ssd(cv::Mat image,float threshold,float* outdata,int num,std::string& save_name)
    {
        //检测目标的类别
        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 img = cv::imread(image_file);
        cv::Mat img = image.clone();
        // 图片的信息(height,width)
        int raw_h = img.size().height;
        int raw_w = img.size().width;
        // boxes 为检测输出的信息
        std::vector<Box> boxes;
        // 设置使用的线宽(line_width)
        int line_width=raw_w*0.005;
        // logout: 检测到目标个数
        printf("detect result num: %d \n",num);
        for (int i=0;i<num;i++)
        {
            if(outdata[1]>=threshold)
            {
                //Box 为 输出内容的信息
                Box box;
                box.class_idx=outdata[0];
                box.score=outdata[1];
                box.x0=outdata[2]*raw_w;
                box.y0=outdata[3]*raw_h;
                box.x1=outdata[4]*raw_w;
                box.y1=outdata[5]*raw_h;
                // boxes 作为 栈(vector)
                boxes.push_back(box);
                // logout:输出output信息
                printf("%s\t:%.0f%%\n", class_names[box.class_idx], box.score * 100);
                printf("BOX:( %g , %g ),( %g , %g )\n",box.x0,box.y0,box.x1,box.y1);
            }
            outdata+=6;
        }
        /* output返回的信息并不是都有用
         * 经过阈值过滤之后 数量 可能会 降低
         */
        // 对过滤后的 正确目标点 进行绘制 矩阵框
        for(int i=0;i<(int)boxes.size();i++)
        {
            Box box=boxes[i];
            /*!
             * img: 使用的img(Mat 格式)
             * Rect rec:矩形结构
             *      左上角点(x,y),w宽,h高
             * const Scalar& color: 使用的颜色(scalar:BGR)
             * thickness: 线宽(默认:1)
             * lineType: 线形(默认:LINE_8,实线)
             * shift: 坐标的小数点位数(默认为0,整形)
             */
            cv::rectangle(img, cv::Rect(box.x0, box.y0,(box.x1-box.x0),(box.y1-box.y0)),
                          cv::Scalar(255, 255, 0),
                          line_width);
    
            std::ostringstream score_str;
            score_str<<box.score;
            // 设置框的标签(名称+分数)
            std::string label = std::string(class_names[box.class_idx]) + ": " + score_str.str();
            int baseLine = 0;
            /*!
             * 计算文本的所占大小的尺寸
             * label:设置的文本
             * fontFace:使用的字体
             * fontScale:字体的大小
             * thickness:线宽
             * baseLine:
             */
            cv::Size label_size = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
            // 绘制文本边框矩阵 CV_FILLED 使用填充
            cv::rectangle(img,
                          cv::Rect(cv::Point(box.x0,box.y0- label_size.height),
                                   cv::Size(label_size.width, label_size.height + baseLine)),
                          cv::Scalar(255, 255, 0),
                          CV_FILLED);
            /*!
             * img:输入图片
             * text:输入文本
             * org:左下角点的坐标
             * fontFace:使用字体
             * fontScale:字的尺寸
             * color:使用的颜色
             * thickness:绘制text的线宽
             * lineType:线形
             */
            cv::putText(img, label, cv::Point(box.x0, box.y0),
                        cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
        }
        // 图形保存
        /*!
         * filename:文件路径
         * img:图形矩阵
         * params:
         */
        cv::imwrite(save_name,img);
        // logout: 输出信息
        std::cout<<"======================================\n";
        std::cout<<"[DETECTED IMAGE SAVED]:\t"<< save_name<<"\n";
        std::cout<<"======================================\n";
        return img;
    }
    

    对于函数的修改已经完成了,接下来对主函数加入 for循环

        struct timeval t0, t1;
        float mytime=0;
        // 使用opencv
        cv::VideoCapture capture;
        capture.open(0);
        if(capture.isOpened())
        {
            cv::Mat image;
            std::cout<<"Capture is opened" << std::endl;
            for(;;)
            {
                capture >> image;
                if(image.empty())
                    break;
                //TODO: 7.1 向 input_data 写入输入的数据,
                get_input_data_ssd(image, input_data, img_h,  img_w);
                // 获取当前值,并赋值给 t0(开始值)
                gettimeofday(&t0, NULL);
                //TODO: 7.2 并调用 set_tensor_buffer 把数据转移到输入Tensor上
                set_tensor_buffer(input_tensor, input_data, img_size * 4);
                //TODO: 8. 调用 run_graph 运行图(做一次前向传播)
                run_graph(graph, 1);
                //获取当前时间,并赋值给 t1(结束时间)
                gettimeofday(&t1, NULL);
    
                mytime = (float)((t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec)) / 1000;
    
    
                std::cout << "--------------------------------------\n";
                std::cout << " times, avg time per run is " << mytime << " ms\n";
                //TODO: 9.1.调用 get_graph_output_tensor 获取输出Tensor
                tensor_t out_tensor = get_graph_output_tensor(graph, 0,0);//"detection_out");
                //TODO: 9.2.并用 get_tensor_shape 取得 tensor 的shape
                /*
                 * [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];
                get_tensor_shape( out_tensor, out_dim, 4);
                //std::cout<<"out_dim" << *out_dim <<std::endl;
                //TODO: 9.3.并用 get_tensor_buffer 取得缓冲区上的数据
                float *outdata = (float *)get_tensor_buffer(out_tensor);
                //std::cout<< "outdata" <<*outdata<<std::endl;
                //获取 outdata 的 检测到目标个数(num)
                int num=out_dim[1];
                //设置阈值,是否为检测目标
                float show_threshold=0.5;
                // 通过 outdata 对 image 进行绘制边框和label信息,并保存
                image = post_process_ssd(image,show_threshold, outdata, num,save_name);
    
    
                //img 图像大小 480,640
                cv::imshow("Sample", image);
    
                if(cv::waitKey(10) >= 0)
                    break;
                //TODO: 9.4 释放 out_tensor 所占空间
                put_graph_tensor(out_tensor);
            }
        }
    

    mssd.cpp 完整代码见文末。

    3.进行编译

    检测的图片.png

            成功检测到杯子和显示屏。
    任务完成。


    附 MSSD.cpp完整代码

    /*
     * Licensed to the Apache Software Foundation (ASF) under one
     * or more contributor license agreements.  See the NOTICE file
     * distributed with this work for additional information
     * regarding copyright ownership.  The ASF licenses this file
     * to you under the Apache License, Version 2.0 (the
     * License); you may not use this file except in compliance
     * with the License.  You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing,
     * software distributed under the License is distributed on an
     * AS IS BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied.  See the License for the
     * specific language governing permissions and limitations
     * under the License.
     */
    
    /*
     * Copyright (c) 2018, Open AI Lab
     * Author: chunyinglv@openailab.com
     */
    
    #include <unistd.h>
    #include <iostream>
    #include <iomanip>
    #include <string>
    #include <vector>
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "tengine_c_api.h"
    #include <sys/time.h>
    #include "common.hpp"
    
    //宏定义 prototxt 文件
    #define DEF_PROTO "models/MobileNetSSD_deploy.prototxt"
    //宏定义 caffemodel 文件
    #define DEF_MODEL "models/MobileNetSSD_deploy.caffemodel"
    //宏定义 默认图片文件
    #define DEF_IMAGE "tests/images/myimages.jpg"
    
    //预测的内容(6个)
    struct Box
    {
    //  左上角点
        float x0;
        float y0;
    //  右下角点
        float x1;
        float y1;
    //  预测的类别
        int class_idx;
    //  该类别的 正确率
        float score;
    };
    /*!
     * @brief 将 图片 的路径名转化为 图片内容的矩阵,并赋值给 input_data
     * @param image_file: 输入的图片路径
     * @param input_data: 输出的图片的矩阵
     * @param img_h: 输出图形的height
     * @param img_w: 输出图形的weight
     */
    //void get_input_data_ssd(std::string& image_file, float* input_data, int img_h,  int img_w)(修改第一处)
    void get_input_data_ssd(cv::Mat image, float* input_data, int img_h,  int img_w)
    {
        // 读取数据, img 为图形的内容
        //cv::Mat img = cv::imread(image_file);(修改第二处)
        cv::Mat img = image.clone();
        // 如果 img 为空
        if (img.empty())
        {
            std::cerr << "Failed to read image file " << image << ".\n";
            return;
        }
        // 将 img 进行 reshape
        cv::resize(img, img, cv::Size(img_h, img_w));
        // 转化数据类型 CV_32FC3:
        img.convertTo(img, CV_32FC3);
        float *img_data = (float *)img.data;
        int hw = img_h * img_w;
    
        /*类似数据归一化
         * 127.5 = 255/2
         * 0.007843 =1/127.5
         */
        float mean[3]={127.5,127.5,127.5};
        for (int h = 0; h < img_h; h++)
        {
            for (int w = 0; w < img_w; w++)
            {
                for (int c = 0; c < 3; c++)
                {
                    input_data[c * hw + h * img_w + w] = 0.007843* (*img_data - mean[c]);
                    img_data++;
                }
            }
        }
    }
    /*!
     * @brief
     *
     * @param image_file: 图片的路径
     * @param threshold:是否为检测目标的阈值
     * @param outdata:输出数据
     * @param num:检测到目标的个数
     * @param save_name:
     *
     */
    //(修改第一处)
    //void post_process_ssd(std::string& image_file,float threshold,float* outdata,int num,std::string& save_name)
    cv::Mat post_process_ssd(cv::Mat image,float threshold,float* outdata,int num,std::string& save_name)
    {
        //检测目标的类别
        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 img = cv::imread(image_file);
        cv::Mat img = image.clone();
        // 图片的信息(height,width)
        int raw_h = img.size().height;
        int raw_w = img.size().width;
        // boxes 为检测输出的信息
        std::vector<Box> boxes;
        // 设置使用的线宽(line_width)
        int line_width=raw_w*0.005;
        // logout: 检测到目标个数
        printf("detect result num: %d \n",num);
        for (int i=0;i<num;i++)
        {
            if(outdata[1]>=threshold)
            {
                //Box 为 输出内容的信息
                Box box;
                box.class_idx=outdata[0];
                box.score=outdata[1];
                box.x0=outdata[2]*raw_w;
                box.y0=outdata[3]*raw_h;
                box.x1=outdata[4]*raw_w;
                box.y1=outdata[5]*raw_h;
                // boxes 作为 栈(vector)
                boxes.push_back(box);
                // logout:输出output信息
                printf("%s\t:%.0f%%\n", class_names[box.class_idx], box.score * 100);
                printf("BOX:( %g , %g ),( %g , %g )\n",box.x0,box.y0,box.x1,box.y1);
            }
            outdata+=6;
        }
        /* output返回的信息并不是都有用
         * 经过阈值过滤之后 数量 可能会 降低
         */
        // 对过滤后的 正确目标点 进行绘制 矩阵框
        for(int i=0;i<(int)boxes.size();i++)
        {
            Box box=boxes[i];
            /*!
             * img: 使用的img(Mat 格式)
             * Rect rec:矩形结构
             *      左上角点(x,y),w宽,h高
             * const Scalar& color: 使用的颜色(scalar:BGR)
             * thickness: 线宽(默认:1)
             * lineType: 线形(默认:LINE_8,实线)
             * shift: 坐标的小数点位数(默认为0,整形)
             */
            cv::rectangle(img, cv::Rect(box.x0, box.y0,(box.x1-box.x0),(box.y1-box.y0)),
                          cv::Scalar(255, 255, 0),
                          line_width);
    
            std::ostringstream score_str;
            score_str<<box.score;
            // 设置框的标签(名称+分数)
            std::string label = std::string(class_names[box.class_idx]) + ": " + score_str.str();
            int baseLine = 0;
            /*!
             * 计算文本的所占大小的尺寸
             * label:设置的文本
             * fontFace:使用的字体
             * fontScale:字体的大小
             * thickness:线宽
             * baseLine:
             */
            cv::Size label_size = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
            // 绘制文本边框矩阵 CV_FILLED 使用填充
            cv::rectangle(img,
                          cv::Rect(cv::Point(box.x0,box.y0- label_size.height),
                                   cv::Size(label_size.width, label_size.height + baseLine)),
                          cv::Scalar(255, 255, 0),
                          CV_FILLED);
            /*!
             * img:输入图片
             * text:输入文本
             * org:左下角点的坐标
             * fontFace:使用字体
             * fontScale:字的尺寸
             * color:使用的颜色
             * thickness:绘制text的线宽
             * lineType:线形
             */
            cv::putText(img, label, cv::Point(box.x0, box.y0),
                        cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
        }
        // 图形保存
        /*!
         * filename:文件路径
         * img:图形矩阵
         * params:
         */
        cv::imwrite(save_name,img);
        // logout: 输出信息
        std::cout<<"======================================\n";
        std::cout<<"[DETECTED IMAGE SAVED]:\t"<< save_name<<"\n";
        std::cout<<"======================================\n";
        return img;
    }
    
    int main(int argc, char *argv[])
    {
        // 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;
        // 设置保存的 生成图片 路径名称
        std::string save_name="save.jpg";
        // device 使用设备默认为空(用于graph)
        const char * device=nullptr;
    
        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;
            }
        }
    
    
    
        const char *model_name = "mssd_300";
        // 设置 proto_file 默认值
        if(proto_file.empty())
        {
            proto_file = root_path + DEF_PROTO;
            std::cout<< "proto file not specified,using "<<proto_file<< " by default\n";
    
        }
        // 设置 model_file 默认值
        if(model_file.empty())
        {
            model_file = root_path + DEF_MODEL;
            std::cout<< "model file not specified,using "<<model_file<< " by default\n";
        }
    /*
    *   Tengine 使用流程:
    *   1. 调用 init_tengine_library  函数初始化
    *   2. 调用 load_model    载入训练好的模型
    *           这里需要指定框架的模型:    tensorflow,caffe,mxnet,onnx
    *           设置时需要修改 Tengine_root/makefile.config
    *               # Enable other serializers
    *               CONFIG_CAFFE_SERIALIZER=y
    *               # CONFIG_MXNET_SERIALIZER=y
    *               # CONFIG_ONNX_SERIALIZER=y
    *               # CONFIG_TF_SERIALIZER=y
    *               CONFIG_TENGINE_SERIALIZER=y
    *           将要使用的 model 去掉注释(默认使用caffe)
    *   3. 调用 create_runtime_graph 函数创建图(类似 pytorch 的 net)
    *   4. 调用 get_graph_input_tensor 获取输入Tensor并用 set_tensor_shape 设置输入Tensor的shape
    *            类似 net(input)
    *   5. 调用 prerun_graph 函数预启动图(类似 malloc,申请资源)
    *   6. 调用 get_graph_output_tensor 获取输出Tensor并用 get_tensor_buffer_size 获取输出的shape
    *   7. 向 input_data 写入输入的数据,并调用 set_tensor_buffer 把数据转移到输入Tensor上
    *   8. 调用 run_graph 运行图(做一次前向传播)
    *   9. 调用 get_graph_output_tensor 获取输出Tensor并用 get_tensor_buffer 取得缓冲区上的数据
    *   10. 最后在退出程序前依次释放各个申请的动态空间
    */
        //TODO: 1. 调用 init_tengine_library  函数初始化, 只能被调用一次
        init_tengine_library();
        // 检查库文件是否高于 0.1
        if (request_tengine_version("0.1") < 0)
            return 1;
        //TODO: 2. 调用 load_model    载入训练好的模型
        /*
         * @param model_name 给导入的模型命名,便于调用
         * @param model_format 模型的文件格式: caffe/onnx/tensorflow/mxnet/tengine
         * @param fname  文件路径(可以有多个,char)
         * @note  0. 保存的模型可能含有多个文件(比如caffe)
         *        1. 请调用 remove_model() to 释放导入的 model
         */
        if (load_model(model_name, "caffe", proto_file.c_str(), model_file.c_str()) < 0)
            return 1;
        std::cout << "load model done!\n";
    
        //TODO: 3. 调用 create_runtime_graph 函数创建图(类似 pytorch 的 net)
        /*
         * @note   1. 需要调用 check_graph_valid 来确定返回的句柄
         *         2. 调用 destroy_runtime_graph() 来释放资源
         */
        graph_t graph = create_runtime_graph("graph", model_name, NULL);
        //TODO: 3.1 需要调用 check_graph_valid 来确定返回的句柄
        if (!check_graph_valid(graph))
        {
            std::cout << "create graph0 failed\n";
            return 1;
        }
    
        if(device!=nullptr)
        {
            //绑定运行 graph 的设备
            set_graph_device(graph,device);
        }
    
    
    
        // 输入图片信息
        // 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;
        //TODO: 4.1 调用 get_graph_input_tensor 获取输入Tensor
        tensor_t input_tensor = get_graph_input_tensor(graph, node_idx, tensor_idx);
        //TODO:4.2 并用 check_tensor_valid 确认返回值是否合法
        if(!check_tensor_valid(input_tensor))
        {
            printf("Get input node failed : node_idx: %d, tensor_idx: %d\n",node_idx,tensor_idx);
            return 1;
        }
        //TODO:4.3 并用 set_tensor_shape 设置输入 Tensor 的shape
        // 输入的信息(1:1张图,3:3通道,img_h:高,img_w:宽)
        int dims[] = {1, 3, img_h, img_w};
        set_tensor_shape(input_tensor, dims, 4);
        //TODO: 5. 调用 prerun_graph 函数预启动图(类似 malloc,申请资源)
        prerun_graph(graph);
    
    
    
    //    //TODO: 7.1.向 input_data 写入输入的数据,
    //    // 将 图片 的路径名转化为 图片内容的矩阵,并赋值给 input_data
    //    get_input_data_ssd(image_file, input_data, img_h,  img_w);
    //    //TODO: 7.2.并调用 set_tensor_buffer 把数据转移到输入Tensor上
    //    set_tensor_buffer(input_tensor, input_data, img_size * 4);
    //    //TODO: 8. 调用 run_graph 运行图(做一次前向传播)
    //    run_graph(graph, 1);
    
    
        /* t0 开始时间
         * t1 结束时间
         * total_time = (t1-t0)*repeat_count 为使用时间
         */
        struct timeval t0, t1;
        float mytime=0;
        // 使用opencv
        cv::VideoCapture capture;
        capture.open(0);
        if(capture.isOpened())
        {
            cv::Mat image;
            std::cout<<"Capture is opened" << std::endl;
            for(;;)
            {
                capture >> image;
                if(image.empty())
                    break;
                //TODO: 7.1 向 input_data 写入输入的数据,
                get_input_data_ssd(image, input_data, img_h,  img_w);
                // 获取当前值,并赋值给 t0(开始值)
                gettimeofday(&t0, NULL);
                //TODO: 7.2 并调用 set_tensor_buffer 把数据转移到输入Tensor上
                set_tensor_buffer(input_tensor, input_data, img_size * 4);
                //TODO: 8. 调用 run_graph 运行图(做一次前向传播)
                run_graph(graph, 1);
                //获取当前时间,并赋值给 t1(结束时间)
                gettimeofday(&t1, NULL);
    
                mytime = (float)((t1.tv_sec * 1000000 + t1.tv_usec) - (t0.tv_sec * 1000000 + t0.tv_usec)) / 1000;
    
    
                std::cout << "--------------------------------------\n";
                std::cout << " times, avg time per run is " << mytime << " ms\n";
                //TODO: 9.1.调用 get_graph_output_tensor 获取输出Tensor
                tensor_t out_tensor = get_graph_output_tensor(graph, 0,0);//"detection_out");
                //TODO: 9.2.并用 get_tensor_shape 取得 tensor 的shape
                /*
                 * [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];
                get_tensor_shape( out_tensor, out_dim, 4);
                //std::cout<<"out_dim" << *out_dim <<std::endl;
                //TODO: 9.3.并用 get_tensor_buffer 取得缓冲区上的数据
                float *outdata = (float *)get_tensor_buffer(out_tensor);
                //std::cout<< "outdata" <<*outdata<<std::endl;
                //获取 outdata 的 检测到目标个数(num)
                int num=out_dim[1];
                //设置阈值,是否为检测目标
                float show_threshold=0.5;
                // 通过 outdata 对 image 进行绘制边框和label信息,并保存
                image = post_process_ssd(image,show_threshold, outdata, num,save_name);
    
    
                //img 图像大小 480,640
                cv::imshow("Sample", image);
    
                if(cv::waitKey(10) >= 0)
                    break;
                //TODO: 9.4 释放 out_tensor 所占空间
                put_graph_tensor(out_tensor);
            }
        }
        //TODO: 10. 最后在退出程序前依次释放各个申请的动态空间
    //    //TODO: 9.4 释放 out_tensor 所占空间
    //    //put_graph_tensor(out_tensor);
        //TODO: 4.4 释放 input_tensor 所占空间
        put_graph_tensor(input_tensor);
        free(input_data);
        //TODO: 3.2 调用 destroy_runtime_graph() 来释放资源
        // 释放 graph 执行所占用的资源
        postrun_graph(graph);
        // 释放 graph 所占空间
        destroy_runtime_graph(graph);
        //TODO: 2.1 请调用 remove_model() to 释放导入的 model
        remove_model(model_name);
    
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:0#02 将 MSSD 制作成视频流

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