esp_dl库 yolov_detect代码解析
app_main,ccp源码如下#includecoco_detect.hpp#includedl_image_jpeg.hpp#includeesp_log.h#ifCONFIG_COCO_DETECT_MODEL_IN_SDCARD#includebsp/esp-bsp.h#endifexternconstuint8_tbus_jpg_start[]asm(_binary_bus_jpg_start);externconstuint8_tbus_jpg_end[]asm(_binary_bus_jpg_end);constchar*TAGyolo11n;externCvoidapp_main(void){#ifCONFIG_COCO_DETECT_MODEL_IN_SDCARDESP_ERROR_CHECK(bsp_sdcard_mount());#endif//jpeg_img_t是dl::image命名空间下的一个结构体类型表示JPEG图像数据//在 dl 这个大命名空间里找到 image 子命名空间再从中取出 jpeg_img_t 这个类型用它来定义变量 jpeg_imgdl::image::jpeg_img_tjpeg_img{.data(void*)bus_jpg_start,.data_len(size_t)(bus_jpg_end-bus_jpg_start)};autoimgdl::image::sw_decode_jpeg(jpeg_img,dl::image::DL_IMAGE_PIX_TYPE_RGB888);COCODetect*detectnewCOCODetect();autodetect_resultsdetect-run(img);for(constautores:detect_results){ESP_LOGI(TAG,[category: %d, score: %f, x1: %d, y1: %d, x2: %d, y2: %d],res.category,res.score,res.box[0],res.box[1],res.box[2],res.box[3]);}delete detect;heap_caps_free(img.data);#ifCONFIG_COCO_DETECT_MODEL_IN_SDCARDESP_ERROR_CHECK(bsp_sdcard_unmount());#endif}逐行代码解读1.asm(“_binary_bus_jpg_start”);extern const uint8_t bus_jpg_start[] asm(“_binary_bus_jpg_start”);extern const uint8_t bus_jpg_end[] asm(“_binary_bus_jpg_end”);asm(“_binary_bus_jpg_start”) 的含义这是 GCC 编译器扩展语法它的作用相当于给 C 变量起了一个汇编层面的别名。你可以把它理解为C 代码里我叫 bus_jpg_start但链接器在二进制文件里找我的名字时请认准 _binary_bus_jpg_start部分含义extern这个变量不在当前文件定义是在别处链接器生成的目标文件里定义的const uint8_t类型无符号 8 位整数常量即字节数组bus_jpg_start[]C 层面的变量名数组形式长度未知由链接器决定asm(_binary_bus_jpg_start)GCC 扩展告诉编译器这个变量在汇编层面实际对应的符号名是_binary_bus_jpg_startextern说明定义不在当前文件中定义但是我们找遍代码发现它没有定义的地方其实他在CMakeLists.txt文件中定义定义如下旧版 ESP-IDF方式set(embed_filesbus.jpg)# 或者多个文件set(embed_filesbus.jpglogo.pngmodel.bin)新版定义方式target_add_aligned_binary_data(${COMPONENT_LIB}images/bus.jpgBINARY)2.代码讲解dl::image::jpeg_img_t jpeg_img {.data (void *)bus_jpg_start, .data_len (size_t)(bus_jpg_end - bus_jpg_start)};在 dl 这个大命名空间里找到 image 子命名空间再从中取出 jpeg_img_t 这个类型用它来定义变量 jpeg_img。符号含义dlESP-DL 库的根命名空间namespace所有 ESP-DL 的 API 都在这里面imagedl下的子命名空间专门封装图像编解码、预处理相关功能jpeg_img_tdl::image内部定义的一个结构体或 typedef用来描述一张 JPEG 图片的元数据bus_jpg_start / bus_jpg_end 是什么这两个名字通常是链接器自动生成的符号。在 ESP-IDF 中如果你用 COMPONENT_EMBED_FILES 或 target_add_binary_data 把一张 .jpg 图片嵌入到固件里链接器会自动生成两个符号bus_jpg_start图片数据在 Flash 中的起始地址bus_jpg_end图片数据在 Flash 中的结束地址所以这段代码的整体逻辑就是把嵌入到固件中的 bus.jpg 图片包装成 ESP-DL 能识别的 dl::image::jpeg_img_t 结构体后续就可以传给 ESP-DL 的图像解码函数去处理。3. auto img dl::image::sw_decode_jpeg(jpeg_img, dl::image::DL_IMAGE_PIX_TYPE_RGB888);auto 是 C11 引入的关键字让编译器自动推断变量类型不用手动写出那个可能很长的类型名。在 ESP-DL 中sw_decode_jpeg 的返回值通常是一个封装好的图像张量/矩阵对象dl::image::sw_decode_jpeg —— 函数全名层级含义dlESP-DL 库的根命名空间imagedl下的图像处理子命名空间sw_decode_jpeg具体的函数名软件Software解码 JPEG参数讲解参数dl::image::DL_IMAGE_PIX_TYPE_RGB888部分含义dl::image同样的命名空间DL_IMAGE_PIX_TYPE_RGB888像素格式每个像素用3 字节表示分别是R红、G绿、B蓝每个通道 8 位共 24 位ESP-DL 的神经网络模型通常要求输入是 RGB888 格式所以这里指定把 JPEG 解码成 RGB 裸数据方便后续直接喂给模型。4.COCODetect *detect new COCODetect();这行代码是 C 中在堆内存上动态创建一个目标检测模型对象。逐段拆解如下COCODetect —— 模型类这是 ESP-DL Model Zoo 里提供的一个目标检测模型类名字里的 COCO 指的是 MS COCO 数据集一个包含 80 类常见物体的标准数据集如人、车、猫、狗、椅子等。new COCODetect() —— 动态内存分配这是 C 的 new 运算符做两件事步骤动作① 分配内存在堆Heap上申请一块足够大的连续内存② 调用构造函数在这块内存上执行COCODetect::COCODetect()初始化模型权重、配置参数等为什么用 new放在栈上 而不是直接定义对象方式代码内存位置特点栈分配COCODetect detect;栈Stack自动释放但 ESP32 栈通常只有8~16KB模型权重根本放不下堆分配✅new COCODetect()堆Heap/PSRAM空间大几百 KB 到几 MB生命周期由程序员控制但必须手动释放模型权重本身可能就占 几百 KB加上中间特征图缓存只能放堆上。5.auto detect_results detect-run(img);这行代码是 通过指针调用目标检测模型的推理方法并用引用接收结果。承接你之前创建的对象逐段拆解detect-run(img) —— 指针调用成员函数| 符号 | 含义 || ---------- | ------------------------------------------------- ||detect| 之前new COCODetect()创建的指针COCODetect*类型 ||-|箭头运算符用于通过指针访问对象的成员函数或变量 ||.run()| 等价于(*detect).run()即先解引用指针得到对象再调用方法 ||run(img)| 让模型对图像img执行一次前向推理Forward Inference|run(img) 内部在做什么这是模型推理的核心一步输入: img (RGB888 图像张量如 320×320×3)│▼┌──────────────────┐│ 预处理 │ 归一化、减均值、除标准差、调整尺寸│ (Preprocess) │└──────────────────┘│▼┌──────────────────┐│ 神经网络前向传播 │ 卷积 → 激活 → 池化 → … → 输出层│ (Forward) │└──────────────────┘│▼┌──────────────────┐│ 后处理 │ NMS 非极大值抑制、置信度过滤、解码边界框│ (Postprocess) │└──────────────────┘│▼输出: 检测框列表每个框含类别、分数、坐标4.auto detect_results —— 引用接收返回值部分含义auto编译器自动推导返回类型通常是std::vectorDetectResult或dl::Tensor等引用Reference给返回值起别名不拷贝数据为什么加 很重要run() 返回的检测结果通常是一个包含多个检测框的列表/向量数据量不小。如果写成auto detect_results detect-run(img); // ❌ 拷贝把结果复制一份到新变量auto detect_results detect-run(img); // ✅ 引用直接绑定到原结果零拷贝6.坐标显示与内存释放for(constautores:detect_results){ESP_LOGI(TAG,[category: %d, score: %f, x1: %d, y1: %d, x2: %d, y2: %d],res.category,res.score,res.box[0],res.box[1],res.box[2],res.box[3]);}deletedetect;heap_caps_free(img.data);细节讲解。函数内部的操作。COCODetect *detect new COCODetect();内部详解源代码如下COCODetect::COCODetect(model_type_t model_type,boollazy_load):m_model_type(model_type){switch(model_type){casemodel_type_t::YOLO11N_S8_V1:casemodel_type_t::YOLO11N_320_S8_V1:m_score_thr[0]coco_detect::Yolo11n::default_score_thr;m_nms_thr[0]coco_detect::Yolo11n::default_nms_thr;break;}if(lazy_load){m_modelnullptr;}else{load_model();}}因为switch语句第一条没有break所以m_score_thr[0] coco_detect::Yolo11n::default_score_thr;m_nms_thr[0] coco_detect::Yolo11n::default_nms_thr;最后两行代码都会执行。coco_detect::Yolo11n的定义如下其中default_score_thr表示置信度阈值default_nms_thr非极大值抑制阈值 / 交并比阈值namespacecoco_detect{classYolo11n:publicdl::detect::DetectImpl{public:staticconstexprfloatdefault_score_thr0.25;staticconstexprfloatdefault_nms_thr0.7;Yolo11n(constchar*model_name,floatscore_thr,floatnms_thr);};}// namespace coco_detectSome of you may be curious about how these parameters are passed.参数在coco_detect.hpp类COCODetect的构造中COCODetect(model_type_t model_type static_castmodel_type_t(CONFIG_DEFAULT_COCO_DETECT_MODEL),bool lazy_load true);源码如下。classCOCODetect:publicdl::detect::DetectWrapper{public:typedefenum{YOLO11N_S8_V1,YOLO11N_320_S8_V1,}model_type_t;COCODetect(model_type_t model_typestatic_castmodel_type_t(CONFIG_DEFAULT_COCO_DETECT_MODEL),boollazy_loadtrue);private:voidload_model()override;model_type_t m_model_type;};紧接着讲COCODetect *detect new COCODetect();因为lazy_loadtrue所以执行m_model nullptr;也就是不加载模型然后我回到了主函数执行 auto detect_results detect-run(img);进入detect-run(img);源代码如下。std::vectordl::cls::result_tClsWrapper::run(constdl::image::img_timg,runtime_mode_t mode){if(!m_model){load_model();}returnm_model-run(img,mode);}其中 load_model();用来加载模型进入到 load_model();源码如下voidCOCODetect::load_model(){switch(m_model_type){casemodel_type_t::YOLO11N_S8_V1:#ifCONFIG_FLASH_COCO_DETECT_YOLO11N_S8_V1||CONFIG_COCO_DETECT_MODEL_IN_SDCARDm_modelnewcoco_detect::Yolo11n(coco_detect_yolo11n_s8_v1.espdl,m_score_thr[0],m_nms_thr[0]);#elseESP_LOGE(coco_detect,coco_detect_yolo11n_s8_v1 is not selected in menuconfig.);#endifbreak;casemodel_type_t::YOLO11N_320_S8_V1:#ifCONFIG_FLASH_COCO_DETECT_YOLO11N_320_S8_V1||CONFIG_COCO_DETECT_MODEL_IN_SDCARDm_modelnewcoco_detect::Yolo11n(coco_detect_yolo11n_320_s8_v1.espdl,m_score_thr[0],m_nms_thr[0]);#elseESP_LOGE(coco_detect,coco_detect_yolo11n_320_s8_v1 is not selected in menuconfig.);#endifbreak;}}模型加载核心代码m_model new coco_detect::Yolo11n(“coco_detect_yolo11n_s8_v1.espdl”, m_score_thr[0], m_nms_thr[0]);模型位置定义放在models\coco_detect\CMakeLists.txt中定义如下if(CONFIG_FLASH_COCO_DETECT_YOLO11N_S8_V1)list(APPEND models ${models_dir}/coco_detect_yolo11n_s8_v1.espdl)m_model new coco_detect::Yolo11n(“coco_detect_yolo11n_320_s8_v1.espdl”, m_score_thr[0], m_nms_thr[0]);源码如下namespacecoco_detect{Yolo11n::Yolo11n(constchar*model_name,floatscore_thr,floatnms_thr){#if!CONFIG_COCO_DETECT_MODEL_IN_SDCARDboolparam_copytrue;if(heap_caps_get_total_size(MALLOC_CAP_SPIRAM)1024*1024*9){//内存申请param_copyfalse;}m_modelnewdl::Model(path,model_name,static_castfbs::model_location_type_t(CONFIG_COCO_DETECT_MODEL_LOCATION),0,dl::MEMORY_MANAGER_GREEDY,nullptr,param_copy);#elseautosd_pathstd::filesystem::path(CONFIG_BSP_SD_MOUNT_POINT)/CONFIG_COCO_DETECT_MODEL_SDCARD_DIR/model_name;m_modelnewdl::Model(sd_path.c_str(),fbs::MODEL_LOCATION_IN_SDCARD);#endifm_model-minimize();m_image_preprocessornewdl::image::ImagePreprocessor(m_model,{0,0,0},{255,255,255});m_image_preprocessor-enable_letterbox({114,114,114});m_postprocessornewdl::detect::yolo11PostProcessor(m_model,m_image_preprocessor,0.25,0.7,10,{{8,8,4,4},{16,16,8,8},{32,32,16,16}});}}// namespace coco_detect代码行功能说明作用/目的m_model-minimize();模型内存瘦身释放.espdl解析阶段产生的临时缓冲区、元数据表等仅保留推理必需的权重张量和网络结构降低内存占用m_image_preprocessor new dl::image::ImagePreprocessor(m_model, {0, 0, 0}, {255, 255, 255});创建图像预处理器实例化预处理对象绑定当前模型获取输入尺寸要求设置归一化参数mean0, std255将像素值从[0,255]线性缩放到[0.0, 1.0]m_image_preprocessor-enable_letterbox({114, 114, 114});启用 Letterbox 缩放开启保持宽高比的等比缩放模式缩放后不足模型输入尺寸的部分用 RGB(114,114,114) 中灰色填充防止图像拉伸变形导致检测精度下降m_postprocessor new dl::detect::yolo11PostProcessor(m_model, m_image_preprocessor, 0.25, 0.7, 10, {{8, 8, 4, 4}, {16, 16, 8, 8}, {32, 32, 16, 16}});创建 YOLO11 后处理器实例化结果解码器绑定模型和预处理器设置置信度阈值0.25低于 25% 的框丢弃、NMS IoU 阈值0.7重叠度超过 70% 的去重、每类最多保留10个框传入三级特征图的 stride/anchor 配置用于将神经网络输出的原始张量解码为带类别、分数、坐标的检测框列表模型加载完毕后回到std::vectordl::cls::result_t ClsWrapper::run(const dl::image::img_t img, runtime_mode_t mode)最后执行m_model-run(img, mode);返回坐标信息

相关新闻

医疗影像“不敢上云“怎么破?从广西千万级平台看数据不出域的工程答案

医疗影像“不敢上云“怎么破?从广西千万级平台看数据不出域的工程答案

影像数据不允许离开院区,AI 却要喂数据训练——这对矛盾曾被当作"无解"。广西用 6 家试点医院、一套 U 型隐私推理架构给出了工程化答案。一、医疗数据上云,卡点从来不是技术"医疗数据到底能不能上云?"这个问基本每一家医…

2026/8/26 11:46:52 阅读更多 →
第14章:FastAPI 接口文档与前后端协作

第14章:FastAPI 接口文档与前后端协作

1. 项目背景 业务场景 "支付平台"的后端接口已经开发了 30 多个。前后端协作中出现了严重的沟通问题: 前端小刘问:“订单列表接口的 total 是 int 还是 string?上周还是 "10",这周变成 10 了——我的 type…

2026/8/26 11:47:33 阅读更多 →
机载电源模块真空甲酸炉关键技术与应用实践

机载电源模块真空甲酸炉关键技术与应用实践

半导体封装的秘密武器——真空共晶炉的有趣事实 大家好!今天要聊的,是那些在半导体封装领域中被工程师们称为“秘密武器”的设备——真空共晶炉。🛠️ 可能你会问,封装?封装有啥好聊的?🤔 要知道…

2026/8/26 12:52:25 阅读更多 →

最新新闻

Web渗透各种漏洞原理

Web渗透各种漏洞原理

SQL注入漏洞原理 SQL注入是最常见、危害最大的Web安全漏洞之一。其核心成因在于:应用程序在构建SQL查询语句时,直接将用户可控的输入拼接进SQL语句,而没有使用参数化查询或预编译机制进行防护。攻击者通过精心构造的输入,可以改变…

2026/8/27 13:12:32 阅读更多 →
三段式真空灌胶的本质

三段式真空灌胶的本质

三段式真空灌胶 近行业里出现了一些说法,把”三段式独立腔体(进料腔/点胶腔/出料腔)“包装成一种新概念,四处传播。作为在真空灌胶设备领域做了20年的厂家,我们觉得有必要把这件事说清楚——不是为了争一个名号,而是因…

2026/8/27 13:12:32 阅读更多 →
第262篇 语义SLAM——给地图加上“这是什么“的标签

第262篇 语义SLAM——给地图加上“这是什么“的标签

传统SLAM建的地图是"几何地图"——只有空间结构信息,没有语义信息。机器人知道"那里有一个长方体",但不知道"那是一张桌子"。对于导航来说几何地图可能够用(知道哪里有障碍物就行),但对…

2026/8/27 13:12:32 阅读更多 →
第261篇 多传感器融合SLAM——视觉+激光+IMU的组合方案

第261篇 多传感器融合SLAM——视觉+激光+IMU的组合方案

单一传感器做SLAM总有各自的短板。相机在暗处和纹理缺失区域不好使,激光雷达在长走廊和开阔场地精度差,IMU有漂移但短时间精度高。把这些传感器组合起来,让它们的优缺点互补,是SLAM走向实用的必然选择。多传感器融合SLAM已经是自动…

2026/8/27 13:12:32 阅读更多 →
实验二 PGP的使用【网络安全】

实验二 PGP的使用【网络安全】

实验二 PGP的使用【网络安全】 前言推荐PGP的安装及使用 * 一、PGP的安装及汉化二、创建用户及密钥三、PGPkeys的使用——加解密文字 * 0.打开程序1.导出自己的公钥2.使用公钥加密文件3.使用私钥解密文件 四、PGPkeys的使用——加解密文件 * 1.密钥的导出与导入2.文件的加…

2026/8/27 13:12:32 阅读更多 →
U-Blox GNSS与蜂窝模块选型到调通:IoT开发板低功耗定位通信实战

U-Blox GNSS与蜂窝模块选型到调通:IoT开发板低功耗定位通信实战

前阵子我们团队在规划一套物联网开发板评估套件时,遇到了一个很典型的选型问题:主控芯片和外围传感器都好说,但定位模块和蜂窝通信模块一直定不下来。最终敲定的方案,是在两块开发板上分别选用了U-Blox的GNSS模块和LTE-M/NB-IoT蜂…

2026/8/27 13:11:32 阅读更多 →

日新闻

Go语言构建企业级AI服务网关:统一管理英伟达等AI接口调用

Go语言构建企业级AI服务网关:统一管理英伟达等AI接口调用

1. 项目概述:从零构建一个企业级的AI服务网关 最近在帮一个做内容审核的团队做技术架构升级,他们原来的业务里,每天有几十万张图片和短视频需要过审,最初是接了几个开源的AI模型自己部署,但效果和性能一直不太稳定。后…

2026/8/27 0:00:51 阅读更多 →
网盘直链下载助手5分钟解析八大网盘真实地址

网盘直链下载助手5分钟解析八大网盘真实地址

网盘直链下载助手5分钟解析八大网盘真实地址 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸…

2026/8/27 1:06:27 阅读更多 →
从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南

从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南

从零点亮 ESP32:Arduino ESP32 开发环境搭建与首次烧录完整指南 【免费下载链接】arduino-esp32 Arduino core for the ESP32 family of SoCs 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 Arduino ESP32 是乐鑫官方的 ESP32 系列 Ardui…

2026/8/27 1:06:27 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/26 14:45:33 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/26 17:46:43 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 14:46:37 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/26 17:46:39 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →