图像噪声去除实验:均值、中值与高斯滤波的PSNR/SSIM对比与调参
1. 图像噪声去除实验的整体设计思路数字图像处理这门课实验环节往往比理论课更让人头疼。课本上给你一个公式告诉你“用中值滤波可以去除椒盐噪声”但真到动手写代码的时候你会发现事情远没有那么简单——噪声类型怎么判断滤波器窗口开多大不同算法在同一张图上跑出来的效果为什么差距那么大这些问题课本不会给你答案只有自己一行行代码敲出来、一张张图对比过才能形成真正属于自己的理解。这次要聊的就是这样一个典型的课程实验图像噪声去除。它是数字图像处理课程中实验环节的经典项目核心目标很明确——给一张被噪声污染的图像用不同的滤波方法把它尽量恢复成干净的样子同时尽可能保留原始图像的细节信息。这个实验适合正在学数字图像处理的学生、刚入门计算机视觉的开发者以及任何想搞清楚“滤波器到底在干什么”的人。不管你之前有没有接触过OpenCV或者MATLAB的图像处理工具箱只要你能写基本的循环和数组操作这个实验就能跑起来。但我想说的不只是“怎么跑通”。我想把我在做这个实验时踩过的坑、调参时总结的经验、以及不同方法之间对比后得出的判断都摊开来聊一聊。因为噪声去除这件事表面上是“调个函数”实际上涉及对噪声模型的理​​解、对滤波器原理的把握、对评价指标的运用以及对“去噪”和“保边”这对根本矛盾的权衡。这些东西搞清楚了以后再遇到图像增强、边缘检测、图像分割这些任务思路会清晰很多。整个实验的设计思路可以概括为一条主线先造噪声再除噪声最后量化对比。造噪声是为了获得“标准答案”——我们知道干净图是什么样也知道加了什么类型的噪声这样才能客观评价去噪效果。除噪声是核心环节要覆盖至少三种典型方法均值滤波、中值滤波、高斯滤波有条件的话还可以加上双边滤波或非局部均值去噪作为进阶对比。量化对比则需要用到PSNR和SSIM这两个指标不能光靠肉眼看。为什么强调“量化”因为肉眼判断在图像处理里非常不可靠。一张图经过均值滤波后看起来“平滑了”但可能边缘也糊了中值滤波看起来“干净了”但细纹理可能被抹掉了。只有把PSNR和SSIM算出来再结合局部放大图的视觉对比才能得出靠谱的结论。这也是我在这个实验中最大的体会不要相信你的眼睛要相信指标但也不要只相信指标要指标加视觉双重验证。下面我会按照实验的实际操作流程从环境准备、噪声模拟、滤波器实现、参数调优、指标评价到问题排查一步步展开。每个环节我都会解释“为什么这么做”并附上我实际跑出来的参数和结果。你可以直接照着复现也可以根据自己的图像和噪声类型做调整。2. 实验环境搭建与噪声模拟实操2.1 工具选型PythonOpenCV还是MATLAB做数字图像处理实验工具选择是第一道坎。我两种都用过说下实际感受。MATLAB的优势在于图像处理工具箱非常成熟imnoise、medfilt2、fspecial这些函数开箱即用文档详细适合快速验证算法。但缺点是脚本语言在批量处理和参数扫描时效率偏低而且离开学校后正版授权是个问题。PythonOpenCV的组合则更灵活。OpenCV的cv2.GaussianBlur、cv2.medianBlur、cv2.filter2D性能很好NumPy做矩阵运算也快配合Matplotlib做可视化非常方便。更重要的是Python生态里有scikit-image这个库它提供了peak_signal_noise_ratio和structural_similarity这两个现成的评价指标函数省去了自己实现的麻烦。我最终选择的是Python 3.9 OpenCV 4.5 scikit-image 0.19 NumPy Matplotlib这套组合。安装命令如下pip install opencv-python scikit-image numpy matplotlib注意OpenCV的cv2.imread默认读取的是BGR格式而Matplotlib显示时用的是RGB格式。如果不做转换图像颜色会偏蓝。这个坑我踩过不止一次建议在读取后统一用cv2.cvtColor(img, cv2.COLOR_BGR2RGB)转一下。2.2 噪声类型的选择与生成图像噪声去除实验的第一步是给干净图像加噪声。常见的噪声类型有三种高斯噪声服从正态分布每个像素值加上一个均值为0、标准差为σ的随机值。这是最贴近真实传感器噪声的模型。椒盐噪声随机将一定比例的像素置为0黑点或255白点模拟传感器坏点或传输错误。泊松噪声服从泊松分布常见于低光照条件下的光子计数噪声。课程实验通常要求覆盖前两种。我用的是scikit-image的random_noise函数它支持多种噪声模式一行代码就能搞定from skimage import io, random_noise img io.imread(lena_gray.png) # 读取灰度图 img_gaussian random_noise(img, modegaussian, var0.01) img_salt_pepper random_noise(img, modesalt, amount0.05)这里有几个参数需要解释。var0.01表示高斯噪声的方差注意random_noise接受的图像是0到1的浮点范围所以方差0.01对应的标准差是0.1换算到0到255范围大约是25.5的灰度波动。amount0.05表示椒盐噪声的比例即5%的像素被污染。实操心得椒盐噪声的amount参数不要设太大。我试过0.1图像几乎面目全非去噪后PSNR也很难看。0.02到0.05是比较合理的范围既能看出噪声影响又不至于完全破坏图像结构。2.3 干净图像的选取与预处理选什么图做实验也有讲究。课程实验常用Lena、Cameraman、Peppers这些标准测试图它们的特点是纹理丰富、有平坦区域也有细节区域能全面检验滤波器的性能。如果你手头没有这些图可以用skimage.data里的内置图像from skimage import data img data.camera() # Cameraman灰度图512x512预处理方面建议统一转成灰度图再做去噪实验。彩色图当然也可以处理但三个通道分别滤波会增加计算量而且评价指标的计算也需要额外处理。灰度图足以说明问题等灰度图上的方法验证通过了再扩展到彩色图也不迟。另外图像的数据类型要统一。skimage.io.imread读进来通常是uint8而random_noise会把它转成float64。后续做滤波时OpenCV的函数对数据类型有要求比如cv2.medianBlur要求输入是uint8或float32。我的做法是加噪声后用np.clip把值限制在0到1之间再乘以255转回uint8这样后续OpenCV函数都能直接处理。3. 三种经典滤波器的实现与参数调优3.1 均值滤波简单但代价明显均值滤波的原理非常直观用一个n×n的窗口在图像上滑动每个位置取窗口内所有像素的平均值作为输出。OpenCV里用cv2.blur或cv2.boxFilter实现import cv2 import numpy as np img_noisy np.clip(img_salt_pepper * 255, 0, 255).astype(np.uint8) img_mean_3 cv2.blur(img_noisy, (3, 3)) img_mean_5 cv2.blur(img_noisy, (5, 5))窗口大小是最关键的参数。3×3的窗口去噪能力有限但细节保留较好5×5的窗口去噪更彻底但图像会明显变模糊。我实测下来对于方差0.01的高斯噪声5×5均值滤波的PSNR大约在28dB左右而3×3只有26dB左右。但看局部放大图会发现5×5滤波后的文字边缘已经糊得看不清了。均值滤波的根本问题在于它对所有像素一视同仁不管这个像素是噪声还是边缘。边缘处的像素值本来就和邻域差异大取平均后边缘就被“拉平”了。所以均值滤波适合处理平坦区域为主的图像对纹理丰富的图像效果不佳。注意事项均值滤波对椒盐噪声几乎无效。因为椒盐噪声的像素值是极端的0或255取平均后只是把噪声点的影响分摊到邻域噪声点本身并没有被消除只是变淡了。我试过用5×5均值滤波处理5%椒盐噪声的图PSNR只有22dB左右视觉上仍然能看到大量黑白点。3.2 中值滤波椒盐噪声的克星中值滤波的思路和均值滤波不同它取窗口内所有像素的中位数作为输出。中位数对极端值不敏感所以椒盐噪声的0和255会被邻域的正常像素值“投票”掉。img_median_3 cv2.medianBlur(img_noisy, 3) img_median_5 cv2.medianBlur(img_noisy, 5)中值滤波对椒盐噪声的效果立竿见影。5%椒盐噪声的图用3×3中值滤波后PSNR能到32dB以上视觉上几乎看不到噪声点。但窗口也不是越大越好。5×5中值滤波虽然去噪更彻底但细小的纹理比如头发丝、远处栅栏会被当成噪声抹掉。这里有个经验判断如果噪声密度低于10%3×3窗口足够如果噪声密度在10%到20%之间考虑5×5超过20%中值滤波也会力不从心需要更复杂的算法。中值滤波的另一个优势是计算复杂度比排序整个窗口低——OpenCV内部用了直方图加速实际运行速度很快。512×512的图3×3中值滤波在我的机器上不到10毫秒。3.3 高斯滤波平衡之选高斯滤波可以看作加权版的均值滤波窗口内每个像素的权重不是相等的而是根据距离中心点的远近按高斯分布递减。这样既平滑了噪声又比均值滤波更好地保留了边缘附近的梯度信息。img_gauss_3 cv2.GaussianBlur(img_noisy, (3, 3), sigmaX1.0) img_gauss_5 cv2.GaussianBlur(img_noisy, (5, 5), sigmaX1.5)高斯滤波有两个参数窗口大小和标准差σ。σ控制权重的衰减速度σ越大离中心远的像素权重越大平滑效果越强。通常σ取窗口大小的1/4到1/3比较合适。比如5×5窗口配σ1.57×7窗口配σ2.0。高斯滤波对高斯噪声的效果最好。方差0.01的高斯噪声5×5高斯滤波的PSNR能到29dB左右比均值滤波略高而且边缘保留更好。但对椒盐噪声高斯滤波和均值滤波一样无能为力因为极端值仍然会被加权平均进去。3.4 三种方法的横向对比我把三种方法在两种噪声下的表现整理成了一张表方便你快速判断该用哪个滤波方法窗口大小高斯噪声PSNR椒盐噪声PSNR边缘保留计算速度均值滤波3×326.2 dB21.8 dB差快均值滤波5×528.1 dB22.3 dB很差快中值滤波3×325.4 dB32.6 dB中等中等中值滤波5×526.8 dB34.1 dB较差较慢高斯滤波3×327.5 dB21.5 dB较好快高斯滤波5×529.3 dB22.1 dB中等快从表里能看出几个关键结论中值滤波在椒盐噪声上碾压其他两种方法PSNR高出10dB以上高斯滤波在高斯噪声上略优于均值滤波窗口越大PSNR通常越高但边缘保留越差。所以实际选方法时先判断噪声类型再根据对细节的要求选窗口大小。4. 评价指标的计算与结果分析4.1 PSNR最常用的量化指标PSNR峰值信噪比的定义是$$PSNR 10 \cdot \log_{10}\left(\frac{MAX^2}{MSE}\right)$$其中MAX是像素最大值8位图像为255MSE是去噪图与干净图之间的均方误差。PSNR越高说明去噪后的图像越接近原始图像。用scikit-image计算PSNR非常简单from skimage.metrics import peak_signal_noise_ratio as psnr psnr_value psnr(img_clean, img_denoised, data_range255)注意data_range参数要设对。如果图像是uint8类型data_range255如果是0到1的浮点类型data_range1.0。设错了结果会差很多。PSNR的优点是计算简单、物理意义明确但缺点也很明显它只衡量像素级的误差不考虑人眼的视觉特性。有时候PSNR很高的图看起来反而比PSNR略低的图更模糊。所以PSNR不能单独用。4.2 SSIM更贴近人眼感受的指标SSIM结构相似性从亮度、对比度、结构三个维度比较两幅图取值范围是-1到1越接近1说明结构越相似。它对模糊和结构失真的敏感度比PSNR高。from skimage.metrics import structural_similarity as ssim ssim_value ssim(img_clean, img_denoised, data_range255)我实测的数据是5%椒盐噪声下3×3中值滤波的SSIM约为0.92PSNR约32.6dB而5×5均值滤波的SSIM只有0.78PSNR约22.3dB。SSIM的差距比PSNR更大说明中值滤波在结构保留上的优势非常明显。4.3 视觉对比局部放大与残差图指标之外我强烈建议做两件事一是截取局部区域放大对比二是画残差图。局部放大能让你看清边缘和纹理的保留情况。比如Cameraman图中摄影师的三脚架区域细线条很多不同滤波器的差异在这里一目了然。残差图则是把去噪图减去干净图放大残差可以看到滤波器在哪些区域引入了误差——通常边缘处残差最大平坦区域残差很小。residual cv2.absdiff(img_clean, img_denoised) residual_enhanced cv2.normalize(residual, None, 0, 255, cv2.NORM_MINMAX)残差图增强后边缘处会呈现明显的亮线说明这些地方被滤波器改变了。中值滤波的残差在椒盐噪声点位置最亮但边缘处较暗均值滤波的残差在边缘处普遍较亮说明边缘被模糊了。4.4 参数扫描找到最优窗口大小为了找到最优参数我写了一个简单的扫描脚本遍历窗口大小从3到11记录PSNR和SSIMresults [] for k in [3, 5, 7, 9, 11]: denoised cv2.medianBlur(img_noisy, k) p psnr(img_clean, denoised, data_range255) s ssim(img_clean, denoised, data_range255) results.append((k, p, s))跑出来的结果是椒盐噪声下中值滤波的PSNR在窗口5×5时达到峰值34.1dB之后随窗口增大反而下降因为过度平滑引入了新的误差。SSIM则在3×3时最高说明小窗口对结构的保留更好。这个矛盾说明PSNR和SSIM并不总是一致选参数时要根据实际需求权衡——如果更看重去噪彻底性选5×5如果更看重细节保留选3×3。5. 常见问题与排查技巧实录5.1 去噪后图像出现彩色伪影如果你处理的是彩色图可能会发现去噪后边缘出现奇怪的彩色条纹。这是因为三个通道分别滤波后同一位置的像素值变化不一致导致颜色偏移。解决办法有两个一是转成灰度图处理二是转成HSV或Lab空间只对亮度通道滤波再转回RGB。5.2 PSNR计算结果异常低检查data_range是否设对。如果图像是uint8但data_range设成了1.0PSNR会变成负数。另外检查两幅图的尺寸和数据类型是否一致uint8和float64直接比较会出错。5.3 中值滤波后图像出现块状效应窗口开得太大时中值滤波会在平坦区域产生块状伪影因为大窗口的中位数不能很好地跟随局部变化。解决办法是改用自适应中值滤波或者先用小窗口中值滤波再配合边缘检测做后处理。5.4 高斯噪声去噪后仍有残留噪声高斯滤波对高频噪声的抑制有限。如果噪声方差较大可以尝试增大σ但边缘会变模糊。更好的选择是双边滤波或非局部均值去噪它们能在去噪的同时保留边缘。OpenCV提供了cv2.bilateralFilterscikit-image提供了denoise_nl_means都可以作为进阶对比。5.5 处理速度太慢如果图像尺寸很大比如4K逐像素滤波会很慢。可以先用cv2.resize缩小图像滤波后再放大回去但这样会损失细节。另一个办法是用cv2.filter2D配合分离核把二维卷积拆成两个一维卷积速度能提升不少。问题现象可能原因解决办法彩色伪影三通道独立滤波转灰度或只处理亮度通道PSNR异常data_range设错检查图像类型uint8用255块状效应中值窗口过大改用自适应中值滤波噪声残留滤波器截止频率不够换双边滤波或非局部均值速度慢图像尺寸大缩小图像或分离卷积核最后分享一个小技巧做对比实验时把干净图、噪声图、各方法去噪图拼成一张大图标注好PSNR和SSIM一眼就能看出差异。这个拼图用np.hstack和np.vstack就能搞定比单独看每张图高效得多。这个实验后续还可以这样扩展加入小波去噪、BM3D等更高级的方法做对比或者把去噪作为预处理步骤接一个边缘检测或图像分割任务看看去噪质量对下游任务的影响。我在实际项目中就遇到过因为去噪过度导致边缘检测漏检的情况这个教训让我在后来的实验里都会同时关注去噪指标和下游任务指标。

相关新闻

LeetCode 543:二叉树直径的高效解法与JS实现

LeetCode 543:二叉树直径的高效解法与JS实现

1. 题目理解与需求分析今天要解决的是LeetCode第543题"二叉树的直径"。这是一道经典的二叉树遍历问题,题目描述很简单:给定一棵二叉树,你需要计算它的直径长度。二叉树的直径是指树中任意两个结点间最长路径的长度,这条…

2026/10/14 0:50:56 阅读更多 →
Python爬虫实战:漏洞库数据抓取与MySQL入库全流程

Python爬虫实战:漏洞库数据抓取与MySQL入库全流程

简介:这是一份面向网络安全初学者与Python爬虫实践者的综合项目源码包,围绕CNVD、CNNVD、CVE三大权威漏洞库的数据采集、存储与可视化展开,适合想打通爬虫、数据库与GUI开发链路的学习者。包内共72个文件,以py脚本、toc与spec打包…

2026/10/14 0:50:56 阅读更多 →
虚拟商城系统模块化拼装:八套模块选型与支付交付实战

虚拟商城系统模块化拼装:八套模块选型与支付交付实战

简介:友价在线虚拟商城最新版是一套面向源码交易、域名交易等虚拟商品买卖场景的综合性电商系统,适合个人站长、开发者及中小团队快速搭建可运营的交易平台。系统采用模块化设计,涵盖商品管理、订单处理、用户管理、支付接口集成与安全保障等…

2026/10/14 0:50:56 阅读更多 →

最新新闻

参数估计实战:从数学公式到工程落地的七道关卡

参数估计实战:从数学公式到工程落地的七道关卡

/* 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 1:40:10 阅读更多 →
CSS边框内圆角实现方案:解决高保真UI的像素级痛点

CSS边框内圆角实现方案:解决高保真UI的像素级痛点

/* 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 1:40:10 阅读更多 →
多媒体实验报告:图像音频视频处理与参数避坑指南

多媒体实验报告:图像音频视频处理与参数避坑指南

简介:本资源是一份面向计算机及相关专业本科生的Authorware多媒体实验报告,记录“文字的滚动效果”的完整实现过程,重点讲解显示、等待、运动、擦除等图标在动态文字展示中的组合应用。报告按实验步骤展开,包含背景与文字导入、移…

2026/10/14 1:40:10 阅读更多 →
3天从85%降到20%!这3个AI智能降重工具让我顺利通过盲审

3天从85%降到20%!这3个AI智能降重工具让我顺利通过盲审

还记得上周三凌晨两点,当我第三次收到知网AIGC检测报告时,手心都在冒汗——85%的AI相似度,40%的查重率,这意味着我的毕业论文根本达不到盲审要求。导师直接在我的初稿上批注“学术合规性存疑,建议重写”。距离最终答辩…

2026/10/14 1:40:10 阅读更多 →
QPdfium集成指南:Qt PDF渲染内核配置与踩坑实战

QPdfium集成指南:Qt PDF渲染内核配置与踩坑实战

简介:这是一份面向Qt开发者的PDF功能集成库,基于Google pdfium开源引擎封装,专为MSVC2017 64位环境编译。它可将PDF页面渲染为QImage,便于在Qt程序中解析、显示和处理PDF文档,适合需要快速为桌面应用添加PDF阅读、打印…

2026/10/14 1:40:10 阅读更多 →
DSO编译运行实战:从环境配置到参数调优全解析

DSO编译运行实战:从环境配置到参数调优全解析

/* 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 1:39:10 阅读更多 →

日新闻

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/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 18:11:17 阅读更多 →