STM32上跑二维码识别:OV5642+DCMI+DMA全链路实战
简介这份资源面向嵌入式开发初学者与参加电子设计竞赛的学生提供基于STM32单片机与OV5642摄像头模块的二维码识别完整工程。包内共316个文件以C源码与头文件为核心包含67个.c与80个.h文件另有编译中间产物如38个.o、38个.d及37个.crf文件以及Keil工程配置、链接脚本、库文件与少量说明文档压缩包约12.05MB可直接导入开发环境编译调试。资源围绕二维码图像采集、二值化处理与解码识别展开涉及字符集、校验功能及图形旋转变化等条码技术要点帮助读者理解从摄像头取像到信息自动处理的完整链路。目前已有2445人学习下载适合希望快速搭建二维码识别原型、研究图像识别算法在单片机端落地的开发者参考借鉴。1. 需求先立住为什么要在 STM32 上跑二维码识别去年带一个嵌入式方向的课设小组题目定的是“基于 STM32 的二维码识别系统”。组里第一反应是二维码识别不是手机扫一下的事吗为什么还要用单片机做这正是这个项目最值得写的地方——它要解决的不是“能不能扫”而是在没有操作系统、没有 OpenCV、主频只有几十到一百多兆、RAM 以 KB 计的 MCU 上把图像采集、预处理、定位、解码整条链路跑通。适合做这个题的人很明确嵌入式、电子信息、自动化方向的学生或者想给门禁、仓储、产线做低成本离线扫码模块的从业者。OV5642 作为并口摄像头配合 STM32 的 DCMI 接口是这条链路上最典型的硬件组合也是这套方案要讲清楚的核心。2. 总体方案设计从 OV5642 到二维码字符串的四段链路2.1 硬件选型与接口分配这套方案的核心器件就三块STM32 主控、OV5642 摄像头、一块能显示中间结果的屏TFT 或 OLED 都行。OV5642 是 500 万像素的并口摄像头输出格式支持 RGB565、YUV、JPEG通过 8 位或 10 位并口把数据推给 STM32 的 DCMI数字摄像头接口。DCMI 的好处是硬件自动搬运配合 DMA 双缓冲CPU 不用一个像素一个像素去读这是能在 MCU 上跑图像的前提。选型上有几个硬约束必须提前算清楚。第一是分辨率OV5642 全尺寸太大二维码识别用 QVGA320×240甚至 QQVGA160×120就够像素越少后续二值化和定位的运算量越小。第二是内存一帧 QVGA 的 RGB565 是 320×240×2 153600 字节STM32F4 系列内部 SRAM 通常 192KB勉强放一帧但双缓冲就不够了所以一般外挂一颗 SDRAM 或者用 FSMC 扩展。第三是主频二值化、连通域标记、定位这些操作是纯 CPU 运算主频低于 100MHz 会明显吃力F4/F7/H7 是合理区间。我一般会先画一张数据流图再动手OV5642 → DCMI → DMA → 帧缓冲SDRAM→ 灰度化 → 二值化 → 定位 → 解码 → 串口/屏幕输出。每一段都有明确的输入输出和内存占用先把这张图算明白后面写代码才不会返工。2.2 图像采集DCMI DMA 双缓冲配置采集是整个链路的地基配不好后面全是玄学问题。OV5642 通过 SCCB类似 I2C配置寄存器设置输出格式、分辨率、帧率然后 DCMI 开始接收。关键是用 DMA 双缓冲让“采一帧”和“处理上一帧”能并行否则每帧都要等采集完才能处理帧率直接砍半。// DCMI DMA 双缓冲初始化以 STM32F4 HAL 为例 void Camera_DMA_Init(void) { // DCMI 配置8 位并口硬件同步RGB565 hdcmi.Init.SynchroMode DCMI_SYNCHRO_HARDWARE; // 行场同步由硬件信号给出 hdcmi.Init.PCKPolarity DCMI_PCKPOLARITY_RISING; // 像素时钟上升沿采样 hdcmi.Init.VSPolarity DCMI_VSPOLARITY_LOW; // 场同步低有效 hdcmi.Init.HSPolarity DCMI_HSPOLARITY_LOW; // 行同步低有效 hdcmi.Init.CaptureRate DCMI_CR_ALL_FRAME; // 全帧捕获 hdcmi.Init.ExtendedDataMode DCMI_EXTEND_DATA_8B; HAL_DCMI_Init(hdcmi); // DMA2 流1双缓冲模式每帧 320*240 个半字 HAL_DMA_Start_IT(hdma_dcmi, (uint32_t)DCMI-DR, (uint32_t)frame_buffer0, FRAME_SIZE); // 第二块缓冲由 HAL_DCMI_Start_DMA 的第二个地址参数指定 HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)frame_buffer0, FRAME_SIZE); }这段代码里几个参数值得说清楚。SynchroMode选硬件同步是因为 OV5642 会输出 VSYNC/HSYNC/PCLK硬件同步比软件轮询稳得多。PCKPolarity要和摄像头实际输出的时钟极性对上对不上会出现整帧错位、图像撕裂这是最常见的翻车点。CaptureRate用全帧捕获配合 DMA 的FRAME_SIZE正好是一帧的像素数。双缓冲的意义在于DMA 填 buffer0 的时候 CPU 处理 buffer1填满后中断里切换处理延迟被隐藏掉实测帧率能比单缓冲高 40% 以上。2.3 预处理与二值化把彩色图变成黑白点阵二维码识别不需要颜色只需要黑白。所以第一步是灰度化RGB565 转灰度用加权公式Gray (R*77 G*150 B*29) 8这个系数是常用近似比等权平均更符合人眼感知对后续阈值分割更友好。灰度化可以在采集后立刻做也可以边采边算我一般放在 DMA 半满/全满中断里趁数据还在缓存里顺手转掉。第二步是二值化。二维码是黑白模块理论上用固定阈值就能分但实际光照不均固定阈值会翻车。常用的是局部自适应阈值比如把图像分成若干块每块单独算阈值。但 MCU 上做全图自适应太慢工程里更常见的是“全局阈值 局部修正”先统计整帧灰度直方图取双峰之间的谷值作为全局阈值再对过暗或过亮的区域做补偿。// 灰度化 全局阈值二值化逐像素适合 QVGA void Binarize_Frame(uint16_t *src, uint8_t *dst, int pixels) { // 第一遍统计直方图找全局阈值 uint32_t hist[256] {0}; for (int i 0; i pixels; i) { uint16_t p src[i]; uint8_t r (p 11) 0x1F; uint8_t g (p 5) 0x3F; uint8_t b p 0x1F; // 扩展到 8 位再算灰度 uint8_t gray (r * 77 g * 150 b * 29) 8; hist[gray]; } // 取直方图谷值作为阈值简化版 Otsu 思路 uint8_t threshold Find_Valley(hist, 256); // 第二遍按阈值二值化黑0白1 for (int i 0; i pixels; i) { uint16_t p src[i]; uint8_t r (p 11) 0x1F; uint8_t g (p 5) 0x3F; uint8_t b p 0x1F; uint8_t gray (r * 77 g * 150 b * 29) 8; dst[i] (gray threshold) ? 0 : 1; } }这里Find_Valley是简化版阈值选取真实工程里如果光照稳定直接给一个经验阈值比如 128也能跑但换个环境就得重调。二值化后的dst是每像素 1 bit 还是 1 byte取决于后续算法如果做连通域标记用 1 byte 方便索引如果只做行列扫描定位可以压成位图省内存。QVGA 下 1 byte/像素是 76800 字节F4 的内部 RAM 放得下但加上帧缓冲就紧张所以二值化结果通常直接覆盖原缓冲或放到独立的小缓冲里。2.4 定位与解码从黑白点阵到字符串二值化之后是定位。二维码有三个角上的“回”字形定位图案这是它能在任意角度被识别的关键。MCU 上做定位常用的是扫描线法逐行扫描找连续的黑白黑白的宽度比例符合 1:1:3:1:1 的段落就认为是定位图案的一行。三个定位点找到后用它们确定二维码的旋转角度和边界再做透视校正把倾斜的码摆正。解码部分是最考验工程取舍的。完整的 QR 解码包括格式信息读取、版本确定、掩码去除、纠错译码RS 纠错全自己写工作量很大。实际项目里通常有两种做法一是移植轻量级解码库把核心解码函数抠出来适配 MCU二是针对固定版本、固定内容的场景写简化解码。我一般建议课设阶段先支持 Version 1~4、纠错等级 L 的码把主流程跑通再逐步扩展。// 定位图案扫描简化示意逐行找 1:1:3:1:1 比例 int Find_Finder_Pattern(uint8_t *bin, int w, int h, Point *out) { for (int y 0; y h; y) { int run[5] {0}; // 连续黑白黑白黑的长度 int state 0; for (int x 0; x w; x) { uint8_t px bin[y * w x]; if (px 0) { // 黑 if (state % 2 0) run[state]; else { state; run[state]; } } else { // 白 if (state % 2 1) run[state]; else { state; run[state]; } } if (state 5) { // 检查比例是否接近 1:1:3:1:1 if (Check_Ratio(run)) { out-x x - (run[0]run[1]run[2]run[3]run[4]); out-y y; return 1; } // 滑动窗口丢弃第一个 run继续 for (int i 0; i 4; i) run[i] run[i1]; run[4] 0; state 4; } } } return 0; }Check_Ratio里做的是比例容差判断因为实际图像有噪声不可能严格 1:1:3:1:1一般允许 ±50% 的偏差。找到三个定位点后用它们算仿射变换矩阵把码区域映射到标准正方形再按模块网格采样得到 0/1 矩阵最后送解码。这一步的坑在于如果定位点找错比如把码里的数据模块误判成定位图案后面全错所以通常要求三个点满足几何关系构成等腰直角三角形才确认。3. 关键模块实现内存、帧率与解码的工程取舍3.1 内存布局把有限的 RAM 花在刀刃上MCU 上做图像内存永远是第一约束。以 QVGA RGB565 为例一帧 153600 字节双缓冲就是 307200 字节F4 的 192KB 内部 RAM 根本不够。所以这套方案通常要外挂 SDRAM通过 FMC/FSMC 映射到地址空间。但 SDRAM 访问速度比内部 SRAM 慢DMA 直接写 SDRAM 可能带宽不够常见做法是 DMA 先写内部 SRAM 的小缓冲再搬运到 SDRAM或者降低分辨率到 QQVGA。我的经验是如果只是做二维码识别QQVGA160×120完全够用一帧 38400 字节双缓冲 76800 字节F4 内部 RAM 能放下省掉 SDRAM 这颗料成本和布线都简单。代价是识别距离近码要占满画面较大比例。课设阶段强烈建议从 QQVGA 起步把链路跑通再考虑提分辨率。3.2 帧率优化哪些操作可以省哪些不能省帧率决定了扫码体验。整条链路里采集是硬件搬运基本不占 CPU灰度化 二值化是逐像素QVGA 下约 76800 次循环F4 上大概几毫秒定位扫描是逐行最坏情况全图扫一遍也是几毫秒解码最耗时RS 纠错在 MCU 上可能几十毫秒。所以瓶颈通常在解码而不是图像处理。优化手段有几个一是降低分辨率像素少一半处理时间少一半二是只处理感兴趣区域ROI如果码总在画面中央就只扫中间那块三是二值化和定位用查表代替实时计算四是解码库选轻量的别把桌面级库直接搬过来。我一般会先测每一段的耗时用 GPIO 翻转 示波器看或者用 DWT 周期计数器找到真正的瓶颈再优化而不是凭感觉。3.3 解码适配轻量库移植与固定版本简化解码是这套方案里最“重”的部分。完整 QR 解码涉及 RS 纠错、掩码、格式信息代码量不小。移植时要注意几点一是库的内存分配要改成静态或预分配MCU 上尽量不用 malloc二是浮点运算尽量换成定点F4 有 FPU 还好F1 没有 FPU 会非常慢三是把与平台相关的打印、文件操作去掉只留核心解码。如果场景固定比如只识别自己生成的码可以走简化路线固定版本、固定纠错等级、固定掩码省掉格式信息解析和部分纠错逻辑代码量能砍一半以上。代价是通用性差换个码可能就不认了。课设里我一般建议先做通用解码跑通后再根据需求裁剪。4. 避坑与常见问题那些让项目卡住的细节4.1 图像撕裂或错位现象屏幕显示的图像上下错位、颜色错乱或者 DMA 传输一半就停了。原因通常是 DCMI 的同步极性配错或者 DMA 缓冲大小和实际帧大小不一致。解决先用固定测试图案比如全红验证 DCMI 配置确认 VSYNC/HSYNC/PCK 极性再检查FRAME_SIZE是否等于宽×高双缓冲的两个地址是否都有效。我踩过一次坑PCKPolarity设反了图像整体偏移半个像素二值化后定位图案全乱查了两天才发现是极性。4.2 二值化后码区域全黑或全白现象光照变化时二维码区域二值化后变成一整块黑或一整块白定位图案消失。原因是全局阈值不适应光照。解决改用局部自适应阈值或者加自动曝光控制OV5642 支持 AEC/AGC通过 SCCB 写寄存器开启。如果不想动摄像头就在二值化前做一次简单的背景估计比如取图像四角的平均灰度作为参考动态调整阈值。4.3 定位图案误检现象程序把码里的数据模块误判成定位图案导致解码失败或解出乱码。原因是比例判断太宽松或者扫描线穿过了非定位区域。解决加几何约束要求找到的三个点构成近似等腰直角三角形且边长比例合理再加一次交叉验证用找到的点反推码边界检查边界内黑白模块比例是否符合二维码特征。4.4 解码耗时过长导致帧率骤降现象采集帧率明明有 30fps但识别结果几秒才出一次。原因是解码在中断里同步执行阻塞了采集。解决把解码放到主循环采集用 DMA 中断只负责切换缓冲和置标志位或者用 RTOS 分任务采集、处理、解码各一个任务用消息队列传递帧指针。注意帧缓冲要加引用计数或双缓冲轮换避免解码时缓冲被覆盖。4.5 内存溢出或 HardFault现象程序跑一段时间后死机或者一进图像处理就 HardFault。原因通常是数组越界、栈溢出或 DMA 访问了非法地址。解决把大数组定义成全局或静态别放栈上检查 DMA 目标地址是否在有效 RAM 区域用__attribute__((aligned(4)))保证缓冲对齐有些 DMA 要求 4 字节对齐。我一般会在链接脚本里把大缓冲单独放一个段方便看内存占用。5. 验收与答辩怎么证明这套方案真的跑通了验收这件事最忌讳只说“能识别”。答辩老师或评审要的是可复现的证据链。我一般会准备三样东西一份测试记录表、一段现场演示、一份关键代码走读。测试记录表按场景分正常光照、弱光、强光、倾斜 15°、倾斜 30°、部分遮挡。每个场景跑 20 次记录成功次数和平均耗时。表格长这样测试场景测试次数成功次数成功率平均耗时(ms)正常光照2020100%85弱光201680%92强光201890%88倾斜15°201995%90倾斜30°201575%95部分遮挡201260%98这张表能直接说明方案的边界正常场景稳极端场景有下降但整体可用。答辩时老师问“弱光为什么掉”你就能答“因为全局阈值在低对比度下分割不准后续可以加 AEC 或局部阈值”显得你清楚瓶颈在哪。现场演示要准备“翻车预案”。比如提前把码打印在不同材质上纸、屏幕、金属演示时如果某个场景失败立刻换一个已知能过的场景别在一个点上死磕。我一般会准备一个“保底码”——尺寸大、光照好、正对摄像头确保演示至少有一次成功。代码走读环节重点讲三处DCMI DMA 双缓冲的配置逻辑、二值化阈值的选择依据、定位图案的比例判断。这三处是方案的技术核心讲清楚就能证明你不是调库调出来的。如果老师追问“为什么不用 OpenCV”你就答“MCU 上没有操作系统和足够内存OpenCV 跑不起来这套是面向裸机或 RTOS 的轻量实现”把约束讲明白。从那以后我每次带图像类项目都强制先做一张“耗时分布表”把采集、预处理、定位、解码各占多少毫秒标出来再动手优化。没有这张表优化就是瞎猜。希望这套结构能帮你把课设或方案顺利交付。本文还有配套的精品资源点击获取

相关新闻

大模型API实现Agent技能调用:从设计到实战全解析

大模型API实现Agent技能调用:从设计到实战全解析

最近不少开发者在动手折腾Agent这一块,我也跟着把一个基于大模型API的技能调用任务完整跑了遍。这个任务听上去不复杂——让Agent按需调用外部工具——但真正落地时会发现,模型如何理解技能、如何准确选工具、如何在多轮对话里不"发疯"&#x…

2026/10/17 15:33:03 阅读更多 →
Praat中文手册:语音信号处理脚本编写与批量声学分析实战

Praat中文手册:语音信号处理脚本编写与批量声学分析实战

简介:这份 praat 中文学习手册面向语音学研究者、语言学与心理学专业学生及对语音科学感兴趣的初学者,帮助读者系统掌握 praat 在语音信号分析、处理与展示方面的操作。手册围绕软件基本界面、语音文件导入导出、波形与频谱分析、声调韵律研究、元音辅音…

2026/10/17 15:33:03 阅读更多 →
百度地图微信小程序JSAPI实战:从导航失败到坐标转换与避坑指南

百度地图微信小程序JSAPI实战:从导航失败到坐标转换与避坑指南

简介:这份资源是面向微信小程序开发者的百度地图 JavaScript API 接口包,用于在微信小程序环境中快速接入地图展示、路径规划、地点检索与定位服务等能力,适合具备一定小程序开发基础、需要为应用补充地理位置功能的开发者使用。压缩包共35个…

2026/10/17 15:33:03 阅读更多 →

最新新闻

跨平台SSL证书导入脚本:Windows与Linux批量部署指南

跨平台SSL证书导入脚本:Windows与Linux批量部署指南

简介:这份资源提供在Windows与Linux系统中导入SSL证书的自动化脚本,面向需要为Web服务配置HTTPS通信的运维人员与系统管理员。压缩包内共2个文件,分别为Linux平台使用的sh脚本与Windows平台使用的bat脚本,包体仅578B,轻…

2026/10/17 16:31:36 阅读更多 →
viewer.min.js图片查看器集成指南:从初始化到避坑实践

viewer.min.js图片查看器集成指南:从初始化到避坑实践

简介:viewer.min.js 是一款轻量级 JavaScript 图像查看器库,面向需要在前端实现图片缩放、旋转、平移等交互效果的前端开发者。资源包以 viewerjs-main 命名,共包含 177 个文件,整体打包 3.14MB,其中以 121 个 js 文件…

2026/10/17 16:31:36 阅读更多 →
信呼OA系统开源部署指南:中小团队低成本的协同办公方案

信呼OA系统开源部署指南:中小团队低成本的协同办公方案

简介:信呼协同办公OA系统v1.8.6提供了一套开源的企业办公自动化解决方案,适合中小型企业和研发团队使用,能够将流程审批、任务协作、日程管理、移动办公等常见需求整合到一个平台中。这个版本在之前基础上优化了系统稳定性和操作体验&#xf…

2026/10/17 16:31:36 阅读更多 →
Flutter蓝牙开发实战:协议设计、插件选型与跨平台适配

Flutter蓝牙开发实战:协议设计、插件选型与跨平台适配

1. 蓝牙协议在 Flutter 项目中的整体设计思路1.1 为什么要在移动端项目里引入蓝牙能力做移动端项目时间长了,总会碰到一类需求:设备不在云端,也不走 Wi-Fi,而是靠蓝牙近距离通信。比如智能手环同步步数、体脂秤回传数据、便携打印…

2026/10/17 16:31:36 阅读更多 →
油猴脚本从入门到实战:安装配置与高效使用指南

油猴脚本从入门到实战:安装配置与高效使用指南

1. 先搞懂油猴插件到底是什么说到油猴插件,很多人的第一反应是:听说过,但一直没搞懂。以为它是什么灰色地带的黑科技,或者觉得那是程序员才折腾的东西。今天把话说透,油猴插件/脚本就是一个运行在浏览器里的轻量小助手…

2026/10/17 16:31:36 阅读更多 →
TortoiseGit绿色对勾消失?从图标覆盖到注册表抢位的完整排查指南

TortoiseGit绿色对勾消失?从图标覆盖到注册表抢位的完整排查指南

那天我像往常一样打开资源管理器,突然发现整个工作目录的文件夹都变成了最普通的黄色小文件夹——TortoiseGit 的绿色对勾不见了。第一反应是仓库坏了,赶紧打开终端跑了个 git status ,结果文件状态一切正常,也没有任何报错。那…

2026/10/17 16:30:36 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →