TILER技术解析:二维平铺内存如何突破嵌入式图像处理的内存墙
1. 从线性存储到二维平铺为什么我们需要TILER如果你在嵌入式系统或者高性能计算领域折腾过图像处理尤其是视频编解码那你一定对“内存墙”这个词不陌生。处理器的算力在飞速增长但内存带宽和访问延迟的改善却相对缓慢很多时候算法跑得慢瓶颈不在CPU而在数据搬运上。我最早在TI的DaVinci系列处理器文档里接触到TILER这个概念时有种豁然开朗的感觉——原来内存访问还能这么“设计”。想象一个最简单的场景你的程序需要处理一张1920x1080的灰度图每个像素8位。在内存里它通常被“线性”或“光栅扫描”方式存储第一行的所有像素按顺序排布紧接着是第二行以此类推。这对于顺序读取整张图片是高效的。但现实中的图像算法比如H.264编码中的运动估计、JPEG解码中的8x8块处理其数据访问模式具有强烈的空间局部性。算法更关心一个局部区域例如一个16x16的宏块内的像素这些像素在二维空间上是相邻的但在线性内存中却是“散落”的。这就带来了一个核心矛盾内存控制器如SDRAM控制器喜欢以“块”Burst为单位进行高效传输比如一次读取128字节。当你需要访问一个16x16的宏块256字节时理想情况是2次128字节的突发传输就能搞定。但在线性存储下这个宏块的数据分布在内存的不同行Row里。第一次突发读取可能只拿到了宏块顶部几行的部分数据大量不属于当前宏块的数据也被一并读取然后被丢弃这就是带宽浪费。为了凑齐整个宏块内存控制器不得不发起多次访问频繁地打开和关闭SDRAM的行Page而SDRAM页打开Page Open是一个高延迟操作。最终结果就是处理一个宏块实际消耗的内存带宽和时钟周期远高于理论值。TILER平铺器技术的出现就是为了根治这个问题。它的设计哲学非常直接既然算法的访问模式是二维的那么我就把数据在物理内存中也按照二维的方式组织起来。它不再将图像视为一行行长线条而是将其切割、重组为一个个小的、正方形的“瓦片”Tile再将这些瓦片按顺序放入内存。这样一个算法关注的局部区域宏块其数据有很大概率被“打包”在少数几个连续的瓦片内从而能用最少的、最完整的内存突发传输来获取极大提升了访问效率减少了SDRAM页切换最终实现了更快的处理速度和更低的系统功耗。这套机制对于需要实时处理高清、超高清视频流的设备至关重要比如监控摄像头、视频会议系统、无人机图传和汽车ADAS系统。接下来我们就深入TILER的内部看看它是如何通过精巧的层级化设计来实现这一目标的。2. TILER架构核心一个虚拟的二维内存宇宙理解TILER首先要跳出“物理内存地址连续”的固有思维。TILER构建了一个独立的、高达4GB的虚拟地址空间专门用于存放以平铺格式组织的数据。这个空间就像一个巨大的、结构化的“画布”图像数据按照特定规则排列其上而硬件主要是DMA引擎如VPDMA知道如何在这个画布上高效地“作画”和“读画”。2.1 层级化结构从视图到子瓦片TILER的虚拟地址空间是一个层次分明、规则严整的体系。从上到下我们可以这样理解视图View - 4GB空间中的8个视角整个4GB TILER空间被划分为8个独立的512MB视图。这相当于给了你8种观察和扫描同一份图像数据的方式。为什么需要8种因为图像处理中数据读取方向可能不同。例如0度视图自然方向从左到右从上到下扫描。90度视图从上到下从左到右扫描相当于图像旋转了90度。180度视图从右到左从下到上扫描。以及带有水平/垂直镜像的组合视图。 通过选择不同的视图硬件在读取平铺数据时无需在内存中物理搬移或旋转数据就能直接以需要的方向获取像素实现了零开销的图像旋转和镜像操作。这是TILER一个非常强大的特性。容器Container - 视图内的数据仓库每个512MB的视图内部又包含了4个128MB的容器。每个容器对应一种元素大小Element Size8位容器用于存储8位/像素的数据例如灰度图LumaY分量。16位容器用于存储16位/像素的数据例如交织存储的色度分量CbCr常见于YUV422格式。32位容器用于存储32位/像素的数据例如ARGB带透明度的RGB图形缓冲区。页模式容器用于非平铺的、传统的线性数据访问。 容器的存在确保了不同位宽的数据都能在各自最优的二维布局中进行存取。页Page - 内存管理的基本单位每个128MB的容器在逻辑上被视作一个256列 x 128行的二维网格网格的每个单元格就是一个4KB的页。这是TILER与底层物理内存管理单元MMU交互的粒度。当系统需要为一段平铺数据分配物理内存时是以4KB页为单位进行映射的。一个4KB页内部又包含了4个1KB的瓦片Tile。瓦片Tile - 二维局部性的载体1KB的瓦片是TILER设计的精髓所在。这个尺寸并非随意设定而是精心匹配了目标SDRAM芯片的页大小Page Size。SDRAM在访问同一“行”Row内的数据时速度极快而切换到不同行则耗时较长。1KB的瓦片被设计成能够完整地放入一个SDRAM页中。这意味着只要算法访问的数据范围不超过一个瓦片那么这次访问在SDRAM层面就是最高效的不会引发行切换。子瓦片Sub-tile - 平衡访问的最终单元每个1KB的瓦片进一步细分为64个128位的子瓦片。子瓦片是数据在二维空间排列的最小逻辑单元。它的结构设计保证了无论以何种方向水平或垂直进行访问都能获得相对均衡的效率。具体来说8位模式一个子瓦片是4行 x 4列共16个的8位像素。16位模式一个子瓦片是2行 x 4列共8个的16位像素。32位模式一个子瓦片是2行 x 2列共4个的32位像素。核心设计思想串联算法需要高效访问一个二维区域如宏块- TILER将数据在物理内存中预先组织成二维瓦片- 一个瓦片的大小1KB正好匹配SDRAM页确保该区域数据尽可能集中在一个SDRAM行内- 子瓦片结构保证二维访问的均衡性- 多个瓦片被组织成页便于MMU管理- 不同位宽的数据放入不同的容器- 整个结构可以通过8个不同的视图来解读实现零开销旋转。2.2 地址转换连接虚拟与物理的桥梁数据以平铺格式存放在TILER的虚拟地址空间里但最终还是要读写到真实的物理SDRAM中。这个过程由DMM动态内存管理器中的PAT页地址转换模块完成。主要有两种方式直接地址转换PAT Direct Access这种方式绕过了查找表LUT要求为整个128MB的容器在物理内存中分配一块连续的128MB空间。这种方式简单直接但缺乏灵活性对物理内存的连续性要求高在实际复杂系统中较少使用。间接地址转换PAT In-Direct Access这是更常见和实用的模式系统通过一个页表LUT以4KB页为粒度将TILER虚拟空间的页映射到物理内存中任意位置的页上。这意味着一个容器的128MB虚拟空间其对应的物理内存可以是分散的、不连续的。关键约束与技巧通常系统只有一个LUT它最多能管理128MB的映射关系。但TILER有4个容器8位、16位、32位、页模式每个都是128MB怎么办答案是别名映射Aliasing。系统可以将这4个容器的虚拟页都映射到同一组物理内存页上。也就是说同一块物理内存当你用8位模式视图去访问时它被解释为8位像素的平铺阵列当你用32位模式视图去访问时它又被解释为32位像素的平铺阵列。这提供了极大的灵活性但要求软件驱动必须小心管理确保不同格式的数据不会错误地相互覆盖。3. TILER的三种核心访问模式详解TILER并非强制所有访问都必须用平铺模式。它提供了三种主要的访问模式以适应不同的数据流和访问模式。3.1 旁路模式Bypass Mode当发起访问的设备Initiator使用的系统地址不在TILER的虚拟地址范围内时TILER对此访问是“透明”的。数据会绕过PAT转换直接传递给内存控制器。但是在DMM层面为了提高SDRAM访问效率它仍然会对传输进行一些优化重组将二维块传输2D Block Burst按行分解为一系列一维递增传输。对于一维递增传输会在特定边界如交错内存区的交错粒度128/256/512字节或非交错区的1KB边界进行拆分以更好地利用内存控制器的缓冲和调度能力。3.2 页模式Paged Mode这种模式主要用于非平铺的线性数据。它利用了DMM PAT的地址转换机制即可以使用LUT进行非连续物理内存映射但数据本身不具备二维平铺结构。其传输优化策略与旁路模式类似。页模式的价值在于它让线性数据也能享受TILER地址空间带来的灵活内存映射好处同时保持传统的线性访问特性。3.3 平铺模式Tiled Mode这是TILER的精华所在专门为高效访问二维平铺数据而设计。在此模式下访问请求被分为两类格式良好的二维块请求Well-formed 2D Block Request这是效率最高的方式。请求必须明确指定方向Orientation、模式8/16/32位和步幅Stride。步幅是一个关键参数它定义了一行数据结束到下一行数据开始之间的字节距离。TILER硬件会根据这些参数自动计算并生成最高效的内存访问序列。一维递增请求或格式不良的二维请求如果不能提供格式良好的请求TILER也能处理但效率会有所下降可能退化为类似页模式的访问方式。步幅Stride的计算是理解平铺模式的关键。它不是一个随意设定的值而是由容器几何结构直接决定的。以最常用的0度视图S0为例8位模式容器逻辑宽度为16384个元素像素每个像素1字节。但步幅不是简单的16384字节。因为数据是按瓦片组织的我们需要计算在容器中Y坐标增加1即向下移动一行时地址需要跳过多少字节。这个值经过计算是16KB16384字节。对于隔行扫描Interlaced的视频场步幅加倍为32KB。16位模式容器逻辑宽度为16384个元素每个元素2字节。计算出的步幅为32KB32768字节隔行扫描时为64KB。32位模式容器逻辑宽度为8192个元素每个元素4字节。计算出的步幅同样为32KB32768字节隔行扫描时为64KB。对于90度视图S1容器的逻辑宽度和高度会发生交换因此步幅值也会相应变化例如8位模式变为8KB。硬件在发起访问时必须使用正确的步幅值才能正确地遍历平铺数据。4. 在软件中驾驭TILER配置、使用与避坑指南理解了原理最终要落地到驱动或应用代码。在基于Linux的嵌入式系统如TI的SDK中使用TILER通常不是直接操作硬件寄存器而是通过内核提供的DMM-TILER驱动和用户空间的库如libdmm来管理。4.1 主要配置步骤与API使用逻辑分配TILER缓冲区你需要指定缓冲区的维度宽、高、像素格式对应8/16/32位容器和方向对应8种视图之一。驱动会计算所需的总页数并通过LUT在物理内存中分配可能是分散的页面。实操心得分配时尽量让缓冲区的宽度和高度与瓦片/页的边界对齐。例如一个宽度恰好是多个瓦片宽度的缓冲区其访问效率最高可以避免跨瓦片的低效访问。获取物理地址与文件描述符分配成功后驱动会返回一个dma_buf的文件描述符fd。这个fd是核心它可以被传递给显示、编码、解码等硬件模块如VPE、VPSS、GPU。这些模块的DMA引擎配置为从TILER地址空间而非系统地址空间读取数据并指定正确的视图模式就能无缝地高效访问平铺数据。CPU访问平铺数据CPU通常不擅长直接处理平铺格式的数据。如果需要用CPU处理例如运行某个自定义的图像算法有两种策略策略一让硬件搬移。使用显示或拷贝引擎如DISPC或VPDMA将平铺缓冲区的内容“解平铺”到一个线性的系统内存缓冲区中CPU再处理线性数据。处理完后再“平铺”回去。这是最常用的方法利用了硬件加速。策略二CPU直接计算地址。理论上你可以根据TILER的地址公式手动计算每个像素在平铺内存中的位置。但这非常复杂且容易出错除非有极致的性能要求且处理区域很小否则不推荐。4.2 常见问题与排查技巧实录即使有成熟的驱动在实际集成TILER时依然会遇到不少坑。下面是我在项目中总结的一些典型问题及排查思路。问题一屏幕显示花屏、错位或颜色异常。排查思路这几乎是TILER配置错误的最直接表现。请按以下顺序检查缓冲区参数确认分配给显示层的缓冲区的宽度、高度、像素格式是否与显示控制器DISPC的配置严格匹配。一个像素是ARGB888832位还是YUV42216位这决定了该用32位容器还是16位容器。步幅Stride设置这是最易出错的地方。显示控制器在读取缓冲区时需要知道步幅。这个值必须使用TILER驱动提供的API来查询例如tiler_get_stride绝不能使用简单的width * bytes_per_pixel来计算。使用错误的步幅会导致显示控制器按错误的内存间隔去读取行数据造成图像撕裂或错位。视图View方向检查显示控制器配置的扫描方向。如果图像旋转了90度但视图却配置为0度显示自然就会出错。确保视图方向与预期的图像朝向一致。物理地址确保传递给显示控制器的是TILER缓冲区的物理地址而不是虚拟地址。驱动API通常会返回一个需要配置到硬件寄存器中的物理地址。问题二视频编码器如H.264编器输入图像异常编码质量差或失败。排查思路编码器通常直接从摄像头或经过处理的缓冲区获取原始帧Raw Frame。输入缓冲区格式确认编码器模块配置的输入像素格式如NV12, YUV422P与TILER缓冲区的实际格式是否一致。例如NV12格式的Y和UV分量可能需要分配两个独立的TILER缓冲区一个8位给Y一个16位给UV交织。数据生产者一致性检查是哪个模块在向这个TILER缓冲区写入数据例如是摄像头采集的ISP还是GPU渲染的结果。写入和读取必须使用相同的TILER模式和视图。如果写入时用的是16位容器、0度视图那么编码器读取时也必须以相同的配置去解读这块内存。缓存一致性如果CPU参与了对TILER缓冲区的修改虽然不推荐必须确保在DMA引擎编码器访问之前正确执行缓存维护操作dma_buf_sync将CPU缓存中的数据写回内存并无效化DMA的缓存。否则DMA读到的是旧数据或缓存中的脏数据。问题三系统内存碎片化导致TILER缓冲区分配失败。排查思路TILER通过LUT以4KB页为单位映射物理内存。虽然物理页可以不连续但系统长时间运行后可能无法找到足够的连续物理页来满足一次大缓冲区如1080p的多个帧缓冲区的分配请求。提前规划静态预留在内存紧张的系统中可以在内核启动参数中通过memmap或CMA连续内存分配器预留一块较大的连续物理内存区域专供TILER使用。动态管理及时释放在应用层建立良好的缓冲区生命周期管理。不用的帧缓冲区及时释放回驱动池。考虑使用缓冲池Buffer Pool技术在初始化时就分配好所需的一组缓冲区循环使用避免运行时频繁分配释放。检查内存状态使用cat /proc/buddyinfo和cat /proc/pagetypeinfo可以查看系统内存的碎片情况。如果高阶连续页块如order3的块很少就可能出现分配失败。问题四性能未达预期内存带宽利用率仍然很高。排查思路使用了TILER不代表性能就一定最优配置不当仍会导致效率损失。访问模式匹配确认发起访问的硬件模块DMA是否真正配置为格式良好的二维块传输2D Tiled Burst。查看相关IP如VPDMA的寄存器配置确保其传输描述符中的步幅、方向、区块尺寸等参数与TILER缓冲区属性完全匹配。如果配置成了一维传输则无法享受平铺带来的最大收益。缓冲区对齐检查分配的TILER缓冲区其起始地址和尺寸是否在瓦片1KB或页4KB边界上对齐。不对齐的访问可能导致单个请求横跨两个SDRAM页引发额外的页切换开销。SDRAM控制器配置确认SDRAM控制器的交错Interleaving设置是否与TILER的期望匹配。TILER设计时假设了内存交错的存在以平衡多个内存控制器的负载。如果交错被禁用或配置错误性能会下降。使用 profiling 工具如果平台支持使用性能分析工具如TI的SysProf监控内存控制器的活跃度、带宽利用率和页命中率。对比使用线性缓冲区和TILER缓冲区时的数据可以直观地看到优化效果。一个关键的避坑技巧文档与代码对照。TI的处理器参考手册TRM中关于TILER的章节通常非常详细但驱动层的API可能对其做了封装或简化。务必仔细阅读SDK中驱动源码的注释和示例代码。例如libdmm库中tiler_alloc函数对于width,height,fmt参数的具体约束可能比TRM中的数学公式更直接地指出了实际使用的限制。永远以实际可运行的示例代码作为配置基准再结合手册理解原理。

相关新闻

C 语言工业级通用组件手写 11:环形 FIFO 缓冲区

C 语言工业级通用组件手写 11:环形 FIFO 缓冲区

目录 前言: 一、环形 FIFO 核心本质与应用场景 1. 什么是环形缓冲区 2. 解决的核心痛点 3. 典型工业级落地场景 二、核心实现原理 1. 双指针循环机制 2. 零拷贝读写 3. 字节流式存储 三、工业级设计规范 1. 封装性设计 2. 接口设计原则 3. 鲁棒性要求 …

2026/10/10 4:24:57 阅读更多 →
变频器的技术应用:接线与基本参数设置

变频器的技术应用:接线与基本参数设置

部分参考自《变频器与步进/伺服驱动技术完全精通教程》 化学工业出版社 图源:部分引自《台达 VFD-M变频器 使用手册》,其余引自互联网。 学习笔记,记录一下新变频器到手后,需要进行的配置工作。 说起来,变频器属于传动…

2026/10/11 9:07:55 阅读更多 →
GameVault错误处理与调试:常见问题排查与系统稳定性保障

GameVault错误处理与调试:常见问题排查与系统稳定性保障

GameVault错误处理与调试:常见问题排查与系统稳定性保障 【免费下载链接】gamevault-app Frontend for the self-hosted gaming platform for drm-free games 项目地址: https://gitcode.com/gh_mirrors/ga/gamevault-app GameVault是一款开源的自托管游戏平…

2026/10/13 2:11:52 阅读更多 →

最新新闻

Node.js+Vue全栈开发出租车智能调度系统实践

Node.js+Vue全栈开发出租车智能调度系统实践

1. 项目背景与核心需求出租车业务管理系统是传统运输行业数字化转型的典型场景。随着网约车平台的兴起,传统出租车公司面临着运营效率低下、调度方式落后、数据统计困难等痛点。这个基于Node.jsVue的全栈项目,正是为了解决以下核心问题:实时调…

2026/10/14 10:08:38 阅读更多 →
瑞利-里茨法:弹性力学能量原理与工程实践

瑞利-里茨法:弹性力学能量原理与工程实践

1. 瑞利-里茨法基础理论1.1 能量原理的物理背景在弹性力学系统中,能量原理为我们提供了一种强有力的分析工具。最小势能原理指出:在所有满足位移边界条件的可能位移场中,真实的位移场使得系统的总势能取得极小值。这个原理不仅适用于静力学问…

2026/10/14 10:08:38 阅读更多 →
Kafka Log存储原理:揭秘顺序写、稀疏索引与Segment分片设计

Kafka Log存储原理:揭秘顺序写、稀疏索引与Segment分片设计

1. 这不是“日志存储”,而是 Kafka 的数据心脏——从磁盘文件结构看吞吐与一致性的底层博弈你打开 Kafka 的 data 目录,看到一堆形如topic-partition-0/00000000000000000000.log和00000000000000000000.index的文件,第一反应可能是&#xff…

2026/10/14 10:08:38 阅读更多 →
Qt+MinGW 编译 PCL 全依赖链实战指南

Qt+MinGW 编译 PCL 全依赖链实战指南

简介:本资源面向需要在Windows平台使用Qt与MinGW构建点云处理环境的C开发者,提供PCL及其全部依赖库的完整编译成果。PCL是开源三维点云处理库,涵盖数据采集、预处理、特征提取与表面重建;配套的Boost、Eigen、FLANN、Qhull与VTK分…

2026/10/14 10:08:38 阅读更多 →
基于机器学习的Web日志异常检测与根因分析

基于机器学习的Web日志异常检测与根因分析

简介:这是一套面向Web安全工程师、运维开发人员及机器学习初学者的终端日志分析工具,聚焦于Web服务器日志的自动化统计、审计与恶意请求识别。项目基于Python实现,集成日志解析、访问量/请求类型多维统计、SQLite(或MySQL&#xf…

2026/10/14 10:08:38 阅读更多 →
macOS C++20开发环境配置:LLVM+VS Code+Clang完整闭环

macOS C++20开发环境配置:LLVM+VS Code+Clang完整闭环

1. 为什么 macOS 上配 C 环境总让人“卡在第一步”?Mac 用着顺手,写代码也清爽,但一到配 C 环境,很多人就停在了终端里那行command not found: g上——不是没装 Xcode,就是装了却找不到编译器路径;不是clan…

2026/10/14 10:07:26 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →