Mean Shift目标跟踪:从核密度估计到工程实现
Mean shift目标跟踪算法在计算机视觉圈子里算是常青树了。它不是什么新潮方法——九十年代就被引入视觉跟踪领域——但今天你依然能在无人机视觉、智能监控、机器人导航这些实时性要求高的场景里看到它。原因很朴素不用训练不用GPU一块普通CPU就能跑满实时帧率核心代码几百行搞定对于“目标颜色特征明显”的场景效果相当能打。如果你刚开始接触目标跟踪或者正为算力受限的嵌入式平台找可落地的方案把mean shift彻底吃透比上来就调深度学习跟踪器要划算得多。这篇我会从数学直觉一路讲到工程实现把论文里一笔带过、实际开发又绕不开的细节全部拆开讲清楚。1. 从核密度估计到目标跟踪Mean Shift的数学直觉1.1 核密度估计与爬山假设你在广场上空撒了一把豆子豆子落在地上有的地方密、有的地方稀。你站在某个位置想找周围最近的一个“豆子堆得最高”的地方。你会怎么走大概率是朝周围豆子密度增加最快的方向迈一步然后继续环顾四周直到发现前后左右都不如脚下密集这才停下来。mean shift干的就是这件事。它把所有样本点当作未知概率密度函数的抽样值用核函数在每个样本点附近铺开一个平滑鼓包再把所有鼓包叠加起来得到一张连续光滑的密度估计曲面。从任意一个起始位置出发沿着密度梯度上升的方向迭代移动最终会收敛到某个局部极大值点——这个点也叫模点。这个过程中不需要知道密度函数的具体形式也不需要假设数据服从什么分布所有信息都来自样本点本身。这种非参数特性是mean shift能跨领域应用的基石。从统计角度看每次迭代相当于在局部做了一次带权重的平均距离越近的样本点贡献越大因此它对噪声和离群点也比较稳健。这也是它后来不仅能用于聚类还被引入边缘检测、特征空间分析乃至视觉跟踪的原因。1.2 从聚类跨界到跟踪的“同一个动作”看到这里你可能已经隐约感觉到跟踪目标时我们做的事情和聚类几乎一模一样。聚类场景中数据点是静态的算法要找的是样本分布的密集中心。跟踪场景中第一帧选定的目标区域包含一批像素这些像素的颜色特征构成一个分布。假设目标在帧间不会突然瞬移很远那么下一帧里目标大概率还出现在上一帧位置附近的小范围内。于是我们从上一帧的目标中心出发在当前帧里沿着“像素特征分布与目标模型相似度增加最快”的方向迭代移动最终到达的位置就是目标在当前帧的新位置。本质上mean shift跟踪就是把“相似度函数”当成一座山把跟踪问题转化为从上一帧位置出发、爬山找峰顶的问题。这个视角转换非常巧妙也是初学者理解这个算法最应该抓住的点。后面所有公式追根溯源都是为了服务这座“山”的寻找过程。2. 目标建模与相似度度量核心细节拆解2.1 目标模型为什么颜色直方图就够了目标建模就是把“目标长什么样”这个模糊概念变成一个可计算的数学表示。绝大多数mean shift跟踪实现都基于颜色直方图原因很实在颜色特征对旋转、微小形变、部分遮挡都有较好的容忍度。比如一个人从正面转身到侧面衣服颜色的统计分布基本不变但像素级纹理结构早就面目全非了。如果换成边缘或纹理特征转身这个动作就会带来灾难性的变化。具体做法分三步。第一步选择颜色空间。实践中最常用的是HSV空间。HSV把颜色分解成色调、饱和度和明度其中色调和饱和度对光照变化相对不敏感。RGB空间里环境光的亮度变化会同时影响三个通道导致直方图整体偏移跟踪很容易被光照干扰。第二步将目标区域内的每个像素映射到颜色空间的某个bin统计每个bin中的像素个数。比如色调通道取16个bin、饱和度通道取8个bin就得到一个128维的直方图向量。第三步归一化。把每个bin的计数除以总像素数得到概率分布向量。这个分布就是目标的“数字身份证”记为q。如果只是用像素数直接统计那么边缘像素和中心像素对直方图的贡献完全一样。可目标边缘像素很容易混入背景目标稍有移动边缘区域就会吸收背景干扰像素直方图被污染。所以要用核函数对像素做空间加权离中心越远的像素权重越低中心附近的像素贡献更大。常用的核函数是抛物线形核函数形式为当距离平方比小于1时权重为1减去距离平方比否则权重为0。用这个核加权后目标直方图的含义就变成了统计每个bin的像素数时中心像素算得多一点、边缘像素算得少一点。2.2 候选模型与巴氏系数有了目标模型q接下来每一帧都要回答一个问题当前帧中哪个位置最像目标做法是在当前帧以某个候选位置为中心框出一个与目标区域同样大小的窗口计算它的核加权直方图记为p(y)。然后比较p(y)与q的相似程度。相似度的度量几乎所有mean shift跟踪文献都用巴氏系数ρ(y) Σ √(q_u · p_u(y))这个系数衡量的是两个概率分布的重叠程度。两个分布完全一样时ρ等于1完全无重叠时ρ等于0。相比欧氏距离或直方图相交距离巴氏系数对“直方图形状相近但数值有偏差”的情况更敏感而且它的值域天然落在0到1之间可以直接作为目标是否可信的参考阈值。实际工程中我会把ρ值作为状态量输出。当ρ长时间低于某个阈值比如0.7说明当前窗口内容和目标模型已经对不上了如果这时还要强行更新模型大概率会把背景当作目标学进去。这个阈值设定在后面避坑部分还要重点展开。2.3 迭代更新权重计算与质心漂移这一节是数学重头戏但我尽量用最直白的方式讲清楚。我们的目标是找让ρ(y)最大化的新位置y。直接对ρ(y)求最大值并不容易因为直方图是离散的候选位置变化时直方图会发生非线性跳变。经典做法是在当前候选位置y0处对ρ(y)做一阶泰勒展开把复杂的非线性优化问题转化成一系列加权统计量的迭代计算。这个转化是算法能保持实时性的关键也意味着每一步位移都是基于局部梯度估计出来的。经过推导每个像素xi会获得一个权重w_i Σ √(q_u / p_u(y0)) · δ[b(xi) u]直觉上w_i衡量的是“这个像素在当前帧中属于目标模型的程度”。如果它所在的bin在目标模型概率很高、当前候选直方图概率较低它的权重就低反之它就能在下一步把搜索结果往自己身边拉。然后计算新的候选中心y1 [Σ w_i · K(||xi - y0||²) · xi] / [Σ w_i · K(||xi - y0||²)]这其实就是一个带权重的质心公式。分子分母是对同一个权重集合求和所以y1会落在“更像目标”的像素群的中心位置。把y0替换成y1重复计算权重和质心就完成了mean shift迭代。当位移小于阈值或达到最大迭代次数就认为收敛。有一个细节必须提前说明因为泰勒展开做了局部近似每一步迭代位置都是估计值。如果一次跳太远可能越过相似度最高点收敛到次优解。所以标准流程里要加一道校验计算ρ(y1)如果ρ(y1)小于ρ(y0)就在y0和y1之间取中点重新计算相似度并逐步缩进直到选出一个相似度不低于当前点的位置。这个“半步校验”在目标快速运动时非常关键少了它窗口很容易震荡甚至飞走。3. 一步一步跑通完整算法流程与参数调优3.1 标准流程拆解与核心代码骨架我平时在项目里实现mean shift跟踪流程固定下来就是下面几步你可以直接照搬初始化读取第一帧框选目标计算目标直方图q记录初始中心y0。进入下一帧以y0为中心计算候选直方图p(y0)。逐像素计算权重w_i。代入加权质心公式得到新位置y1。执行半步校验修正y1。若位移小于阈值则收敛输出目标位置否则令y0 y1回到第2步。输出位置后根据相似度决定是否更新目标模型继续下一帧。核心迭代的Python骨架大概长这样for _ in range(max_iter): hist_candidate compute_histogram(frame, y, h) # 候选直方图 rho_old bhattacharyya(hist_model, hist_candidate) weights compute_weights(hist_model, hist_candidate, frame, y, h) y_new weighted_centroid(weights, frame, y, h) # 半步校验防止越界跳过最优峰 while bhattacharyya(hist_model, compute_histogram(frame, y_new, h)) rho_old: y_new 0.5 * (y y_new) # 在中点处收缩 if distance(y_new, y) eps: break y y_new整个过程没有复杂矩阵运算没有GPU没有预训练模型。我在一台低功耗嵌入式单板电脑上实测过处理720P视频单帧跟踪耗时大约在1毫秒量级跑25帧每秒毫无压力。3.2 直方图与颜色空间参数怎么定参数选择直接决定跟踪效果。下面这张表是我多年调试后固定下来的推荐区间可以省去大量试错时间参数项推荐区间备注颜色空间HSV光照变化大时优先使用HS弃用V通道色调通道bin数16~30低于8区分度不足高于48对噪声太敏感饱和度通道bin数8~16辅助区分相似色调特征维度2维HS组合最常见加V需降低光照权重带宽h目标宽高的1/22/3目标较大时取偏小值防背景干扰迭代阈值0.5像素更小收益有限白白增加迭代次数最大迭代次数1520次常规情况58次就能收敛这张表针对的是中分辨率目标区域比如几十乘几十到一两百像素。如果你跟踪的目标非常小bin数要适当减少否则大部分bin都是空的直方图稀疏得像筛子目标很大时bin数可以适当增加但要保证目标区域内有足够像素支撑统计稳定性。直方图bin数要和目标像素量匹配这是很多新手容易忽略的点。3.3 核函数、带宽与模型更新策略核函数的选择工程上首选抛物线形核函数。它理论上是渐进最优的计算也很简单一次乘法和一次比较就完成。高斯核也可以使用但我实际对比下来跟踪准确率几乎没有差别反而多出指数运算开销没必要为了“听起来更高级”去换它。带宽h是mean shift跟踪里最重要的超参数。它决定了搜索窗口的物理大小。h太大窗口会包含大量背景像素直方图被背景污染目标易漂移h太小目标稍微运动就脱离窗口迭代根本找不到目标。调参时先用视频里目标的典型尺寸估算再跑几段真实数据验证通常就能锁定一个稳定值。模型更新是老生常谈但决定成败。最容易犯的错误是每一帧都更新目标模型。我初学阶段就这么干过结果目标一旦被遮挡两秒模型就被背景淹没之后再也追不回来。正确做法是设置更新门槛只有当前帧相似度高于阈值时才更新低置信度时保持模型不变。还可以引入学习率让新直方图以一定比例融合进旧模型而不是完全替换。4. 真实场景避坑指南常见问题与排查技巧4.1 目标快速移动窗口脱靶非常典型的场景正在跟踪的行人突然跑起来相邻两帧目标位移超过半个窗口搜索窗口里目标只占一小块甚至完全没有mean shift迭代就会收敛到背景上。排查思路分三步。第一估算目标的帧间位移上限看带宽h是否设置过小第二如果目标是加速运动先引入运动预测把预测位置作为mean shift的起始搜索中心第三使用由粗到细的多分辨率策略先在缩小一半的图像上跑一次mean shift得到粗略位置再回到原图精确定位。这个方法在跟踪大目标时尤其实用。4.2 目标与背景颜色相近窗口漂移这是mean shift最大的“翻车现场”。我在一个项目里跟踪室内人员人的上衣颜色与墙面非常接近。前面几百帧还算正常视角稍微一变窗口就一点点滑到墙上再也没有回来。根本原因是单一颜色直方图丢掉了空间信息和结构信息。背景与目标颜色分布重合相似度函数在背景区域也存在不小的峰值窗口自然会被“山”吸过去。我的处理方案有三条增加梯度方向直方图作为第二特征通道颜色相近但纹理不同的目标就能区分开。空间分块建模把目标区域切成2×2或3×3的小块每块单独建直方图匹配时按块加权综合。这个做法保留了“左半边是A色、右半边是B色”的空间关系效果提升非常明显。采用更保守的模型更新策略只在高相似度时更新避免背景像素一点一点“学”进模型。4.3 遮挡与目标消失丢失与找回遮挡问题是任何没有检测能力的跟踪算法的软肋。目标被完全遮挡期间搜索窗口里全是背景巴氏系数会明显下跌。此时如果继续照常更新模型基本等于自杀。我实践下来的方案是当相似度低于0.7~0.8时判定疑似遮挡或丢失立即停止模型更新同时启动位置预测用线性速度模型或卡尔曼滤波持续外推目标位置在预测位置附近继续执行mean shift搜索因为目标很可能从遮挡物边缘重新出现等相似度重新恢复到阈值以上再恢复正常跟踪流程。这套机制在车辆被路灯、树冠短暂遮挡的监控视频里实测效果很好。4.4 直方图没有空间感的“结构误导”最后说一个隐蔽问题颜色直方图对像素位置完全不敏感。如果目标本身左右对称、上下对称那么窗口偏移后直方图可能没有明显变化迭代就会在两个相似峰值之间犹豫不决。比如一个红白相间的交通锥窗口往左偏和往右偏看到的红白比例几乎一样mean shift就会在这两个位置附近来回摆动跟踪坐标抖动严重。解决办法就是我前面提到的空间分块。分块不仅保留目标的空间构成关系还能在相似度计算时引入位置权重离目标中心越近的子块权重越高。加入空间约束后窗口一旦偏移各子块的匹配度会同时下降算法能明显感知“偏了”不再左右摇摆。5. 工程化改进路线与实战心得5.1 与卡尔曼滤波的黄金组合说实战中最有用的改进我首推mean shift与卡尔曼滤波的结合。它解决的正是mean shift最痛的问题——纯位置搜索没有运动记忆。具体结构不复杂卡尔曼滤波器维护目标的位置和速度状态。每帧开始先用卡尔曼预测目标当前可能出现的位置把它作为mean shift迭代的起始中心。mean shift在预测位置附近精确定位得到更准确的位置观测。再用这个观测更新卡尔曼状态。目标短暂被遮挡时卡尔曼也能在短时间内维持合理的外推位置避免mean shift一头扎进背景。简单算一笔收益账mean shift常规迭代平均5到8次收敛。将起始点从上一帧位置换成卡尔曼预测位置后平均收敛次数能降到3到5次跟踪稳定性和实时性同时得到提升。位置抖动也明显减少。5.2 尺度自适应与模型重置原始mean shift假设窗口尺寸不变但实际目标走近走远、尺度变化很常见。经典的改进方式是用多个带宽h同时跑mean shift比如在原带宽的0.8倍、1.0倍、1.2倍下各跑一次选巴氏系数最高的结果作为最终位置。代价是计算量增加但目标尺度缓慢变化时效果非常稳。另一条路线是定期重置模型。我在有的项目里固定每30帧用历史高置信度帧重新初始化一次目标直方图相当于给模型洗澡防止长时间跟踪累积漂移。要注意重置时使用的历史帧必须足够可信否则会把之前的错误学回来。5.3 个人经验总结最后聊几点这些年实际使用mean shift的体会。第一别和深度学习硬碰硬选场景。如果目标类别不固定、算力充足、标注数据很多深度学习跟踪器确实更强。但如果你的场景是固定摄像头、目标外观相对稳定、实时性有硬约束、算力有限mean shift加卡尔曼依然是性价比极高的方案。我见过同事在嵌入式平台上调深度跟踪器两周还没达到实时要求换成mean shift方案一天就跑通效果完全够用。第二参数一定要离线调好再上线。h的值、直方图bin数、更新阈值和相似度阈值这些参数与你的具体目标和分辨率强相关不要直接抄论文里的数值。我的习惯是录一段覆盖真实场景的视频离线做网格搜索找到在关键帧都不丢目标的最小参数组合再上线微调。第三模型更新要“胆小”。宁可模型稍微旧一点也不要让低置信度观测污染模型。后来我把模型更新逻辑改成“相似度高于阈值才更新且新模型由旧模型与当前候选加权融合”长跑几十分钟都不会漂移这在早期版本里是不可想象的。最后分享一个调试技巧跑算法时把每帧的目标窗口画出来同时把相似度ρ实时显示在画面一角。一旦发现ρ持续下降或窗口抖动立刻暂停回看判断是目标外观变化还是背景干扰。定位问题的速度会快很多。别问我调了多少次才练出这个习惯——反正每次都靠它救场。

相关新闻

pix2pixHD模型搭建核心解析:多尺度判别与级联生成器实战指南

pix2pixHD模型搭建核心解析:多尺度判别与级联生成器实战指南

1. 项目概述:为什么pix2pixHD的models搭建是图像生成落地的关键一环pix2pixHD不是个“拿来就能跑”的玩具模型,它是一套为高分辨率图像条件生成而深度定制的工程化方案。我带过好几个图像生成方向的模拟项目X,发现90%的新手卡点不在数据预处理…

2026/10/17 19:52:01 阅读更多 →
openclaw技能机制解析:从Graham发布fly技能看Agent技能开发与部署

openclaw技能机制解析:从Graham发布fly技能看Agent技能开发与部署

1. 从"Graham 加入 openclaw"这条消息说起第一次看到"Graham 加入 openclaw 并发布 fly 技能"这个标题,我脑子里冒出来的第一个念头是:这又是一个把"人"和"平台"绑在一起讲的故事。Graham 是谁、openclaw 是什么…

2026/10/17 19:52:01 阅读更多 →
工程拆解与风元素分析:从黑盒观察到性能优化的完整指南

工程拆解与风元素分析:从黑盒观察到性能优化的完整指南

这次我们来看一个不算新、但很值得重新过一遍的话题:工程拆解和风元素分析。这里的“工程”不是指某一个开源工具的安装包,而是指一份可以反复使用的“项目拆解方法论”;“风元素”则是拆解对象里最容易被忽略、又最能体现项目质量的一类表现…

2026/10/17 19:51:00 阅读更多 →

最新新闻

从私钥到Web3入口:钱包技术演进与实现原理

从私钥到Web3入口:钱包技术演进与实现原理

1. 钱包到底是什么:先搞懂“币”其实不在钱包里很多刚接触区块链的朋友都会问我一个问题:我钱包里的币到底存在哪里?是不是像银行一样,有个中央服务器帮我记账?答案可能和直觉相反——你的币从来不在“钱包”里&#x…

2026/10/18 8:36:17 阅读更多 →
FastColoredTextBox中文修正版:彻底解决光标错位与样式渲染

FastColoredTextBox中文修正版:彻底解决光标错位与样式渲染

简介:FastColoredTextBox 是一款广受欢迎的开源高亮代码文本框控件,支持自定义显示模式,并默认内置 C#、HTML 等多种语言的高亮规则;原版在处理中文双字节字符时经常出现显示异常、光标位置偏移,多个 Style 同时使用时…

2026/10/18 8:36:17 阅读更多 →
用Flask自研研学预约系统:数据模型、支付回调与防超卖实战

用Flask自研研学预约系统:数据模型、支付回调与防超卖实战

1. 项目缘起:为什么我一头扎进了自研研学预约系统先说清楚我做的这套东西是什么:一个面向中小型营地/研学机构的在线课程预约系统,用Python的Flask框架从零搭起,涵盖了课程排期、名额管理、在线支付确认、后台订单管理这些核心环节…

2026/10/18 8:36:17 阅读更多 →
拆解开源Agent框架:插件化的核心机制、实战与避坑指南

拆解开源Agent框架:插件化的核心机制、实战与避坑指南

先把话说在前面:我拆的是最近热度很高的一个开源 Agent 框架,拆法比较粗暴,直接把源码拉下来,从入口一路跟到插件装载、工具调用、上下文组装、退出清理,一条线走完。拆完之后我对“插件化”这三个字的理解&#xff0c…

2026/10/18 8:36:17 阅读更多 →
Spring Event事件机制:从同步耦合到异步解耦的完整实践与避坑指南

Spring Event事件机制:从同步耦合到异步解耦的完整实践与避坑指南

简介:面向 Spring 开发者的 Event 事件机制示例工程,以可运行代码演示事件定义、发布、监听、顺序控制与异步处理等核心用法,适合正在系统学习 Spring 事件模型,或希望在业务模块间实现低耦合通信的读者。资源共 75 个文件&#x…

2026/10/18 8:36:17 阅读更多 →
阵列天线方向图对比:均匀阵、切比雪夫、泰勒与稀疏阵选型指南

阵列天线方向图对比:均匀阵、切比雪夫、泰勒与稀疏阵选型指南

简介:阵列天线方向图比较是阵列天线设计中的重要基础,单元个数、阵元间距与波长三者共同决定波束宽度、旁瓣电平与零点位置。这套MATLAB源码包面向无线通信、雷达与天线工程学习者,可帮助读者快速仿真不同参数组合下的方向图差异,…

2026/10/18 8:35:17 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →