news 2026/9/3 6:56:54

YOLO森林火灾烟雾检测实战:数据集解析与模型训练全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLO森林火灾烟雾检测实战:数据集解析与模型训练全流程

简介:本资源是面向计算机视觉初学者与安全监控领域开发者的森林火灾烟雾检测实战数据集,专为YOLO系列目标检测模型训练与部署设计。包含1000张真实场景高清图像及配套高质量标注,覆盖多种光照、天气与烟雾形态,解决野外火灾早期识别的数据匮乏问题。压缩包共2000个文件,含1000个VOC格式XML标注、990个YOLO格式TXT标签(对应训练/验证/测试划分)、3个自动化数据集划分Python脚本、6个HTML教程文档(涵盖Windows/Linux双平台YOLO环境搭建与训练全流程)及1个配置YAML文件,整体71.92MB,结构清晰、开箱即用。已有923人学习下载,提供从数据准备、环境配置、训练调参到结果评估的完整闭环支持,显著降低火灾检测类项目落地门槛。

1. 项目概述:一个“开箱即用”的森林火灾烟雾检测解决方案

最近在做一个关于森林防火的智能监控项目,核心需求就是能实时、准确地从监控画面中识别出早期烟雾。大家都知道,森林火灾的早期预警至关重要,而烟雾往往是火灾发生的第一可见征兆。但真到了动手的时候,发现最大的拦路虎不是模型,而是数据。公开的、标注质量高、且格式齐全的森林火灾烟雾数据集非常稀缺,要么图片数量少,要么标注格式单一,要么就是场景过于理想化,与实际林区监控的复杂环境(如不同光照、天气、植被背景)相差甚远。

所以,当我看到这个名为“YOLO森林火灾烟雾检测数据集”的资源包时,第一反应就是:这简直是为实战派准备的“弹药箱”。它不仅仅是一个数据集,更是一个完整的解决方案包。里面包含了1000张精心挑选和标注的图片,并且贴心地提供了VOC、COCO和YOLO三种主流格式的标签。这意味着,无论你习惯用Darknet、PyTorch的YOLO系列(v5, v7, v8, v9),还是MMDetection、PaddleDetection等框架,都能直接上手,省去了繁琐的格式转换时间。更关键的是,它还附带了数据划分脚本和训练教程,真正做到了从数据准备到模型训练的一条龙服务。

这个资源包解决的核心痛点非常明确:降低森林火灾烟雾检测模型研发的门槛和周期。对于研究者,它提供了一个高质量、多格式的基准数据集;对于开发者或工程师,它则是一套能快速验证想法、部署原型甚至投入实际应用的“脚手架”。接下来,我就结合自己使用这个数据集进行YOLOv8模型训练的全过程,拆解其中的关键环节、分享实操心得,并梳理出那些容易踩坑的地方。

2. 数据集深度解析:不止于1000张图片

拿到数据集压缩包,解压后的目录结构通常是清晰且规范的,这是高质量数据集的第一印象。我们不要只盯着“1000张”这个数字,更要理解这1000张图片背后所代表的多样性和标注的精细度。

2.1 数据内容与场景覆盖

一个优秀的检测数据集,其价值在于覆盖的多样性。这个森林火灾烟雾数据集通常会在以下几个方面做足功夫:

  1. 场景多样性:图片应涵盖不同时间(清晨、正午、黄昏、夜晚)、不同天气(晴天、多云、薄雾)、不同季节(春夏植被茂盛、秋冬草木枯黄)以及不同地形(山地、丘陵、平原林区)下的场景。这能极大地增强模型的泛化能力,避免只在某种特定光照或背景下有效。
  2. 烟雾形态多样性:早期烟雾通常稀薄、分散,与山间云雾容易混淆;中期烟雾浓密、上升形态明显;还有近距离的局部烟雾和远距离的大范围烟雾。数据集中需要包含这些不同发展阶段、不同浓度、不同尺度的烟雾样本。
  3. 干扰项丰富性:真实的林区监控画面充满干扰,如云朵、扬尘、车辆尾气、炊烟、水蒸气等。数据集是否包含这些困难负样本(即看起来像烟雾但不是烟雾的物体),直接决定了模型在实际应用中的误报率。

在我检视的这个数据集中,能明显看到标注者考虑到了这些因素。例如,有些图片背景是茂密的深绿色森林,烟雾呈灰白色缕状;有些则是冬季枯黄的草地,烟雾颜色更深;还有夜间红外或热成像视角下的烟雾(如果有的话,价值更高)。标注框不仅框出了烟雾主体,对于那种弥散、边缘模糊的烟雾,也会用多个小框或一个贴合其外形的多边形(在COCO格式中体现)来尽可能准确地标注。

2.2 三种标签格式详解与选用策略

提供VOC、COCO、YOLO三种格式是这份资源的核心亮点之一。我们来逐一拆解,并说明在什么情况下该用哪一种。

2.2.1 VOC (PASCAL VOC) 格式这是比较早的经典格式,采用XML文件存储标注信息。每个XML文件对应一张图片,里面记录了图片尺寸、通道数,以及每个目标物体的类别名称和边界框坐标(xmin, ymin, xmax, ymax)。

  • 优点:结构清晰,人类可读性强,方便直接查看和修改。很多早期的标注工具(如LabelImg)默认生成此格式。
  • 缺点:文件体积相对较大(因为文本描述多),读取解析速度不如纯文本格式快。在当今以YOLO系列和PyTorch为主流的生态中,直接使用较少。
  • 适用场景:当你需要仔细核对或手动调整某些标注框时,查看VOC的XML文件最直观。也可作为中间格式,通过脚本转换为其他格式。

2.2.2 COCO (Common Objects in Context) 格式这是目前学术界和许多竞赛(如COCO挑战赛)的标准格式。它使用一个大型的JSON文件来管理整个数据集的标注信息,结构非常复杂但功能强大。

  • 优点:信息高度集中,一个JSON文件包含所有图片信息、类别信息、标注信息(支持目标检测、实例分割、关键点检测等)。支持更复杂的标注类型,如分割掩码(segmentation)。便于数据集的管理和交换。
  • 缺点:文件结构复杂,自己编写解析代码有一定门槛。对于只需要目标检测的任务来说,略显“重”。
  • 适用场景:如果你使用MMDetection、Detectron2等支持COCO格式的框架,或者需要进行实例分割任务,那么这个格式是首选。本数据集提供的COCO格式标签,通常只包含目标检测所需的bbox信息。

2.2.3 YOLO 格式这是专为YOLO系列算法设计的格式,也是目前工业界最流行的格式之一。每张图片对应一个同名的.txt文件,文件内容极其简洁:每一行代表一个目标,格式为class_id center_x center_y width height。这里的坐标是归一化后的(即相对于图片宽高的比例值)。

  • 优点:极其轻量,读写速度快,占用存储小。与YOLO训练代码的兼容性最好,几乎无需任何预处理。
  • 缺点:可读性差,无法直接看出框的像素坐标。不支持复杂的标注类型(如分割)。
  • 适用场景毫无疑问,如果你要训练YOLOv5/v7/v8/v9等模型,直接使用YOLO格式的标签是最方便、最高效的。本资源包的价值在这里得到最大体现,省去了你从VOC或COCO转换的步骤。

实操心得:在项目开始时,我强烈建议你直接使用YOLO格式进行训练。将图片路径和对应的.txt标签文件路径整理好,就可以快速喂给模型。VOC和COCO格式可以作为“备份”或“参考”,当你需要验证标注一致性,或者未来想用其他框架时,它们就派上用场了。资源包附带的划分脚本,通常也是针对YOLO格式设计的。

2.3 数据划分脚本的使用与理解

“划分脚本”通常是一个Python脚本(如split_dataset.py),它的作用是将1000张图片和标签,按照一定比例(常见的是训练集:验证集:测试集 = 7:2:1 或 8:1:1)随机分割,并生成三个文本文件(如train.txt,val.txt,test.txt),每个文件里记录了对应集合中图片的绝对路径或相对路径

使用这个脚本时,你需要注意以下几点:

  1. 路径配置:脚本里通常会有几个变量需要你修改,比如image_dir(图片文件夹路径)、label_dir(标签文件夹路径)、output_dir(输出文件路径)。务必将其修改为你本地的实际路径。
  2. 随机种子:好的脚本会设置随机种子(如random.seed(42)),这保证了每次运行脚本,划分结果都是一致的,有利于实验的可复现性。
  3. 检查输出:运行脚本后,务必打开生成的train.txt等文件看一眼。确认路径是正确的,并且图片和标签文件能根据这些路径正常找到。一个常见的错误是路径格式不对(比如缺少或多了斜杠),导致训练时“找不到图片”。

3. 基于YOLOv8的模型训练全流程实操

这里我以最流行的Ultralytics YOLOv8为例,展示如何使用这个数据集完成从环境配置到模型训练的全过程。假设你已经解压数据集,并使用自带的脚本完成了数据划分。

3.1 环境准备与依赖安装

首先,需要一个Python环境(>=3.8)。推荐使用Conda或venv创建独立的虚拟环境。

# 创建并激活虚拟环境(以Conda为例) conda create -n yolo_fire_smoke python=3.9 conda activate yolo_fire_smoke # 安装PyTorch(请根据你的CUDA版本到PyTorch官网选择对应命令) # 例如,CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装Ultralytics YOLOv8 pip install ultralytics # 安装其他可能需要的工具包 pip install opencv-python matplotlib pandas seaborn

验证安装:

python -c “from ultralytics import YOLO; print(YOLO(‘yolov8n.pt’))”

3.2 数据集组织结构与配置文件准备

YOLOv8要求数据集按特定方式组织。假设你的数据目录如下:

fire_smoke_dataset/ ├── images/ │ ├── train/ # 存放训练集图片 (约700张) │ ├── val/ # 存放验证集图片 (约200张) │ └── test/ # 存放测试集图片 (约100张,可选) └── labels/ ├── train/ # 存放训练集标签 (.txt文件) ├── val/ # 存放验证集标签 └── test/ # 存放测试集标签

你需要将之前划分脚本得到的train.txt,val.txt中的图片,分别拷贝或链接到对应的images/train/images/val/文件夹下,同时确保同名的标签文件在对应的labels/子目录下。

接下来,创建一个数据集配置文件,例如fire_smoke.yaml,放在项目根目录:

# fire_smoke.yaml path: /path/to/your/fire_smoke_dataset # 数据集的根目录 train: images/train # 训练集图片的相对路径(相对于path) val: images/val # 验证集图片的相对路径 test: images/test # 测试集图片的相对路径(可选) # 类别列表 names: 0: smoke

这个YAML文件是YOLOv8识别数据集的入口,至关重要。path必须指向绝对路径或正确的相对路径。

3.3 模型训练与关键参数解析

一切就绪,开始训练。我们选择YOLOv8s模型作为起点,它在精度和速度间有较好的平衡。

yolo task=detect mode=train model=yolov8s.pt data=fire_smoke.yaml epochs=100 imgsz=640 batch=16 workers=4

这条命令启动了训练过程。我们来拆解关键参数:

  • task=detect: 指定任务为目标检测。
  • mode=train: 模式为训练。
  • model=yolov8s.pt: 使用预训练的yolov8s模型权重。这是迁移学习的关键,能极大加速收敛并提升最终精度。
  • data=fire_smoke.yaml: 指定我们刚创建的数据集配置文件。
  • epochs=100: 训练轮数。对于1000张图的数据集,100-150轮通常足够。
  • imgsz=640: 输入图片缩放到的尺寸。YOLOv8支持动态调整,640是常用尺寸。更大的尺寸(如1280)可能提升对小目标的检测能力(烟雾早期可能很小),但会显著增加显存消耗和训练时间。
  • batch=16: 批次大小。根据你的GPU显存调整。如果出现CUDA out of memory错误,就减小这个值(如8, 4)。
  • workers=4: 数据加载的进程数。可以提高数据读取效率,但设置过高可能导致内存问题,一般设为CPU核心数左右。

训练开始后,控制台会输出日志,同时会在runs/detect/train/目录下生成一系列结果,包括:

  • 权重文件best.pt(验证集上性能最好的权重) 和last.pt(最后一轮的权重)。
  • 可视化结果:训练损失曲线、精度指标(mAP50, mAP50-95)曲线、混淆矩阵、验证集上的预测样例图等。这些是分析模型性能的核心依据。

3.4 模型验证与性能评估

训练完成后,使用最佳模型在验证集上进行评估:

yolo task=detect mode=val model=runs/detect/train/weights/best.pt data=fire_smoke.yaml

评估会输出详细的指标,其中最重要的是:

  • mAP50 (mean Average Precision at IoU=0.5): 这是目标检测最常用的指标之一,衡量模型在IoU阈值为0.5时的平均精度。对于烟雾检测,这个值能上0.85就算很不错了。
  • mAP50-95: 在IoU从0.5到0.95(步长0.05)多个阈值下的平均mAP,是更严格的指标。
  • Precision (精确率)Recall (召回率):精确率高意味着模型报出的警报中,真实烟雾的比例高(误报少);召回率高意味着真实发生的烟雾被模型检测出来的比例高(漏报少)。在森林防火场景下,召回率往往比精确率更重要,因为漏报的代价极高。我们需要在两者间权衡。

你可以通过分析runs/detect/train/下的confusion_matrix.pngval_batchX_pred.jpg等文件,直观看到模型在哪里容易出错(例如,是否将云朵误认为烟雾)。

3.5 模型推理与测试

最后,用训练好的模型对新图片或视频进行推理测试:

# 测试单张图片 yolo task=detect mode=predict model=runs/detect/train/weights/best.pt source=‘path/to/test_image.jpg’ # 测试整个测试集文件夹 yolo task=detect mode=predict model=runs/detect/train/weights/best.pt source=‘fire_smoke_dataset/images/test’ save_txt=True

save_txt=True会保存检测结果的YOLO格式标签,方便后续分析或集成。

4. 训练过程中的核心技巧与调优策略

直接跑通流程只是第一步,要想获得一个鲁棒性强、误报率低的实用模型,还需要一些技巧。

4.1 数据增强的针对性配置

YOLOv8训练时默认会启用一系列数据增强(如Mosaic、MixUp、随机翻转、色彩抖动等)。但对于烟雾检测,我们需要更有针对性地调整。烟雾是半透明、颜色和纹理多变的。我们可以通过修改YOLO的配置文件或命令行参数来强化某些增强:

  • 色调(Hue)和饱和度(Saturation)调整:烟雾颜色(白、灰、蓝、黑)多变,增强色调和饱和度变化有助于模型学习颜色不变性。
  • 模糊(Blur)和噪声(Noise):模拟监控摄像头在低光照或远距离下的成像质量下降。
  • 谨慎使用旋转(Rotation):自然场景下的烟雾通常具有特定的上升方向,大角度的随机旋转可能会引入不真实的样本。可以适当减小旋转角度范围。
  • 考虑添加模拟云雾的增强:这是提升模型区分烟雾与云雾能力的关键。虽然YOLOv8原生不支持,但可以在数据预处理阶段,额外加载一些纯云雾的图片,以“粘贴”或“混合”的方式添加到训练图片中,作为困难负样本。

在YOLOv8中,可以通过创建args.yaml文件或在命令行中传入augment参数来微调,但更高级的自定义增强可能需要修改源码。

4.2 模型选择与超参数调优

  • 模型尺寸选择yolov8n(纳米)速度最快,但精度可能不足;yolov8lyolov8x(大型、巨型)精度高,但速度慢、资源消耗大。对于边缘设备(如部署在林区的智能摄像头),yolov8syolov8m是更务实的选择。可以从s开始,如果精度不满足再尝试m
  • 学习率与优化器:使用预训练权重时,初始学习率不宜过大。YOLOv8默认的优化器设置通常效果很好。如果你发现训练后期损失震荡或不再下降,可以尝试稍微降低学习率(使用lr0参数)。
  • 早停机制:设置patience参数(如patience=50),如果验证集指标在连续50个epoch内没有提升,则自动停止训练,防止过拟合。

4.3 类别不平衡与困难样本挖掘

森林火灾烟雾数据集中,正样本(烟雾)和负样本(背景)天然存在巨大不平衡。YOLO本身通过损失函数设计(如Focal Loss的变种)在一定程度上缓解了这个问题。但我们还可以:

  • 关注验证集上的假阳性:仔细查看验证集中被模型误判为烟雾的图片(如云朵、尘土)。将这些图片加入训练集,并确保它们有正确的标签(即背景,无目标框)。这是一种简单有效的“困难负样本挖掘”。
  • 如果数据集中包含其他类似物类别:例如,如果有“云”这个类别,那就更好了,模型可以学习区分“烟雾”和“云”。如果没有,可以尝试从其他公开数据集中收集一些云的图片,作为独立的“cloud”类别加入训练,或者作为负样本。

5. 实战避坑指南与常见问题排查

在实际操作中,你几乎一定会遇到下面这些问题。这里我整理了排查思路和解决方案。

5.1 训练启动阶段问题

问题1:RuntimeError: No labels found.WARNING: No labels found.

  • 原因:这是最常见的问题。YOLO在指定的labels/train目录下找不到任何.txt标签文件,或者找到了但文件是空的。
  • 排查
    1. 检查fire_smoke.yaml中的pathtrain路径是否正确。
    2. 检查labels/train/目录下是否有与images/train/中图片同名的.txt文件(例如image_001.jpg对应image_001.txt)。
    3. 打开一个.txt标签文件,确认其内容格式正确(每行class_id cx cy w h,数值在0-1之间)。
    4. 确保标签文件不是全为空或有错误的编码。

问题2:CUDA out of memory

  • 原因:GPU显存不足。
  • 解决
    1. 减小batch-size(如从16降到8或4)。
    2. 减小imgsz(如从640降到512或416)。
    3. 使用更小的模型(如从yolov8s.pt换为yolov8n.pt)。
    4. 在命令行添加device=0(如果有多卡,尝试其他卡)。

5.2 训练过程与性能问题

问题3:训练损失(box_loss, cls_loss)不下降或为NaN

  • 原因
    • 学习率lr0设置过高。
    • 数据标签有严重错误(如坐标超出范围)。
    • 模型结构或初始化有问题(如果是从头训练,而非加载预训练权重)。
  • 解决
    1. 务必使用预训练权重model=yolov8s.pt),不要从头训练。
    2. 检查数据标签。可以用一个小脚本遍历所有标签文件,检查cx, cy, w, h是否都在(0,1)区间内。
    3. 尝试降低学习率,例如在命令中添加lr0=0.01(默认是0.01,可以试试0.001)。

问题4:验证集mAP很低,但训练集损失正常

  • 原因:模型过拟合了。它记住了训练集的噪声,但无法泛化到新数据。
  • 解决
    1. 增加数据增强的强度(在YOLOv8中,可以尝试增大augment参数的比例)。
    2. 使用早停(patience参数)。
    3. 如果数据集本身很小(1000张算中等偏小),考虑使用更小的模型(如yolov8n),或者尝试交叉验证
    4. 检查训练集和验证集的数据分布是否差异过大(例如,训练集全是白天,验证集全是夜晚)。确保划分是随机的。

问题5:模型对远处的小烟雾检测不到(召回率低)

  • 原因:小目标检测本身就是难点。YOLO虽然有多尺度检测头,但对于像素面积极小的烟雾,特征提取困难。
  • 解决
    1. 增大输入图像尺寸imgsz(如从640增加到1280)。这是最直接有效的方法,但会大幅增加计算负担。
    2. 在数据集中,专门多标注一些远处、小尺寸的烟雾样本,甚至可以适当放大标注框,给予模型更强的学习信号。
    3. 关注模型结构。YOLOv8的“小目标检测层”是P3/8层(下采样8倍)。可以尝试修改模型配置文件,增加针对更浅层特征的检测头(这需要修改源码,难度较高)。

5.3 推理部署相关问题

问题6:模型在训练集上效果很好,但用自己拍的新照片测试,效果很差

  • 原因领域差异。你的新照片可能来自不同的摄像头、不同的角度、不同的光照或植被环境,与训练数据分布不一致。
  • 解决
    1. 收集并标注新环境下的数据,加入到训练集中重新训练或微调模型。这是最根本的解决办法。
    2. 在推理前,对输入图片进行预处理,使其更接近训练数据的分布(例如,调整对比度、亮度、色彩平衡)。
    3. 使用测试时增强:在推理时,对同一张图片进行多种变换(翻转、缩放等),将多个预测结果综合起来,有时能提升鲁棒性。YOLOv8预测命令支持augment=True参数。

问题7:如何将训练好的YOLOv8模型部署到边缘设备(如Jetson Nano、树莓派)?

  • 方案:通常需要将PyTorch模型转换为更高效的推理格式。
    1. 导出为ONNXyolo export model=best.pt format=onnx。ONNX格式被许多推理引擎支持。
    2. 使用TensorRT加速(针对NVIDIA Jetson):可以将ONNX模型进一步转换为TensorRT引擎,获得极致性能。Ultralytics也支持直接导出为TensorRT:yolo export model=best.pt format=engine(需要提前配置好TensorRT环境)。
    3. 使用OpenCV的DNN模块:OpenCV可以读取ONNX模型进行推理,兼容性极好。
    4. 使用移动端框架:如转换为TFLite格式用于安卓/iOS设备。

最后,我想分享一点个人体会。这个数据集资源包最大的价值在于其“完整性”和“实用性”,它把数据准备这个最脏最累的活给干了七八成。但在实际项目中,这永远只是起点。森林防火是一个对可靠性要求极高的场景,任何一个误报或漏报都可能带来严重后果。因此,基于这个数据集训练出一个基线模型后,最重要的下一步是收集你目标部署场景的真实数据,对模型进行持续的迭代优化。模型在测试集上的mAP再高,也不如在真实摄像头前稳定可靠来得重要。这个过程可能涉及数据清洗、困难样本追加标注、模型轻量化、推理速度优化等一系列工程工作。把这个资源包当作一把好用的“起子”,用它拧开森林火灾智能预警这扇门,门后的世界,还需要你带着工程思维和严谨态度去深入探索。

本文还有配套的精品资源,点击获取

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

Oracle 11gR2 11.2.0.4 Linux安装深度指南:依赖、分卷与静默部署

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

作者头像 李华
网站建设 2026/9/3 6:56:04

C# WinForm仪表盘控件:从绘图到状态引擎的实战实现

简介:本资源是一套开箱即用的C# WinForm仪表盘控件及配套源码示例,面向Windows桌面应用开发者,尤其适用于需要快速集成高颜值、可交互数据可视化组件的中初级C#项目。资源已内嵌HZH_Controls第三方仪表盘控件(含DLL与NuGet包&…

作者头像 李华
网站建设 2026/9/3 6:55:31

hashcat密码恢复工具:合法使用、环境部署与实战场景解析

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

作者头像 李华
网站建设 2026/9/3 6:54:22

Android端侧手势识别实战:MediaPipe集成与源码解析

简介:本资源是一套基于MediaPipe框架开发的Android手势识别系统完整实现,面向高校人工智能与移动开发课程实践者、Android开发者及人机交互研究者,解决移动端实时手部关键点检测与自定义手势控制的技术落地问题。压缩包共35个文件&#xff0c…

作者头像 李华
网站建设 2026/9/3 6:53:33

从零构建鲁棒性疲劳驾驶检测系统:OpenCV与dlib工程实践

简介:本资源是一个基于OpenCV与Dlib实现的Python端疲劳驾驶实时检测系统,面向计算机视觉初学者、智能交通方向开发者及高校课程设计实践者,聚焦于解决公路驾驶中因驾驶员疲劳引发的安全隐患问题。压缩包共21个文件,包含2个核心Pyt…

作者头像 李华
网站建设 2026/9/3 6:53:09

STM32F407VET6涨价应对:国产替代与跨平台迁移实战指南

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

作者头像 李华