目标检测: rtdetr在RK3588上部署

RT-DETR 简介(Real-Time Detection Transformer)

RT-DETR(Real-Time DEtection TRansformer)是由百度提出的一种面向实时场景的端到端目标检测 Transformer 模型 。与传统基于 CNN 的 YOLO 系列不同,RT-DETR 以 DETR 架构 为基础,通过一系列结构与训练策略优化,实现了 无需 NMS 的实时目标检测能力,在速度与精度之间取得了良好平衡。

1. 模型架构特点

RT-DETR 采用 CNN + Transformer Encoder--Decoder 的混合结构:

  • Backbone(主干网络)

    使用高效 CNN(如 ResNet / ConvNeXt / CSP 风格改造)提取多尺度特征,兼顾推理速度与特征表达能力。

  • Hybrid Encoder(混合编码器)

    将 CNN 特征映射与 Transformer 编码器结合,通过注意力机制增强全局建模能力,同时引入轻量化设计以降低计算开销。

  • Transformer Decoder(解码器)

    使用一组固定数量的 Object Queries,直接预测目标类别和边界框,实现:

    • 端到端检测

    • 无需 Anchor

    • 无需 NMS 后处理

2. 核心优势

  • 端到端检测

    直接输出最终检测结果,避免传统检测器复杂的后处理流程(如 NMS)。

  • 实时性能友好

    针对实时推理进行了大量工程级优化,相比原始 DETR 系列,大幅降低收敛时间与推理延迟。

  • 结构规整,利于部署

    模型拓扑清晰,算子类型相对集中,适合进行 ONNX 导出与 NPU 算子映射。

  • 精度--速度平衡良好

    在 COCO 等数据集上,在同等算力条件下可达到接近 YOLO 系列的速度,同时具备 Transformer 的全局建模优势。

下面在RK3588上完成对RTDETR的部署。

cpp后处理:

复制代码
#include "DFINE.h"


inline static int clamp(float val, int min, int max) {
    return val > min ? (val < max ? val : max) : min;
}

static float sigmoid(float x) { return 1.0 / (1.0 + expf(-x)); }

static float unsigmoid(float y) { return -1.0 * logf((1.0 / y) - 1.0); }

inline static int32_t __clip(float val, float min, float max)
{
    float f = val <= min ? min : (val >= max ? max : val);
    return f;
}

static int8_t qnt_f32_to_affine(float f32, int32_t zp, float scale)
{
    float dst_val = (f32 / scale) + zp;
    int8_t res = (int8_t)__clip(dst_val, -128, 127);
    return res;
}

static float deqnt_affine_to_f32(int8_t qnt, int32_t zp, float scale) { return ((float)qnt - (float)zp) * scale; }



int DFINE::proprecessDFINE(std::vector<rknn_output> output, std::vector<float> out_scales,
                            std::vector<int32_t> out_zps,
                            std::vector<cv::Rect> &boxes,
                            std::vector<float> &confidences,
                            std::vector<int> &classids,
                            std::vector<int> &anchorf,
                            float confThres)
{
    // labels:[1,300,0,0]   boxes: [1,300,4,0]  scores: [1,300,0,0]

    float *labels_data = (float*)output[0].buf;
    float *boxes_data = (float*)output[1].buf;
    float *scores_data = (float*)output[2].buf;
    for (int i = 0; i < 300; ++i) {
        std::cout << "scores_data[i]: " << scores_data[i] << std::endl;
        if(scores_data[i] < confThres) continue;
        int labelsid = static_cast<int>(labels_data[i]);
        float x1 = boxes_data[i * 4 + 0] * 640;
        float y1 = boxes_data[i * 4 + 1] * 640;
        float x2 = boxes_data[i * 4 + 2] * 640;
        float y2 = boxes_data[i * 4 + 3] * 640;
        std::cout << "x: " << x1 << std::endl;
        std::cout << "y: " << y1 << std::endl;
        std::cout << "w: " << x2 << std::endl;
        std::cout << "h: " << y2 << std::endl;

        boxes.push_back(cv::Rect(int(x1), int(y1), (int)(x2 - x1), (int)(y2 -y1)));

        float ss = scores_data[i];


        confidences.emplace_back(ss);
        classids.emplace_back(labelsid);
    }


    return 0;
}


cv::Mat DFINE::letterboxYolo(cv::Mat src, int &x_pad, int &y_pad, float &scale,int &channel)
{

    if(channel == 1)
    {
        cv::cvtColor(src,src,cv::COLOR_BGR2GRAY);
    }

    int input_w = modelInputSize.width;  // 模型输入的宽
    int input_h = modelInputSize.height; // 模型输入的高

    int w, h, x, y;
    float r_w = static_cast<float>(input_w) / src.cols;
    float r_h = static_cast<float>(input_h) / src.rows;

    if (r_h > r_w) {
        w = input_w;
        h = std::round(r_w * src.rows);  // 避免浮点数误差
        x = 0;
        y = (input_h - h) / 2;
        scale = r_w;
    } else {
        w = std::round(r_h * src.cols);
        h = input_h;
        x = (input_w - w) / 2;
        y = 0;
        scale = r_h;
    }

    x_pad = x;
    y_pad = y;

    cv::Mat re, out;

    if (src.channels() == 3) {
        re = cv::Mat(h, w, CV_8UC3);
        cv::resize(src, re, re.size(), 0, 0, cv::INTER_LINEAR);
        out = cv::Mat(input_h, input_w, CV_8UC3, cv::Scalar(114, 114, 114));
    } else if (src.channels() == 1) {
        re = cv::Mat(h, w, CV_8UC1);
        cv::resize(src, re, re.size(), 0, 0, cv::INTER_LINEAR);
        out = cv::Mat(input_h, input_w, CV_8UC1, cv::Scalar(114));
    } else {
        return cv::Mat(); // 返回空矩阵,避免错误
    }

    // 将缩放后的图像复制到填充后的图像
    re.copyTo(out(cv::Rect(x, y, re.cols, re.rows)));
    return out;
}


cv::Rect DFINE::get_rect(cv::Mat &img, cv::Rect bbox)
{
    float l, r, t, b;
    float r_w = modelInputSize.width / (img.cols * 1.0);
    float r_h = modelInputSize.height / (img.rows * 1.0);

    if (r_h > r_w) {
        l = bbox.x;
        r = bbox.x + bbox.width;
        t = bbox.y - (modelInputSize.height - r_w * img.rows) / 2;
        b = bbox.y + bbox.height - (modelInputSize.height - r_w * img.rows) / 2;

        l = l / r_w;
        r = r / r_w;
        t = t / r_w;
        b = b / r_w;
    } else {
        l = bbox.x - (modelInputSize.width - r_h * img.cols) / 2;
        r = bbox.x + bbox.width - (modelInputSize.width - r_h * img.cols) / 2;
        t = bbox.y;
        b = bbox.y + bbox.height;

        l = l / r_h;
        r = r / r_h;
        t = t / r_h;
        b = b / r_h;
        l = l < 0 ? 0 : l;
        t = r < 0 ? 0 : t;
    }

    return cv::Rect(round(l), round(t), round(r - l), round(b - t));
}

root@lubancat:~/rknn_dfine_demo_Linux# ./rknn_dfine_demo ../rknn_path/rtdetrv2_FP16.rknn ../rknn_path/dog.jpg 10

结果展示:

相关推荐
陈广亮5 小时前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬5 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia5 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区5 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两8 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪8 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232558 小时前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源
王鑫星8 小时前
SWE-bench 首次突破 80%:Claude Opus 4.5 发布,Anthropic 的野心不止于写代码
人工智能
lnix9 小时前
当“大龙虾”养在本地:我们离“反SaaS”的AI未来还有多远?
人工智能·aigc