MATLAB双目标定实战:从采集到三维重建的避坑指南

简介:这份资源面向计算机视觉初学者与需要完成课程实验的学生,提供基于MATLAB工具箱的双目标定完整实践材料,帮助解决相机内外参数求解、几何失真校正与立体视觉入门问题。压缩包共182个文件,约15.71MB,以128张jpg标定板采集图、23个mat中间数据、14个m脚本、8张png结果图为主,另含docx实验说明、pptx汇报材料与md项目说明,覆盖从图像采集到参数估计的完整链路。已有112人学习下载。资源内含分阶段代码与结果展示,涉及标定板角点检测、单目标定、双目标定、重投影误差分析等环节,配套文档对实验步骤、理论背景与注意事项有较详细说明,便于读者对照复现流程、理解坐标系转换与投影模型,并在此基础上调试自己的双目视觉项目。

1. 双目标定这件事,为什么你照着教程跑完还是对不齐

如果你手头有一个基于matlab工具箱完成双目标定实验.rar,大概率你正卡在“知道要用 MATLAB 标定,但不知道从哪一步开始、参数怎么设、结果怎么判断”的阶段。双目标定不是把两张棋盘格照片丢进工具箱点一下“Calibrate”就完事,它要解决的是两个相机之间的旋转和平移关系,以及每台相机自己的内参和畸变。做对了,后续极线校正、视差计算、三维重建才有意义;做错了,重投影误差看着不大,实际点云却是歪的。这篇笔记面向两类人:一是刚拿到标定任务、需要一套能复现流程的工程师;二是已经跑过单目标定、想搞清楚双目标定边界条件的老手。我会按“数据采集—单目标定—双目标定—验证—避坑”的顺序,把 MATLAB 工具箱里那些默认参数背后的含义讲清楚,让你拿到 .rar 之后能直接照着做,而不是反复试错。

2. 用 MATLAB 工具箱做双目标定:从采集到标定板的硬性条件

2.1 为什么双目标定不能直接套用单目结果

单目标定只关心一台相机的内参矩阵和畸变系数,它不关心相机在空间中的位置。双目标定则要把两台相机当成一个刚体系统,同时优化内参、外参和畸变。MATLAB 的stereoCameraCalibrator工具箱在底层用的是张正友标定法的扩展版本,它会把左右相机对同一块标定板的观测联合起来做非线性最小二乘。如果你把单目标定得到的内参直接固定,只算外参,重投影误差往往能降下来,但极线校正后的图像对可能仍然存在垂直视差。常见做法是:先分别做单目标定,检查每台相机的重投影误差是否在 0.5 像素以内,再把两组内参作为初值传给双目标定,让工具箱同时微调所有参数。这样既利用了单目标定的稳定性,又保留了双目标定的联合优化能力。

2.2 标定板选型与采集数量:20 对起步,30 对更稳

标定板首选黑白棋盘格,材质建议用氧化铝板或打印后贴在平整玻璃上。MATLAB 工具箱支持checkerboardPattern生成的棋盘格,方格边长常见 25mm、30mm、50mm。方格越小,角点定位精度越高,但相机视野内能拍到的方格数会减少。我一般用 30mm 方格、10×7 内角点的棋盘格,在 1.5 米到 3 米距离内采集。采集数量上,左右相机各拍 20 到 30 对图像是底线。少于 15 对,外参优化容易陷入局部极小;多于 40 对,提升不明显但计算时间线性增长。采集时让标定板覆盖画面中心、四角、上下边缘,同时改变标定板的倾斜角度,俯仰和偏航各覆盖 ±30 度。不要只在一个平面上平移,那样无法约束相机的焦距和主点。

2.3 采集时的同步与光照:别让运动模糊毁掉角点

双目标定要求左右相机在同一时刻拍摄同一块标定板。如果两个相机是独立触发的,标定板移动时会产生视差,角点检测会直接失败。常见做法是用硬件同步线连接两台相机的触发引脚,或者用软件同步方案:先让标定板静止,再分别拍摄。静止拍摄虽然效率低,但能避免同步问题。光照方面,避免强反光和阴影落在棋盘格上。反光会让角点检测出现亚像素偏移,阴影会改变局部对比度。我习惯在均匀漫射光源下拍摄,曝光时间固定在 5ms 到 10ms,增益调低,宁可画面稍暗也不要过曝。过曝的棋盘格边缘会糊成一片,detectCheckerboardPoints函数会直接返回空。

2.4 在 MATLAB 里加载图像并检测角点的最小命令

% 假设左右图像分别存放在 left 和 right 文件夹中 leftImages = imageDatastore('left/*.jpg'); rightImages = imageDatastore('right/*.jpg'); % 检测棋盘格角点,指定棋盘格内角点数量 [imagePoints, boardSize] = detectCheckerboardPoints(... leftImages.Files, rightImages.Files); % 检查检测结果:imagePoints 是 M×2×N 的数组,M 是角点数,N 是图像对数 fprintf('检测到 %d 对图像,棋盘格内角点 %d 个\n', ... size(imagePoints, 3), prod(boardSize));

这段代码做了三件事:用imageDatastore管理图像路径,避免手动拼字符串;调用detectCheckerboardPoints同时处理左右图像,返回的imagePoints第三维是图像对索引;最后打印检测到的图像对数和角点数。如果size(imagePoints,3)小于你实际拍摄的对数,说明有些图像角点检测失败,需要检查光照或棋盘格是否完整。boardSize返回的是[rows, cols],对应内角点数量,比如 10×7 的棋盘格,boardSize是[9, 6],因为内角点比方格数少一。

3. 单目标定与双目标定联合优化:参数怎么设、结果怎么看

3.1 单目标定先跑一遍:用cameraCalibrator拿到初值

在双目标定之前,先对左右相机分别做单目标定。MATLAB 的cameraCalibrator应用可以交互式操作,也可以用命令行estimateCameraParameters。我一般用命令行,方便脚本化。单目标定的输出包括内参矩阵IntrinsicMatrix、径向畸变RadialDistortion、切向畸变TangentialDistortion。内参矩阵的焦距分量fx和fy应该接近,如果相差超过 5%,说明图像传感器像元不是正方形,或者标定板角度覆盖不够。主点cx和cy应该接近图像中心,偏离超过 10% 就要怀疑镜头安装或图像裁剪。

% 单目标定:左相机 [paramsLeft, ~, errorsLeft] = estimateCameraParameters(... imagePoints(:,:,:,1), boardSize, ... 'WorldUnits', 'mm', ... 'EstimateTangentialDistortion', true); % 查看重投影误差 fprintf('左相机平均重投影误差: %.3f 像素\n', errorsLeft.MeanReprojectionError);

WorldUnits设为mm是为了让外参平移向量有物理单位。EstimateTangentialDistortion设为true会多估计两个切向畸变系数,对于广角镜头有必要,对于普通镜头可以设为false以减少过拟合。errorsLeft.MeanReprojectionError是平均重投影误差,低于 0.5 像素算合格,低于 0.3 像素算优秀。如果高于 1 像素,先别急着做双目标定,回去检查角点检测和图像质量。

3.2 双目标定:stereoCameraCalibrator的三种调用方式

MATLAB 提供三种双目标定入口:交互式应用stereoCameraCalibrator、命令行函数estimateCameraParameters配合stereoParams、以及stereoCalibrate函数。交互式应用适合快速验证,命令行适合批量处理。我常用的是estimateCameraParameters的双目版本,因为它能直接复用单目标定的初值。

% 双目标定:联合优化 [stereoParams, pairsUsed, estimationErrors] = estimateCameraParameters(... imagePoints, boardSize, ... 'WorldUnits', 'mm', ... 'EstimateTangentialDistortion', true, ... 'InitialIntrinsicMatrix', {paramsLeft.IntrinsicMatrix, paramsRight.IntrinsicMatrix}, ... 'InitialRadialDistortion', {paramsLeft.RadialDistortion, paramsRight.RadialDistortion});

InitialIntrinsicMatrix和InitialRadialDistortion是可选参数,传入单目标定结果可以加速收敛并降低陷入局部极小的概率。pairsUsed返回实际参与优化的图像对索引,如果某些图像对因为角点检测失败被剔除,这里能看到。estimationErrors包含左右相机的重投影误差和双目重投影误差。双目重投影误差是把三维点分别投影到左右图像上计算的,它比单目误差更能反映双目标定的质量。

3.3 关键参数解读:焦距、主点、旋转矩阵、平移向量

双目标定输出的stereoParams包含几个核心字段。stereoParams.CameraParameters1和CameraParameters2分别是左右相机的内参和畸变。stereoParams.RotationOfCamera2是右相机相对于左相机的旋转矩阵,stereoParams.TranslationOfCamera2是平移向量,单位由WorldUnits决定。旋转矩阵应该是正交矩阵,行列式为 1。如果行列式接近 -1,说明坐标系定义反了。平移向量的模长就是两个相机光心之间的距离,也就是基线。基线太短(小于 5cm)会导致深度分辨率差,基线太长(大于 1m)会导致左右图像重叠区域太小。常见做法是让基线在 10cm 到 30cm 之间,具体取决于拍摄距离和视场角。

参数含义合格范围异常处理
平均重投影误差左右图像角点重投影偏差< 0.5 像素检查角点检测和光照
焦距比 fx/fy像元纵横比0.95 ~ 1.05检查传感器和镜头
主点偏移主点与图像中心距离< 10% 图像尺寸检查图像裁剪
旋转矩阵行列式正交性≈ 1检查坐标系定义
基线长度两相机光心距离5cm ~ 1m调整相机间距

3.4 用极线校正验证双目标定结果

双目标定做完后,必须做极线校正验证。MATLAB 提供rectifyStereoImages函数,它根据stereoParams对左右图像做透视变换,使对应极线水平对齐。校正后,同一物点在左右图像上的 y 坐标应该相同。你可以用stereoAnaglyph生成红蓝叠加图,或者用disparity函数计算视差图。如果校正后图像出现大面积黑色区域,说明外参估计不准,或者采集时标定板没有覆盖足够视场。

% 读取一对图像并校正 I1 = imread(leftImages.Files{1}); I2 = imread(rightImages.Files{1}); [J1, J2] = rectifyStereoImages(I1, I2, stereoParams); % 生成红蓝叠加图,检查垂直对齐 figure; imshow(stereoAnaglyph(J1, J2)); title('极线校正后的红蓝叠加图');

rectifyStereoImages的输出J1和J2是校正后的图像。stereoAnaglyph把左图设为红色通道,右图设为青色通道。如果双目标定准确,同一物点的红色和青色应该重合,只留下水平方向的视差。如果出现垂直方向的彩色边缘,说明旋转矩阵估计有偏差,需要重新采集或调整优化参数。

4. 双目标定避坑:5 个让重投影误差骗过你的常见问题

4.1 现象:重投影误差很低,但极线校正后垂直视差明显

原因:双目标定优化时只最小化重投影误差,如果标定板只出现在图像中心区域,外参在边缘区域的约束不足,优化器会用内参补偿外参误差。解决:采集时让标定板覆盖图像四角和边缘,至少 30% 的图像对包含边缘角点。如果已经采集完,可以在estimateCameraParameters中设置'EstimateTangentialDistortion', false,减少内参自由度,迫使外参承担更多约束。

4.2 现象:角点检测返回空,或检测到的角点数少于棋盘格内角点数

原因:棋盘格反光、阴影、运动模糊,或者棋盘格尺寸设置错误。detectCheckerboardPoints默认假设棋盘格是白底黑格,如果打印时黑白反了,检测会失败。解决:用imshow检查图像,确认棋盘格完整且对比度足够。如果棋盘格是黑底白格,在函数中设置'InvertCheckerboard'为true。运动模糊可以通过缩短曝光时间或使用闪光灯解决。

4.3 现象:双目标定报错“Not enough valid image pairs”

原因:左右图像对中,至少有一张角点检测失败,或者左右图像对的棋盘格角点顺序不一致。MATLAB 要求左右图像的棋盘格方向一致,如果左相机拍到的是正方向,右相机拍到的是旋转 180 度,角点匹配会错乱。解决:采集时保持两个相机同向,不要一个正装一个倒装。如果必须倒装,在检测前用imrotate把图像旋转回正方向。另外,检查imagePoints的第三维是否等于图像对数量,如果少了,用pairsUsed找出被剔除的对。

4.4 现象:基线长度估计为负数或异常大

原因:旋转矩阵或平移向量的符号约定错误。MATLAB 的TranslationOfCamera2是右相机光心在左相机坐标系中的位置,如果两个相机前后颠倒,平移向量会反向。解决:确认CameraParameters1对应左相机,CameraParameters2对应右相机。如果搞反了,交换imagePoints的左右顺序重新标定。基线异常大通常是因为标定板在左右图像中的尺度差异过大,检查两个相机的焦距是否一致。

4.5 现象:校正后图像有效区域很小,大量黑色边框

原因:双目标定估计的旋转角过大,或者相机实际安装存在较大夹角。rectifyStereoImages会做透视变换,如果夹角超过 15 度,变换后的有效像素会急剧减少。解决:调整相机安装,让光轴尽量平行,夹角控制在 5 度以内。如果无法调整,可以在校正时设置'OutputView', 'full',但这样会引入更多黑色区域。更好的做法是重新标定,并在优化时固定内参,只优化外参。

5. 从标定结果到三维重建:一个可复现的验证技巧

双目标定的最终目的是三维重建。我习惯用triangulate函数验证标定质量:在左右图像上手动选取同一物点的像素坐标,用stereoParams三角化出三维坐标,再反投影回图像,看误差是否在 1 像素以内。这个技巧比看重投影误差更直观,因为它直接检验了极线几何的一致性。

% 手动选取一对对应点(示例坐标,实际需根据图像调整) pointsLeft = [320, 240]; % 左图像素坐标 (x, y) pointsRight = [280, 240]; % 右图像素坐标 (x, y) % 三角化 worldPoint = triangulate(pointsLeft, pointsRight, stereoParams); % 反投影验证 projectedLeft = worldToImage(stereoParams.CameraParameters1, ... eye(3), zeros(1,3), worldPoint); projectedRight = worldToImage(stereoParams.CameraParameters2, ... stereoParams.RotationOfCamera2, stereoParams.TranslationOfCamera2, worldPoint); fprintf('左反投影误差: %.3f 像素\n', norm(projectedLeft - pointsLeft)); fprintf('右反投影误差: %.3f 像素\n', norm(projectedRight - pointsRight));

triangulate接受左右像素坐标和stereoParams,返回三维点。worldToImage把三维点投影回图像平面,需要传入相机的旋转矩阵和平移向量。左相机默认在原点,旋转为单位矩阵,平移为零向量。右相机用stereoParams中的外参。如果反投影误差超过 1 像素,说明双目标定结果在局部区域不够准确,需要增加该区域的标定板图像对。这个验证方法的好处是你可以针对特定区域反复检查,而不是只看全局平均误差。

我自己的习惯是:每次双目标定完成后,先看平均重投影误差,再看极线校正的红蓝叠加图,最后用三角化反投影验证三个不同深度的点。三步都过了,才把stereoParams保存下来用于后续任务。标定这件事没有后悔药,采集阶段偷的懒,后期会以点云漂移的形式还回来。希望帮到你。

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

相关新闻

MCAL功能安全设计:从寄存器级防护到ASIL-D落地实践

MCAL功能安全设计:从寄存器级防护到ASIL-D落地实践

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

2026/10/20 13:52:16 阅读更多 →
宠物识别系统实战:基于Python与TensorFlow的图像分类毕设指南

宠物识别系统实战:基于Python与TensorFlow的图像分类毕设指南

每年毕设季&#xff0c;总会有人问我这样的问题&#xff1a;想做一个跟“人工智能”沾边的项目&#xff0c;但又怕难度太高hold不住。这里我直接给一个结论——如果你需要在2026年完成计算机方向的毕业设计&#xff0c;又希望项目既有技术含量、又能落地演示&#xff0c;“宠物…

2026/10/20 13:52:16 阅读更多 →
基于U-Net的道路裂缝检测:毕设级语义分割全流程实战

基于U-Net的道路裂缝检测:毕设级语义分割全流程实战

1. 项目概述与核心思路拆解1.1 为什么说这个选题是毕设的“安全牌”又带点含金量道路裂缝检测这个方向&#xff0c;在计算机视觉和深度学习的毕设题目里属于“进可攻退可守”的典型选择。进可攻——你可以往实例分割、多任务学习、轻量化部署方向延伸&#xff0c;做成一个能跑在…

2026/10/20 13:51:15 阅读更多 →

最新新闻

OSG项目GIF插件编译失败怎么办?giflib 4.1.6 64位静态库接入与避坑指南

OSG项目GIF插件编译失败怎么办?giflib 4.1.6 64位静态库接入与避坑指南

简介&#xff1a;面向需要在 OpenSceneGraph 3.2.1 中启用 GIF 插件编译的开发者&#xff0c;这份资源提供 giflib 4.1.6 的 64 位静态链接库及配套头文件&#xff0c;可解决 osg 的 Plugin gif 链接依赖问题&#xff0c;省去自行构建 giflib 的环节。压缩包共 3 个文件&#x…

2026/10/20 14:34:30 阅读更多 →
微电网两阶段鲁棒优化调度:CCG算法应对风光不确定性

微电网两阶段鲁棒优化调度:CCG算法应对风光不确定性

1. 微电网优化调度为什么要考虑不确定性 做园区级微电网调度的人应该都有过这种体会&#xff1a;今天光伏预测曲线明明很漂亮&#xff0c;上午十点突然飘来一片云&#xff0c;出力直接砍半&#xff1b;或者风电场那边预报微风&#xff0c;结果下午风速爬升&#xff0c;风机满发…

2026/10/20 14:34:30 阅读更多 →
AI气象数据融合实现流感提前两周预警的完整实践

AI气象数据融合实现流感提前两周预警的完整实践

流感预警这件事&#xff0c;难点从来不在“发现”&#xff0c;而在“提前”。我在复现某区域流感预警Demo系统时&#xff0c;核心目标只有一个&#xff1a;在流感监测曲线还没来得及明显抬头之前&#xff0c;用AI气象数据融合的方式提前两周给出区域风险判断。很多人问我&#…

2026/10/20 14:34:30 阅读更多 →
深入理解TensorFlow 2.x自动求导:GradientTape原理、实践与排坑指南

深入理解TensorFlow 2.x自动求导:GradientTape原理、实践与排坑指南

很多人从 TensorFlow 1.x 迁移过来&#xff0c;第一件事就是找tf.gradients&#xff0c;然后一脸懵&#xff1a;没了&#xff0c;取而代之的是一个叫tf.GradientTape的东西。更懵的是&#xff0c;新写的训练代码里看不到"计算图"这种概念&#xff0c;梯度就像变魔术一…

2026/10/20 14:34:30 阅读更多 →
用Qt+Windows API打造远程控制软件:架构、协议与实战

用Qt+Windows API打造远程控制软件:架构、协议与实战

简介&#xff1a;基于Qt框架实现的Windows远程控制软件资源&#xff0c;包含服务器端&#xff08;被控端&#xff09;与客户端&#xff08;主控端&#xff09;两部分&#xff0c;附完整源码与可直接运行的编译程序。服务器端以进程形式后台运行&#xff0c;客户端为图形界面程序…

2026/10/20 14:34:30 阅读更多 →
OpenCV 2.4.13 安装指北:源码编译、环境配置与五条血泪排错记录

OpenCV 2.4.13 安装指北:源码编译、环境配置与五条血泪排错记录

简介&#xff1a;面向计算机视觉与图像处理开发者&#xff0c;该zip压缩包提供的是OpenCV 2.4.13.3经典旧版安装资源&#xff0c;适合需要维护旧项目、复现历史算法或学习传统视觉知识的用户&#xff1b;相比高版本OpenCV&#xff0c;2.4.x在函数命名与调用方式上存在差异&…

2026/10/20 14:33:27 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →