喷码OCR缺陷检测实战:从数据标注到模型训练与VisualDL分析
简介面向工业自动化的缺陷检测实战项目专注于OCR喷码缺陷检测适合机器视觉入门者及有经验的工程师。资源围绕喷码字符识别与缺陷判定涵盖数据收集、图像预处理、特征提取、模型训练到检测算法实现的完整流程并提供可运行的项目源码与原理讲解。包体共207个文件以图片样本jpg/png、标注数据csv/json、模型权重pdparams、训练配置yml及Python脚本py为主体另有字体文件ttf与训练日志等压缩包约156.81MB结构紧凑便于查阅。目前已有114人学习下载。通过实际项目可掌握OCR在喷码缺陷检测中的应用理解图像去噪、对比度增强、特征提取与模板比对等关键环节源码注释丰富便于二次开发与按需调优。适合质量控制、生产管理与工业自动化人员参考。1. 喷码质检不是玄学OCR 缺陷检测项目的完整落地链路产线上的喷码日期、批号一旦出问题字符断点、拖尾、漏喷在自然光下肉眼很难一眼抓住打印头轻微堵塞或者油墨浓度波动就会让整批产品的追溯信息直接作废。这个“缺陷检测-OCR喷码缺陷检测项目实战”压缩包就是把这套肉眼判断换成算法系统的完整资源从图片采集到一图一 CSV 的标注组织再到预处理、模型训练与 VisualDL 日志分析源码和原理流程教程是配套给的。我把它完整跑过一遍里面最值得研究的是 mobilenetv3_small 骨干在轻量分类任务下的训练输出以及 CSV 标注与图片的对应方式。适合要落地喷码质检的工程师也适合刚进入机器视觉领域的新手先把链路跑通再谈调优比死记算法概念有用得多。2. 检测链路先立起来从一图一 CSV 到模型训练的完整流程2.1 项目文件结构与运行前准备解压后你会发现目录结构和普通目标检测项目不太一样没有大量 jpg 平铺在一起而是出现了visualdl-scalar-output_mobilenetv3_small这样一段长目录名外加vdl_log和一堆picture_XX.csv。第一反应别急着翻模型代码先弄清楚这些文件之间的关系。项目内主要目录和文件的用途大致如下文件 / 目录作用visualdl-scalar-output_mobilenetv3_small训练过程的标量输出目录记录 loss、准确率等随 step 变化的曲线数据vdl_logVisualDL 日志目录浏览器可视化读取的原始数据picture_XX.csv每张喷码图片对应的标注信息表picture_XX.jpg产线采集的原始喷码图像与同名 CSV 对应原理流程教程文档从采集到判定的完整链路说明运行前先确认基础依赖。这个项目跑在 Paddle 生态框架上配套需要 OpenCV 做图像处理加上 VisualDL 做日志可视化。我一般会先建一个干净的 Python 环境然后逐个检查依赖是否就位。python -c import paddle; print(paddle.__version__) python -c import cv2; print(cv2.__version__) python -c import visualdl; print(visualdl.__version__)这段检查的意义在于把环境问题和算法问题先隔离开。实际踩坑中很多“模型跑不起来”的报错最后都指向 paddle 版本和 python 版本不匹配而不是代码本身。Paddle 2.x 系列在这个项目里都能跑通但如果你本地装的是 1.x 老版本建议先升级否则后面加载模型权重会直接报结构不匹配。2.2 CSV 里装的是什么这个项目的标注方式值得先说清楚它不是 COCO 或 VOC 那种把所有标注汇总到一个大 json/xml 里的做法而是每张图对应一个 CSV 文件。打开picture_19.csv内容大概是这样的filename,x1,y1,x2,y2,label picture_19.jpg,12,34,128,96,spray_code picture_19.jpg,150,45,290,92,spray_code每一行代表一个检测区域的坐标框和类别名。这里坐标我确认过是绝对像素值不是归一化数值所以后面做 ROI 裁剪时可以直接喂给 OpenCV 的切片操作不需要再做换算。如果后续你要把数据迁移到 YOLO 格式需要额外做一步归一化这个后面会提到。这种一图一 CSV 的格式有一个明显好处单张图出问题时不会污染全局标注文件排查数据错位时直接对比同名文件即可。缺点也很明显文件数量多做数据划分时要小心不能按 CSV 个数随机切分要按图片名把人队和缺陷样本比例控制好。2.3 五个核心环节采集、预处理、特征提取、训练、判定把整个项目的检测流程拆开看它就是一条标准工业视觉链路。图像采集环节项目没有依赖高成本工业相机普通 USB 摄像头加固定光源就能提供可用数据这也符合摘要里强调的“不依赖昂贵硬件设备”。预处理环节是重头戏去噪、对比度增强、ROI 提取都在这里完成直接影响后续模型的输入质量。特征提取并不是传统意义上手工设计 HOG 或 SIFT 特征而是交给 MobileNetV3 Small 这类轻量卷积网络自动完成。模型训练则依赖大量标注样本通过交叉熵损失不断调整权重。最后的缺陷判定是把模型输出的概率分数和预设阈值做比较决定这一件产品是否放行。从项目源码的组织方式看这五个环节分别对应不同的脚本模块预处理和训练是两个独立目录互不干扰。这种拆法对调试很友好预处理出了问题不需要动训练代码单独重跑预处理脚本就行。3. 把图片整理成可训练样本预处理与数据校验3.1 先跑一遍数据完整性检查很多新手拿到项目后直接开训结果训练到一半发现某张图找不到对应 CSV或者某个 CSV 指向的图片根本不存在。这个项目里因为是一图一 CSV 的格式数据完整性问题更容易发生。我拿到数据后的第一个动作永远是跑一段完整性校验脚本。import os img_dir images csv_dir csvs # 提取文件名主干去掉扩展名 imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} csvs {os.path.splitext(f)[0] for f in os.listdir(csv_dir) if f.endswith(.csv)} missing_csv imgs - csvs missing_img csvs - imgs print(缺少 CSV 的图片, missing_csv if missing_csv else 无) print(缺少图片的 CSV, missing_img if missing_img else 无)这段脚本的逻辑是先分别读取images和csvs两个目录下的文件名去掉扩展名后放进两个集合然后做差集运算。凡是出现在图片集合但不在 CSV 集合里的说明漏标了反过来则说明标注文件是孤儿数据。参数上要注意endswith(.jpg)只匹配了 jpg 格式如果项目里混有 png 图需要把扩展名条件加上。我一般会直接改成{os.path.splitext(f)[0] for f in os.listdir(img_dir) if os.path.isfile(os.path.join(img_dir, f))}对所有文件一视同仁地检查。3.2 去噪与对比度增强预处理脚本的关键参数预处理的质量直接决定模型能学到什么。喷码图像最典型的问题是油墨不均导致对比度差以及拍摄时带入的椒盐噪声。项目中预处理部分的核心操作可以浓缩成下面这段流程。import cv2 def preprocess(img_path): # 灰度读取减少颜色通道对字符特征的干扰 img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) # CLAHE 自适应对比度增强clipLimit 控制抑制噪声的程度 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) img clahe.apply(img) # 中值滤波去噪ksize3 保留笔画边缘 img cv2.medianBlur(img, 3) # Otsu 大津法二值化自动计算分割阈值 _, binary cv2.threshold(img, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) return binary这里clipLimit2.0是 CLAHE 的对比度限制参数值越大对比度增强越强但噪声也会被放大。喷码字符本身较细我建议在 1.5 到 3.0 之间尝试太大容易把字符内部噪点也提亮给后续训练制造假缺陷。tileGridSize(8, 8)表示把图像分成 8×8 的小块分别做直方图均衡这个值跟图像分辨率有关如果是 1920×1080 的大图建议改成(16, 16)否则区块过少局部光照不均问题压不住。中值滤波的ksize3是个经验值既能去掉孤立噪声点又不至于把喷码字符的断点抹平。最后用 Otsu 自动阈值代替固定阈值是因为产线光照不是恒定不变的固定阈值在这种场景下很难一次调好Otsu 的自动计算能力能省去频繁调参的麻烦。3.3 数据增强要同步修改 CSV喷码检测里缺陷样本往往比良品少不做增强的话模型很容易把所有图片都识别成良品也就是常说的“学懒了”。常见的做法是对图片做轻微旋转、平移、加高斯噪声和模拟油墨浓度变化。但这里有个大坑如果增强只作用于图片而不动 CSVROI 坐标就全部错位了。正确的处理逻辑是对图片做仿射变换时必须把变换矩阵套用到 CSV 里的坐标点上。比如旋转 5 度图片旋转了原来的(x1, y1)就不再指向字符区域。我在这个项目上踩过这个坑增强后训练数据里一大半 ROI 切出来是空白背景模型精度直接崩到百分之六十多。从那以后我只要做增强就强制同步用同一套仿射矩阵更新坐标框。4. 训练阶段MobileNetV3 Small 做骨干VisualDL 看曲线4.1 为什么是 MobileNetV3 Small喷码缺陷检测本质上是一个轻量级图像分类问题判断某个 ROI 区域里的喷码是否合格。这种任务不需要 ResNet50 级别的超大感受野反而更看重单次推理耗时和部署体积。MobileNetV3 Small 引入深度可分离卷积和轻量注意力机制在保持精度的同时把参数量压得很低很适合产线工位机这类算力有限的场景。模型参数量量级单张推理耗时CPU 参考适用场景MobileNetV3 Small约 2-3M低产线实时检测、嵌入式设备MobileNetV3 Large约 5-6M中需要更高精度的离线检测ResNet18约 11M较高缺陷类型复杂、样本量大的场合项目里选择 MobileNetV3 Small 作为骨干和摘要里“快速准确检测、减少误报漏报”的目标是对应的。实际跑下来它对喷码这种结构相对固定的字符识别足够用且不会把训练时间拖到不可接受。如果你后续要检测的缺陷类型变多比如同时要看划痕、脏污、印刷偏移可以换成 Large 版本但推理耗时也会上浮。4.2 训练配置与关键参数项目源码中提供了可直接运行的训练脚本核心配置集中在 yaml 文件里。我按照项目里自带的配置整理了一份最简可用的训练参数Global: device: gpu seed: 42 Arch: name: MobileNetV3_small class_num: 2 Train: batch_size: 32 learning_rate: 0.001 epochs: 60 num_workers: 4 Loss: name: CrossEntropyLossclass_num: 2指的是良品和缺陷两个类别。如果你的产线还有“可疑待检”这种中间状态就得改成 3 并准备对应数量的标注数据。batch_size32在普通 8G 显存下够用显存不够就降到 16同时把learning_rate相应调低一些。epochs60对这个数据规模是合理的太少学不到位太多容易过拟合后面看 VisualDL 曲线再决定是否提前停止。学习率这里我习惯用0.001做初始值配合余弦退火调度器前半段快速收敛后半段精细微调。如果发现 loss 在训练后期怎么都降不下去优先检查预处理输出而不是盲目调学习率。4.3 VisualDL 日志解读别只看 loss 一条曲线训练结束后项目里会生成visualdl-scalar-output_mobilenetv3_small_vdl_log--Metrics_Training(Step)_ loss.csv这样的文件。这个文件名又长又拗口但里面信息很关键它记录了每个 step 的训练 loss 值。启动 VisualDL 查看日志的命令很简单visualdl --logdir vdl_log --port 8040启动后在浏览器访问http://localhost:8040就能看到 loss 和准确率随 step 变化的两条曲线。我通常关注三个东西loss 是否在预期 epoch 内正常下降、是否有明显尖刺、训练集和验证集曲线是否开始分叉。如果 loss 曲线前 10 个 epoch 下降很快后面趋于平稳这是正常现象。如果出现反复震荡说明学习率偏高砍半再训。如果训练 loss 一直降但验证准确率停滞说明过拟合了这时候优先关掉数据增强或加大 dropout而不是再堆训练轮数。5. 避坑清单喷码检测项目里最常见的五处翻车点5.1 字符断裂被误判为缺陷现象良品喷码反复被判定为缺陷误报率居高不下。原因ROI 裁剪尺寸太小加上预处理中二值化阈值过重字符笔画被切断成两截模型误以为出现了断点缺陷。解决把 ROI 最小尺寸限制在 32×32 以上二值化前先做一次轻度中值滤波。增强阶段不要用腐蚀类操作喷码本身笔画细腐蚀等于人为制造缺陷。5.2 CSV 坐标与图片内容对不上现象训练时切出来的 ROI 图像大量是空白背景或错位前景。原因某个环节对图片做了缩放或旋转但 CSV 里的坐标还是原始像素值。解决预处理脚本里对图片做几何变换时同步对坐标做同样变换。检查方法是把预测框和原图画在一起输出到调试目录人眼确认位置是否吻合。5.3 VisualDL 启动后页面空白现象浏览器打开 localhost:8040 后没有曲线。原因--logdir指向的目录层级不对VisualDL 没有在指定目录下找到日志文件。解决确认vdl_log下是否有完整的日志文件而不是再套一层子目录。有时程序会把日志写到vdl_log/train这类嵌套路径里这时候--logdir要指向vdl_log本身让它递归查找子目录。5.4 光照变化导致检测结果忽好忽坏现象白天调试准确率正常晚上换了一批光线环境后误检明显增多。原因预处理里的对比度增强参数是固定写死的无法适应光照突变。解决用 CLAHE 代替全局直方图均衡这类自适应方法在光照不均场景下更稳。同时固定产线打光角度和亮度减少变量进入模型。5.5 分类阈值一刀切现象漏检率一直压在很低但良品被误杀的数量也很大。原因所有缺陷类型共用同一个概率阈值不同类型缺陷的置信度分布差异被忽略了。解决按缺陷类型分别统计模型输出分数分布。断点缺陷分数普遍偏高可以放心用 0.9 阈值油墨不均类缺陷分数接近 0.7就单独设低一些。这类统计可以让训练阶段顺手输出一份混淆矩阵不花多少时间。6. 推理与验收把训练好的模型装回产线前做三件事模型训练完不等于项目结束推理阶段的鲁棒性才是产线关心的重点。我一般会在部署前再跑一个简单的推理脚本验证模型输出是否符合预期。def infer(img_path): binary preprocess(img_path) # 按 CSV 坐标裁剪 ROI送入模型得到各类别分数 roi binary[y1:y2, x1:x2] score model.predict(roi) is_defect score[defect] 0.9 return is_defect, score这里的0.9只是一个初始值真正落地之前需要花时间做阈值校准。我的做法是准备一百张良品和一百张缺陷图先把阈值设高记录漏检率再逐步调低记录误报率。两条曲线交叉的位置附近就是当前工况下的较优阈值。别追求单一阈值解决所有问题产线环境复杂阈值留一点余量比卡到极限更安全。验收节奏上我习惯按三阶段走。第一阶段用历史标注数据回放确认新模型不比旧方案差。第二阶段上产线旁路运行三天模型只输出结果不下发拦截指令对比人工复检结果。第三阶段才真正接入拦截逻辑。这个节奏看似慢但能避免模型在某个没覆盖到的光照条件下突然误杀大量良品。整套流程跑下来我最大的感受是喷码缺陷检测看起来是个图像问题实际上大部分时间都在和数据较劲。从那以后我每次做这类项目都会强制先走一遍数据完整性检查再谈训练和调参顺序乱了我宁愿重来。这个项目给我的参考价值不仅是那份源码更是整条链路的组织方式希望帮到你。本文还有配套的精品资源点击获取

相关新闻

恶意代码检测图像化平台:字节转灰度图与CNN分类

恶意代码检测图像化平台:字节转灰度图与CNN分类

简介:这是一个面向计算机相关专业本科生的毕业设计完整项目,主题为恶意代码检测分类平台,融合程序设计、算法模型与Web管理端展示。资源定位清晰,适合需要毕设参考、课设拓展或对恶意样本分类感兴趣的开发者。包内共157个文件&…

2026/10/9 12:33:59 阅读更多 →
用AI大模型教孩子管理压岁钱:从记账到财商启蒙的完整实践

用AI大模型教孩子管理压岁钱:从记账到财商启蒙的完整实践

春节后的饭桌上,孩子把红包拆得干干净净,数完突然塞到我手里:“爸,还是你帮我存着吧。”我当时说不清是高兴还是难受。高兴的是他信任我,难受的是我知道“帮你存着”这四个字已经让我们家红包含糊了六年:第…

2026/10/9 12:33:59 阅读更多 →
ffdec-18.5.0.zip 实战指南:Flash 反编译、SWF 资源提取与批量导出

ffdec-18.5.0.zip 实战指南:Flash 反编译、SWF 资源提取与批量导出

简介:FFDec 18.5.0 压缩包面向需要维护旧 Flash 项目、研究 SWF 结构或执行逆向工程的开发者,提供反编译、资源提取、ActionScript 查看与编辑、元数据分析等核心能力。包内共 62 个文件,压缩后大小约 13.51MB,其中 ffdec.bat、ff…

2026/10/9 12:33:59 阅读更多 →

最新新闻

Agent-Reach:智能体触达外部系统的落地实践与架构解析

Agent-Reach:智能体触达外部系统的落地实践与架构解析

最近连续被同一个问题折腾了好几天:明明智能体(Agent)本身的推理能力已经调得不错了,可业务方跟我说“你的Agent根本没法用”,因为它连公司内部的订单系统都查不到,更别说替用户完成预约、查询库存这类实际…

2026/10/9 14:31:57 阅读更多 →
校园驿站管理系统毕设实战:Spring Boot+MyBatis-Plus从设计到部署

校园驿站管理系统毕设实战:Spring Boot+MyBatis-Plus从设计到部署

简介:这份资源是面向高校计算机专业学生的 Java 毕业设计与课程设计完整方案,基于 SSM 框架开发的校园驿站管理系统,适合需要完成期末大作业、毕业设计或想通过实战掌握 Java Web 开发的学习者。系统围绕快递驿站业务展开,管理员可…

2026/10/9 14:31:57 阅读更多 →
Java电商毕设实战:库存并发与退款一致性解决方案

Java电商毕设实战:库存并发与退款一致性解决方案

简介:本资源是一份面向计算机专业本科生与Java初学者的毕业设计类实践文档,聚焦B/S架构下零食电商系统的全流程设计与实现。文档完整覆盖绪论、系统架构、数据库设计、各功能模块(用户管理、商品展示、购物车、订单处理、定制服务、促销管理&…

2026/10/9 14:31:57 阅读更多 →
Java+微信小程序+SSM小区管理系统毕设实战指南

Java+微信小程序+SSM小区管理系统毕设实战指南

简介:本资源是一套完整的毕业设计实战项目,面向计算机专业本科生及Java全栈初学者,聚焦微信小程序与SSM后端协同开发的小区管理场景。系统采用前后端分离架构:后端基于SSM(SpringSpringMVCMyBatis)框架&…

2026/10/9 14:31:57 阅读更多 →
Text-to-CAD实战解析:从自然语言到三维模型的生成原理与避坑指南

Text-to-CAD实战解析:从自然语言到三维模型的生成原理与避坑指南

1. 非标设计的一天,从一段描述开始做机械设计或者产品结构设计的人,应该都有过这种经历:脑子里想得清清楚楚的一个零件——比如一块带折弯的钣金支架、四个安装孔、一个加强筋——打开CAD软件之后,却要先选基准面、画草图、标约束、拉伸、切除、倒角,一顿操作下来十分钟过去了,…

2026/10/9 14:31:57 阅读更多 →
PIC18F87J10与PCA9422协同实现电池供电系统的动态电源管理

PIC18F87J10与PCA9422协同实现电池供电系统的动态电源管理

做电池供电的便携设备时,电源管理往往比业务逻辑更让人头疼。之前我把 PCA9422 和 PIC18F87J10 搭在一起做了一套完整的电源管理方案,从硬件设计、I2C 配置、动态调压到低功耗切换都实际跑了一遍。这篇文章就是这次实践的整体记录,核心思路是…

2026/10/9 14:30:56 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26: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/9 6:17:20 阅读更多 →