news 2026/10/4 1:43:15

基于 TensorRT 7 API 的 RetinaFace 人脸检测部署:tensorrtx/retinaface 完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 TensorRT 7 API 的 RetinaFace 人脸检测部署:tensorrtx/retinaface 完整实战指南
  • 人工智能
  • 深度学习
  • 计算机视觉

【免费下载链接】tensorrtx

Implementation of popular deep learning networks with TensorRT network definition API

项目地址:https://gitcode.com/gh_mirrors/te/tensorrtx
点击查看免费下载

本文以 retinaface/README.md 为主线,结合 retina_r50.cpp、decode.cu、retinaface_trt.py 等源码,系统讲解如何在 tensorrtx 仓库中用 TensorRT 网络定义 API 搭建 RetinaFace(ResNet50 / MobileNet0.25 两种骨干)推理引擎,涵盖权重转换、FP16/INT8 精度配置、C++ 与 Python 双端推理以及后处理解码原理。读完本文,你可以独立完成 RetinaFace 的 TensorRT 引擎生成、序列化与部署,并掌握其自定义解码插件与 INT8 标定的实现细节。

一、项目背景:tensorrtx 中的 RetinaFace

tensorrtx 仓库使用TensorRT 网络定义 API(而非 ONNX/UFF 解析器)逐层重建常见深度学习网络,retinaface目录即是对人脸检测模型 RetinaFace 的完整移植:

  • 原始 PyTorch 实现来自biubug6/Pytorch_Retinaface,作者 fork 出wang-xinyu/Pytorch_Retinaface并额外添加了genwts.py,用于把 PyTorch 权重导出为 TensorRT 所需的.wts文本权重文件;
  • 当前分支使用TensorRT 7 API编写(retina_r50.cpp中createEngine、addConvolutionNd、addPluginV2等均为 TRT 7 风格接口),仓库另有一个基于 TensorRT 4 的旧分支(trt4分支下的retinaface);
  • 支持两种骨干网络:ResNet-50(对应retina_r50.cpp,权重Resnet50_Final.pth)与 MobileNet0.25(对应retina_mnet.cpp,权重mobilenet0.25_Final.pth),二者共享同一套 FPN + SSH + 检测头 + 解码插件结构。

二、源码结构与完整推理流水线

retinaface目录下关键文件及职责如下:

文件职责
decode.h定义输入尺寸INPUT_H=480、INPUT_W=640、Detection结构体及DecodePlugin插件类声明
decode.cuDecode 插件的 CUDA Kernel 实现:在 GPU 上完成先验框解码、置信度计算与关键点回归
retina_r50.cppResNet-50 骨干的完整网络构建、引擎序列化(-s)与推理(-d)主程序
retina_mnet.cppMobileNet0.25 骨干的对应实现
common.hpp图像预处理、NMS、权重加载、BatchNorm 折叠为 Scale 层等公共函数
calibrator.h / calibrator.cppINT8 熵校准器Int8EntropyCalibrator2实现
retinaface_trt.pyPython 推理封装,反序列化 engine 并完成前后处理
CMakeLists.txt编译decodeplugin动态库及两个可执行文件

整条推理流水线为:图像预处理(letterbox 缩放 + 128 灰边填充)→ ResNet-50/MobileNet0.25 骨干 → FPN 特征融合 → SSH 上下文模块 → Bbox/Class/Landmark 三个检测头 → Decode 插件(先验框解码)→ CPU 端 NMS → 绘制结果。网络完全由 TensorRT API 手工搭建,不经过任何模型解析器。

三、输入尺寸、输出格式与核心配置宏

3.1 输入与输出张量

输入/输出规格定义在两处:

  • 输入尺寸定义在 decode.h:INPUT_H = 480、INPUT_W = 640,注释明确要求H、W 必须能被 32 整除(因为下采样到 1/8、1/16、1/32 三个尺度);
  • 输入张量名为data,形状{3, INPUT_H, INPUT_W}(CHW);输出张量名为prob,由 decode.cu 决定输出维度。

输出尺寸由 retina_r50.cpp 中的公式给出:

OUTPUT_SIZE = (H/8 * W/8 + H/16 * W/16 + H/32 * W/32) * 2 * 15 + 1

代入 480×640 即(60×80 + 30×40 + 15×20) × 30 + 1 = 189001。其含义为:三个尺度上每个特征点对应 2 个 anchor,每个 Detection 记录 15 个 float(4 个 bbox 坐标 + 1 个置信度 + 10 个关键点坐标),首元素output[0]存放检测数量。对应的Detection结构体(decode.h):

struct alignas(float) Detection { float bbox[4]; // x1, y1, x2, y2 float class_confidence; // 分类置信度 float landmark[10]; // 5 个关键点坐标 };

3.2 编译期宏配置

README 列出的全部可配置宏集中在 retina_r50.cpp 文件顶部:

#define USE_INT8 // 精度开关:USE_INT8 / USE_FP16 / USE_FP32 三选一 #define DEVICE 0 // GPU id #define BATCH_SIZE 1 // 批大小 #define CONF_THRESH 0.75 // 置信度阈值(绘图时过滤) #define IOU_THRESH 0.4 // NMS 的 IoU 阈值
宏位置默认值说明
USE_FP16/USE_INT8/USE_FP32retina_r50.cpp/retina_mnet.cpp顶部retina_r50默认USE_INT8,retina_mnet默认USE_FP16精度三选一,只能启用其一;USE_INT8需要 GPU 支持 Fast INT8 且需要标定数据
DEVICE同上0指定 CUDA 设备 id,main中通过cudaSetDevice(DEVICE)生效
BATCH_SIZE同上1引擎批大小;推理时同一张图会被复制 BATCH_SIZE 份送入
CONF_THRESH同上0.75最终绘制的置信度下限,低于此值的检测框被跳过
IOU_THRESH同上0.4传入 NMS 的 IoU 阈值
INPUT_H/INPUT_Wdecode.h480/640网络输入分辨率,必须能被 32 整除

在 retina_r50.cpp 中,精度选择直接映射为 Builder 配置:

#if defined(USE_FP16) config->setFlag(BuilderFlag::kFP16); #elif defined(USE_INT8) std::cout << "Your platform support int8: " << builder->platformHasFastInt8() << std::endl; assert(builder->platformHasFastInt8()); config->setFlag(BuilderFlag::kINT8); Int8EntropyCalibrator2 *calibrator = new Int8EntropyCalibrator2( 1, INPUT_W, INPUT_H, "./widerface_calib/", "r50_int8calib.table", INPUT_BLOB_NAME); config->setInt8Calibrator(calibrator); #endif

可见 INT8 模式会断言平台支持platformHasFastInt8(),并注册一个以./widerface_calib/为标定图片目录、缓存表名为r50_int8calib.table的熵校准器。

四、第一步:从 PyTorch 权重导出 retinaface.wts

README 的 Run 流程第一步是从 PyTorch 实现生成.wts权重文件:

git clone https://github.com/wang-xinyu/Pytorch_Retinaface.git # 下载权重文件 'Resnet50_Final.pth',放到 Pytorch_Retinaface/weights 目录 cd Pytorch_Retinaface python detect.py --save_model # 保存 PyTorch 模型权重 python genwts.py # 生成 retinaface.wts # 会在当前目录生成 'retinaface.wts'

要点:

  • .wts是 tensorrtx 系列通用的轻量权重格式:首行是权重块数量,随后每行依次为权重名称、元素个数(十进制)与十六进制数据,解析逻辑见 common.hpp 的loadWeights;
  • 导出后的retinaface.wts必须放到本仓库retinaface目录下,因为 retina_r50.cpp 硬编码从"../retinaface.wts"相对路径加载权重;
  • 若使用 MobileNet0.25 骨干,则用mobilenet0.25_Final.pth走完全相同的流程生成retinaface.wts,其余步骤与retina_r50几乎一致。

五、第二步:构建、序列化引擎与 C++ 推理

5.1 编译

将retinaface.wts放入tensorrtx/retinaface后:

git clone https://gitcode.com/gh_mirrors/te/tensorrtx cd tensorrtx/retinaface mkdir build cd build cmake .. make

CMakeLists.txt 会完成三件事:

  1. 用cuda_add_library(decodeplugin SHARED .../decode.cu)编译libdecodeplugin.so(链接nvinfer与cudart),这是 Decode 插件的运行时库,推理前必须保证其已生成;
  2. 编译retina_r50(源码 calibrator.cpp + retina_r50.cpp);
  3. 编译retina_mnet(同样依赖decodeplugin),并链接 OpenCV、nvinfer、cudart。

5.2 序列化与推理

sudo ./retina_r50 -s # build and serialize model to file i.e. 'retina_r50.engine' wget https://github.com/Tencent/FaceDetection-DSFD/raw/master/data/worlds-largest-selfie.jpg sudo ./retina_r50 -d # deserialize model file and run inference

程序仅接受一个命令行参数(retina_r50.cpp):

  • -s:走APIToModel()→createEngine()构建网络并序列化,将二进制 engine 写入retina_r50.engine;
  • -d:从retina_r50.engine反序列化引擎,读取worlds-largest-selfie.jpg执行推理。

推理主流程(retina_r50.cpp)值得注意的细节:

  • 图像预处理调用 common.hpp 的preprocess_img:按长边等比缩放、短边用128 灰边填充到 480×640;
  • 送入网络前做减均值归一化:BGR 三通道分别减去104.0 / 117.0 / 123.0(与 PyTorch 训练设置一致);
  • doInference中为输入输出分别分配 CUDA 显存,通过cudaMemcpyAsync+context.enqueue完成异步推理;
  • 推理耗时以微秒为单位打印(代码中循环执行 1000 次取耗时,方便做性能测量);
  • 输出经过 common.hpp 的nms()(按置信度降序排序、贪心 IoU 抑制)后,置信度大于CONF_THRESH的框用绿色矩形绘制,5 个关键点用不同颜色的圆点标注,最终保存为0_result.jpg。

retina_mnet的用法完全相同,仅需先用mobilenet0.25_Final.pth生成retinaface.wts并运行./retina_mnet。

六、核心原理:Decode 插件与先验框解码

RetinaFace 的输出头(Bbox 4 维、Class 2 维、Landmark 10 维)经拼接后,解码工作全部下沉到自定义 CUDA 插件Decode_TRT中完成,避免在 CPU 上逐个解码带来开销。

6.1 网络侧的三路输入

在 retina_r50.cpp 中,三个尺度分别生成:

  • BboxHead:2 * 4个通道(2 个 anchor × 4 坐标);
  • ClassHead:2 * 2个通道(2 个 anchor × 2 类,face/background);
  • LandmarkHead:2 * 10个通道(2 个 anchor × 5 点 × 2 坐标)。

随后通过addConcatenation把每个尺度的三路输出拼成一个张量,再经插件注册表查找"Decode_TRT"版本"1"创建插件:

auto creator = getPluginRegistry()->getPluginCreator("Decode_TRT", "1"); IPluginV2 *pluginObj = creator->createPlugin("decode", &pfc); ITensor* inputTensors[] = {cat1->getOutput(0), cat2->getOutput(0), cat3->getOutput(0)}; auto decodelayer = network->addPluginV2(inputTensors, 3, *pluginObj); decodelayer->getOutput(0)->setName(OUTPUT_BLOB_NAME); network->markOutput(*decodelayer->getOutput(0));

插件声明与注册见 decode.h(DecodePluginCreator通过REGISTER_TENSORRT_PLUGIN宏注册)。

6.2 GPU Kernel 的解码逻辑

插件核心是 decode.cu 的CalDetectionkernel。forwardGpu以步长 8/16/32 依次处理三个特征尺度,对应 anchor 基础尺寸逐级 ×4(16 → 64 → 256,见 decode.cu)。每个特征点上执行:

  1. 置信度计算:对两个类别分数做 softmax 变体conf2 = exp(conf2) / (exp(conf1) + exp(conf2)),低于 0.02 的候选直接跳过(decode.cu),并用atomicAdd统计最终检测数;
  2. 先验框生成:prior = ((x+0.5)/w, (y+0.5)/h, anchor*(k+1)/W, anchor*(k+1)/H)(decode.cu);
  3. bbox 解码:中心坐标使用方差 0.1、宽高使用方差 0.2 的反向解码公式,随后还原为x1,y1,x2,y2并乘以输入分辨率(decode.cu);
  4. 关键点解码:5 个关键点(双眼、鼻尖、双嘴角)同样以 0.1 方差解码并换算回像素坐标(decode.cu)。

解码后的数据直接以Detection结构体逐条写入输出缓冲区,首元素为检测数量,后续每 15 个 float 对应一个检测结果——这也是 C++nms()与 Python 端post_process能直接解析的基础。

七、Python 推理封装

README 第 4 步提供了基于 TensorRT Python API 的推理封装 retinaface_trt.py,前置条件:

  • 安装python-tensorrt、pycuda等依赖;
  • 已生成retina_r50.engine且libdecodeplugin.so已编译完成。

运行方式:

python retinaface_trt.py

脚本的核心流程(retinaface_trt.py):

  1. 用ctypes.CDLL("build/libdecodeplugin.so")加载自定义插件库,使引擎反序列化时能找到Decode_TRT插件;
  2. Retinaface_trt类读取build/retina_r50.engine,分配分页锁定内存(cuda.pagelocked_empty)与显存缓冲;
  3. preprocess_image与 C++ 版一致:长边缩放 +(128,128,128)填充到 480×640、减(104,117,123)均值、HWC→CHW→NCHW;
  4. infer通过cuda.memcpy_htod_async+context.execute_async+cuda.memcpy_dtoh_async异步推理;
  5. post_process解析输出:读取output[0]检测数 → 按 15 维切分 → 阈值过滤 → 用torchvision.ops.nms做 NMS → 把填充区域坐标还原到原图;
  6. 每个检测框绘制矩形与 5 个关键点,结果保存为output_<原文件名>(默认输入zidane.jpg);
  7. 脚本还演示了用threading.Thread并发调用infer(同一 PyCUDA context 内 push/pop 切换),可作为多线程推理的参考模板。

八、INT8 量化部署

README 单独一节给出 INT8 量化的完整流程,结合 calibrator.h 可理解其实现:

  1. 准备标定图片:从训练集随机挑选上千张图片即可(人脸检测场景建议来自 WiderFace);也可下载 README 提供的widerface_calib标定图片包(GoogleDrive 或百度网盘,提取码a9wh);
  2. 解压:将标定图片目录解压到retinaface/build下(即./widerface_calib/,与 retina_r50.cpp 中的路径硬编码一致);
  3. 切换精度:在retina_r50.cpp顶部把USE_INT8设为启用状态(#define USE_INT8),重新make;
  4. 重新序列化并测试:再次执行./retina_r50 -s生成 INT8 引擎,然后./retina_r50 -d验证精度。

实现层面,Int8EntropyCalibrator2(calibrator.h)派生自nvinfer1::IInt8EntropyCalibrator2,采用Entropy Calibrator 2(kENTROPY_CALIBRATION_2)算法:每次getBatch从img_files_中读取一个 batch 的图片并缩放到输入尺寸;标定结果写入缓存表r50_int8calib.table,readCalibrationCache/writeCalibrationCache使二次构建可复用缓存、跳过重新标定。由于retina_r50.cpp默认启用USE_INT8,若你首次构建还没有标定数据,记得先切换为USE_FP16或USE_FP32。

九、自定义与扩展要点

  • 修改输入分辨率:只需改动 decode.h 的INPUT_H/INPUT_W,要求能被 32 整除;注意同时更新 Python 端 retinaface_trt.py 中手写的INPUT_H/INPUT_W;
  • 更换 GPU:修改DEVICE宏;多卡机器上可通过cudaSetDevice(DEVICE)定位目标卡;
  • 调整批大小:修改BATCH_SIZE后重新-s序列化;需注意当前推理代码在多 batch 时是将同一张图复制多份输入(retina_r50.cpp),若要处理不同图片需自行改造数据装载;
  • 精度与阈值调优:CONF_THRESH/IOU_THRESH可按场景调节;Decode 插件内 0.02 的低置信度预筛(decode.cu)会影响最终检测数量上限;
  • BN 折叠实现:所有 BatchNorm 层在 common.hpp 的addBatchNorm2d中通过scale/shift/power三个权值折叠进IScaleLayer,这是纯 API 建网省去 BN 层、提升运行效率的关键技巧,其他 tensorrtx 模型也普遍复用该模式。

十、延伸阅读

  • 构建脚本与依赖:见 retinaface/CMakeLists.txt(OpenCV、CUDA、nvinfer及aarch64嵌入式平台路径分支);
  • 权重加载与图像预处理细节:见 retinaface/common.hpp;
  • INT8 标定器实现:见 retinaface/calibrator.h 与 retinaface/calibrator.cpp;
  • 仓库总览与更多模型部署说明:见根目录 README.md 及tutorials/目录下的系列文档(安装、性能测量、INT8/FP16 支持检查等)。
  • 人工智能
  • 深度学习
  • 计算机视觉

【免费下载链接】tensorrtx

Implementation of popular deep learning networks with TensorRT network definition API

项目地址:https://gitcode.com/gh_mirrors/te/tensorrtx
点击查看免费下载
上一篇:OpenAI 流式响应实战:Python 里把等待压到首字符
下一篇:OpenCore Legacy Patcher 2.5 实操笔记:老 Mac 升级 macOS

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 1:42:42

PDFsam Basic 批量处理实战:拆分合并旋转提取,敏感文件零上传

PDFsam Basic 批量处理实战&#xff1a;拆分合并旋转提取&#xff0c;敏感文件零上传 在线 PDF 工具方便&#xff0c;但合同、成绩单、身份证扫描件上传到陌生服务器&#xff0c;安全性无从考证。PDFsam Basic&#xff08;开源 AGPL-3&#xff09;把拆分合并旋转提取四大刚需全…

作者头像 李华
网站建设 2026/10/4 1:41:35

Python中常用的列表函数

Python中常用的列表函数函数功能说明len(list)获取列表的长度max(list)/min(list)获取列表中的最大值、最小值sorted(list)对列表排序并返回新的列表list.sort(reverseFalse)对列表排序&#xff08;True为降序&#xff0c;默认值False为升序&#xff09;list.reverse()逆序现有…

作者头像 李华