使用gapi的传统推理的简单示例
标签搜索
侧边栏壁纸
  • 累计撰写 36 篇文章
  • 累计收到 3 条评论

使用gapi的传统推理的简单示例

CloudZ
2023-05-20 / 0 评论 / 7 阅读 / 正在检测是否收录...

https://forum.opencv.org/t/simple-example-of-traditional-inference-using-gapi/2872


#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/gapi.hpp"
#include "opencv2/gapi/core.hpp"
#include "opencv2/gapi/imgproc.hpp"
#include "opencv2/gapi/infer.hpp"
#include "opencv2/gapi/infer/ie.hpp"
#include "opencv2/gapi/cpu/gcpukernel.hpp"
#include "opencv2/gapi/streaming/cap.hpp"

// 命令行参数
const std::string about = "This is an OpenCV-based version of Security Barrier Camera example";
const std::string keys =
    "{ h help | | print this help message }"
    "{ input | | Path to an input img file }"
    "{ fdm | | IE face detection model IR }"
    "{ fdw | | IE face detection model weights }"
    "{ fdd | | IE face detection device }";

namespace custom {
//! [G_API_NET]
// 面部检测器:接受一个Mat,返回另一个Mat
G_API_NET(Faces, <cv::GMat(cv::GMat)>, "face-detector");

// SSD后处理函数 - 这不是网络而是内核。
// 内核体单独声明,这只是一个接口。
// 此操作接受两个Mats(检测结果和源图像),
// 并返回ROI的向量(由默认阈值过滤)。
// 阈值(或要选择的类)可能成为参数,但由于
// 此内核是自定义的,因此没有太大意义。
G_API_OP(PostProc, <cv::GArray<cv::Rect>(cv::GMat, cv::GMat)>, "custom.fd_postproc") {
    static cv::GArrayDesc outMeta(const cv::GMatDesc &, const cv::GMatDesc &) {
        // 此函数需要由G-API引擎确定
        // 给定输入参数时输出格式是什么。
        // 由于输出是数组(具有特定类型),
        // 因此没有什么可描述的。
        return cv::empty_array_desc();
    }
};

// 上述内核的基于OpenCV的实现。
GAPI_OCV_KERNEL(OCVPostProc, PostProc) {
    static void run(const cv::Mat &in_ssd_result,
                    const cv::Mat &in_frame,
                    std::vector<cv::Rect> &out_faces) {
        const int MAX_PROPOSALS = 200;
        const int OBJECT_SIZE = 7;
        const cv::Size upscale = in_frame.size();
        const cv::Rect surface({0,0}, upscale);
        out_faces.clear();
        const float *data = in_ssd_result.ptr<float>();
        for (int i = 0; i < MAX_PROPOSALS; i++) {
            const float image_id = data[i * OBJECT_SIZE + 0]; // batch id
            const float confidence = data[i * OBJECT_SIZE + 2];
            const float rc_left = data[i * OBJECT_SIZE + 3];
            const float rc_top = data[i * OBJECT_SIZE + 4];
            const float rc_right = data[i * OBJECT_SIZE + 5];
            const float rc_bottom = data[i * OBJECT_SIZE + 6];
            if (image_id < 0.f) { // indicates end of detections
                break;
            }
            if (confidence < 0.5f) { // a hard-coded snapshot
                continue;
            }
            // 将浮点坐标转换为绝对图像
            // 帧坐标;剪切源图像边界。
            cv::Rect rc;
            rc.x = static_cast<int>(rc_left * upscale.width);
            rc.y = static_cast<int>(rc_top * upscale.height);
            rc.width = static_cast<int>(rc_right * upscale.width) - rc.x;
            rc.height = static_cast<int>(rc_bottom * upscale.height) - rc.y;
            out_faces.push_back(rc & surface);
        }
    }
};
//! [Postproc]
} // namespace custom

int main(int argc, char *argv[])
{
    cv::CommandLineParser cmd(argc, argv, keys);
    cmd.about(about);
    if (cmd.has("help")) {
        cmd.printMessage();
        return 0;
    }

    const std::string input = cmd.get<std::string>("input");

    // 表达我们的处理管道。使用基于lambda的构造函数
    // 用于在专用作用域中保留所有临时对象。
    //! [GComputation]
    cv::GComputation pp([]() {
        // 声明一个空的GMat - 管道的开端。
        cv::GMat in;

        // 在输入帧上运行面部检测。结果是一个单独的GMat,
        // 在内部表示1x1x200x7 SSD输出。
        // 这是infer的单补丁版本:
        // - 推理在整个输入图像上运行;
        // - 图像自动转换并调整为网络预期的格式。
        cv::GMat detections = cv::gapi::infer<custom::Faces>(in);

        // 使用自定义内核解析SSD输出到ROI(矩形)列表。
        // 注意:解析SSD可能成为“标准”内核。
        cv::GArray<cv::Rect> faces = custom::PostProc::on(detections, in);

        // 现在指定计算的边界 - 我们的管道消耗
        // 一个图像并产生一个输出。
        return cv::GComputation(cv::GIn(in), cv::GOut(faces));
    });
    //! [GComputation]

    // 注意:此时加载尺寸可能非常有用!

    // 在我们的计算定义之后,指定它应如何执行。
    // 执行由我们用于编译管道(这是不同步骤)的推理后端和内核后端定义。
    // 声明FaceDetection网络的IE参数。注意这里custom::Face
    // 是我们先前在GAPI_NETWORK()中指定的类型名称。
    // cv::gapi::ie::Params<>是通用配置描述,它是
    // 针对我们使用的每个特定网络进行专门化。
    //
    // OpenCV DNN后端将具有其自己的带有设置的参数结构
    // 与OpenCV DNN模块相关。其他可能的推理也适用
    // 后端...
    //! [Param_Cfg]
    auto det_net = cv::gapi::ie::Params<custom::Faces> {
        cmd.get<std::string>("fdm"), // read cmd args: path to topology IR
        cmd.get<std::string>("fdw"), // read cmd args: path to weights
        cmd.get<std::string>("fdd"), // read cmd args: device specifier
    };

    // 形成一个内核包(带有我们的单个基于OpenCV的后处理实现)
    // 和一个网络包(持有我们的三个网络)。
    auto kernels = cv::gapi::kernels<custom::OCVPostProc>();
    auto networks = cv::gapi::networks(det_net);

    {   // inference !
        // 声明我们将从管道接收的数据对象。
        cv::Mat in_frame = cv::imread("input");  // the input !
        // 捕获到的帧本身
        std::vector<cv::Rect> faces;             // 检测到的面部数组

        pp.apply(cv::gin(in_frame), cv::gout(faces), cv::compile_args(kernels, networks));

        // do something with the face rects
    }

    return 0;
}


0

评论

博主关闭了当前页面的评论