OpenCV人脸识别实战:Haar级联检测与LBPH身份识别完整教程
1. 整体设计与方案选型先说结论用 OpenCV 和 Python 做一套能识别具体身份的人脸识别系统不一定要上深度学习框架。我这次实战项目选的是 OpenCV 自带的 Haar 级联做人脸检测再用 LBPH 识别器做身份判断全程在 CPU 上跑不需要 GPU不需要训练大规模网络连环境到效果跑通不到半天时间。很多刚入门的朋友容易把“人脸检测”和“人脸识别”当成一回事实际上这是两个完全不同的环节。人脸检测解决的是“人脸在哪”输出是一个矩形框人脸识别解决的是“这个人是谁”输出是一个标签和置信度。整个项目的链路其实很清楚摄像头逐帧取图先用检测器框出人脸区域裁剪灰度图再送进 LBPH 模型预测。这个流程看起来简单但里面藏着不少坑比如 opencv-python 安装版本不对导致cv2.face无法导入比如置信度阈值设错导致陌生人被认成熟人。下面我会把从环境配置到实时识别的每一步都过一遍。这篇文章适合谁看适合刚接触计算机视觉、想快速实现一个人脸识别 Demo 的开发者也适合把 OpenCV 作为毕设或个人项目基础的同学。看完你至少能自己采集人脸数据、训练模型、连摄像头实时识别并且知道出了问题该从哪里排查。1.1 先分清“检测”和“识别”人脸检测和人脸识别是前后依赖的关系但不要因为名字里都带“人脸”就把它们混在一起。人脸检测要做的是定位。给定一张图片检测器会扫描图像中的不同位置和不同尺度判断哪些区域长得像人脸最后返回一组坐标(x, y, w, h)。在 OpenCV 里典型实现是 Haar 级联分类器速度很快但容易受角度、遮挡和复杂背景影响。人脸识别要做的是分类。获取到人脸区域后系统需要把这小块图像和已知的人脸库进行比较找到最接近的样本并返回标签。如果拿生活中的场景类比人脸检测像是人在屋里找“谁的脸露出来了”人脸识别则是看清这张脸对应的是哪个熟人。所以真正的实战流程是检测到人脸 → 裁剪人脸区域 → 统一尺寸和颜色空间 → 送入识别器 → 得到标签和置信度。很多人直接在整张图上做识别既慢又不准就是这个环节没拆开。1.2 为什么不直接上深度学习现在效果最好的人脸识别方案基本都靠深度学习比如各种基于卷积神经网络的检测器和特征提取模型。那我为什么还用传统视觉方案原因是场景决定技术选型。我这个项目是一个本地离线运行的身份区分 Demo使用人数就几个人摄像头固定拍摄光线基本稳定对识别精度要求没有到“金融级”那么高。在这种情况下Haar 级联加载一个几百 KB 的 XML 文件就能检测人脸LBPH 模型训练几十秒钟就能跑完识别时不需要显卡代码量也比深度学习方案少一个量级。深度学习方案的优势是泛化能力强比如侧脸、遮挡、复杂光照下依然能工作但代价是训练数据量大、模型体积大、部署麻烦。如果只是做一个室内固定场景的人脸识别传统算法已经足够。更重要的是把传统方案跑通以后你对“检测、对齐、特征提取、分类、置信度”这一整套流程会有非常直观的理解再切到深度学习方案时会轻松很多。1.3 OpenCV 内置的三种识别器对比OpenCV 的face模块里提供了三个经典的人脸识别器分别是 Eigenfaces、Fisherfaces 和 LBPH。初学者最好先搞清楚它们的区别不要随便选个方法就开跑。识别器核心原理优点缺陷EigenfacesPCA 主成分分析降维实现简单训练速度快对光照非常敏感需要人脸对齐FisherfacesLDA 线性判别分析比 Eigenfaces 更关注类别间差异样本量小时容易过拟合同样受光照影响LBPH局部二值模式直方图对光照有一定鲁棒性实现稳定对大幅姿态变化不太友好需要样本多样性我在这个项目里选择 LBPH。一个很现实的原因是 LBPH 不需要像 Eigenfaces 和 Fisherfaces 那样对所有训练图像做严格的人脸对齐和归一化采集数据时稍微歪一点头也能训练出结果。另一个原因是 LBPH 的置信度输出直观方便我写阈值判断。LBPH 的基本思想是把图像分成若干小块在每个小块内计算每个像素的局部二值模式再统计成直方图最后把所有小块的直方图拼接起来作为人脸特征描述。识别的时候计算当前人脸的特征直方图和已有人脸库中每个特征的相似度返回距离最近的标签。2. 环境准备与依赖安装这个项目的依赖其实很少核心就是 Python 和 OpenCV。如果你之前已经装过很多 Python 包建议单独建一个虚拟环境避免把系统环境搞乱。2.1 创建虚拟环境并安装依赖我用的是 Python 3.10 版本OpenCV 版本只要在 4.x 以上都没问题。先建虚拟环境python -m venv face_env source face_env/bin/activateWindows 终端下激活命令是face_env\Scripts\activate。然后安装依赖pip install opencv-contrib-python numpy这里有个非常容易踩的坑如果你只安装opencv-python那么导入cv2后执行cv2.face.LBPHFaceRecognizer_create()会直接报AttributeError: module cv2.face has no attribute LBPHFaceRecognizer_create。因为face模块属于 OpenCV 的 contrib 扩展包只有opencv-contrib-python才自带。还有一点要注意不要同时安装opencv-python和opencv-contrib-python。两者都会向cv2写入同名文件很容易互相覆盖出现一些莫名其妙的问题。常规做法就是只装一个opencv-contrib-python基础模块和扩展模块都齐了。2.2 Haar 级联模型文件从哪里来OpenCV 自带了训练好的 Haar 级联人脸检测模型不需要自己去训练检测器。文件名是haarcascade_frontalface_default.xml在安装 OpenCV 后可以通过cv2.data.haarcascades这个路径访问import cv2 face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml )如果你需要更强的检测效果还可以尝试haarcascade_frontalface_alt2.xml或haarcascade_frontalface_alt_tree.xml。这几个模型的检测倾向略有不同default通常是最通用的选择alt2对正脸的检测率稍好但误检率也可能略高。我习惯先用default跑通流程再根据实际效果换别的。加载模型后真正干活的是detectMultiScale方法。最影响检测结果的三个参数是scaleFactor每次缩放图像的比例默认我常用1.1。这个值越小检测越精细但速度越慢。minNeighbors每个候选区域需要保留的邻近检测框数量值越大漏检越多值越小误检越多默认取5。minSize最小检测框尺寸。摄像头上离得远的小人脸不值得检测设置(80, 80)可以过滤掉很多误检。3. 数据集准备与训练代码实现在训练识别模型之前首先要准备一个人的多张人脸样本。样本质量直接决定识别效果这一点很多人容易忽略。如果你的训练数据和实际运行时光线、角度差异过大后面识别率会非常难看。3.1 用摄像头自动采集人脸样本我采用的方式是写一个采集脚本从摄像头逐帧读取画面先做人脸检测再把检测到的人脸裁剪成统一尺寸的灰度图保存下来。每个对象准备 150 到 200 张足够 LBPH 训练出稳定模型。目录结构建议这样组织dataset/ person_0/ 0000.jpg 0001.jpg ... person_1/ 0000.jpg 0001.jpg ...目录名就是标签名训练时程序会自动把目录名映射成数字标签。采集脚本如下import cv2 import os face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) person_name person_0 save_dir os.path.join(dataset, person_name) os.makedirs(save_dir, exist_okTrue) cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头不可用) exit(1) count 0 while count 200: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.2, minNeighbors5, minSize(80, 80) ) for (x, y, w, h) in faces: face_roi gray[y:y h, x:x w] if face_roi.size 0: continue face_roi cv2.resize(face_roi, (200, 200)) cv2.imwrite(os.path.join(save_dir, f{count:04d}.jpg), face_roi) count 1 cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.imshow(collect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()采集时我建议做几个动作正面对着摄像头头部稍微左转、右转、低头、抬头也可以慢慢前后移动。这样模型能学到同一个人的多个姿态比一直保持同一个姿势拍 200 张要实用得多。3.2 训练 LBPH 识别模型数据采集完成后训练脚本会自动扫描dataset下的每个子目录把目录名映射成数字标签。训练代码很简单但有两个关键点图像必须统一尺寸标签必须是整数数组。import cv2 import os import json import numpy as np DATA_DIR dataset faces [] labels [] label_map {} current_label 0 for person_name in sorted(os.listdir(DATA_DIR)): person_dir os.path.join(DATA_DIR, person_name) if not os.path.isdir(person_dir): continue if person_name not in label_map: label_map[person_name] current_label current_label 1 for file_name in os.listdir(person_dir): if not file_name.lower().endswith((.jpg, .png)): continue file_path os.path.join(person_dir, file_name) face cv2.imread(file_path, cv2.IMREAD_GRAYSCALE) if face is None: continue face cv2.resize(face, (200, 200)) faces.append(face) labels.append(label_map[person_name]) if len(faces) 0: raise ValueError(数据集为空请先采集人脸图片) recognizer cv2.face.LBPHFaceRecognizer_create() recognizer.train(faces, np.array(labels)) recognizer.save(model.yml) with open(label_map.json, w, encodingutf-8) as f: json.dump(label_map, f, indent2) print(训练完成)保存的model.yml就是训练好的 LBPH 模型label_map.json保存数字标签和目录名的对应关系。识别阶段会用这两个文件完成预测。3.3 LBPH 参数与置信度解读训练 LBPH 时除了尺寸还有四个参数值得关注radius、neighbors、grid_x、grid_y。默认值radius1, neighbors8, grid_x8, grid_y8在大多数场景下都能用不建议新手一开始就乱调。这几个参数的含义分别是radius局部二值模式采样的半径。半径越大能观察到的纹理范围越广但细节信息越容易被抹掉。neighbors采样邻居点的数量通常需要大于等于 8。grid_x和grid_y把图像分成多少个横向和纵向小块直方图是在每个小块内分别统计的。我实际测试时发现grid_x8, grid_y8已经能让识别结果比较稳定。如果你觉得模型对五官细节不够敏感可以适当调大网格比如12x12但训练和识别都会变慢。predict方法返回两个值标签和置信度。这里和很多人直觉相反置信度越小越可信不是越大越可信。在我固定摄像头的环境里同一个人正常角度拍摄时置信度通常在 50 到 70 之间转头或者光线变化时置信度可能会升到 90 以上。所以阈值可以设到 80 或 85小于阈值才显示姓名。4. 实时人脸识别的完整流程训练好模型以后就可以写一个真正跑摄像头实时识别的脚本。这个脚本会在每一帧图像中做人脸检测对检测到的人脸区域进行预测最后把识别结果绘制到画面上。4.1 加载模型并逐帧预测实时识别脚本主体如下import cv2 import json with open(label_map.json, r, encodingutf-8) as f: label_map json.load(f) label_map {int(k): v for k, v in label_map.items()} recognizer cv2.face.LBPHFaceRecognizer_create() recognizer.read(model.yml) face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(80, 80) ) for (x, y, w, h) in faces: face_roi cv2.resize(gray[y:y h, x:x w], (200, 200)) label, confidence recognizer.predict(face_roi) if confidence 80: name label_map.get(label, unknown) display_text f{name} {confidence:.1f} else: display_text unknown cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText( frame, display_text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2 ) cv2.imshow(face recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()注意label_map.json里所有键都是字符串加载后要转成int否则label_map.get(label, unknown)取不到值所有能识别出来的人都会显示成unknown。这个错误很隐蔽我第一次跑的时候就踩到了。4.2 中文显示问题与处理方案cv2.putText自带字体不支持中文如果你直接把姓名写成中文画面上会出现问号或乱码。最简单的解决方案是标签就用英文或拼音也就是目录名直接叫person_0、person_1。如果项目必须显示中文可以用 Pillow 先把中文画在图像上再转回 OpenCV 的 BGR 格式。示例代码如下from PIL import Image, ImageDraw, ImageFont import numpy as np def draw_chinese(frame, text, pos): img Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) draw ImageDraw.Draw(img) font ImageFont.truetype(msyh.ttc, 24) draw.text(pos, text, fontfont, fill(0, 255, 0)) return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)这里的msyh.ttc是 Windows 系统字体换成你机器上存在的字体文件即可。有一点要留意每帧用 Pillow 画字会比putText慢低配机器上帧率会有肉眼可见的下降。4.3 提高识别稳定性和帧率实时识别最常见的体验问题有两个画面卡顿和结果闪烁。卡顿的根源是每帧都在原始分辨率上做 Haar 检测图像越大扫描越慢闪烁的根源是置信度在阈值附近抖动导致名字一会儿显示一会儿消失。要解决卡顿最简单的方法是先把输入帧缩小一半再检测检测到的人脸坐标放大回原始分辨率。比如small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) small_gray cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(small_gray, scaleFactor1.1, minNeighbors5, minSize(40, 40)) faces [(x * 2, y * 2, w * 2, h * 2) for (x, y, w, h) in faces]这样检测面积缩小到原来的四分之一速度提升非常明显。要解决结果闪烁可以不用每一帧都刷新姓名而是做一个简单的滑动窗口统计连续几帧识别到同一个标签才认为结果是稳定的。比如缓存最近 5 帧的识别标签取出现次数最多的那个作为最终显示结果。这种方法看着朴素但在固定摄像头场景下比调阈值更有效。5. 常见问题与排查实录整个项目做完以后我把容易踩到的问题整理成了一张速查表。这些基本覆盖了新手最常见的情况。现象可能原因处理方式cv2.face无法导入安装的是opencv-python缺少 contrib 模块卸载后改装opencv-contrib-python摄像头一直打不开权限未开启或设备索引不对检查系统摄像头权限尝试VideoCapture(1)训练时报错数据集为空或者图像尺寸不一致确认faces列表非空统一 resize 成 200×200类型错误labels 不是数组标签列表没转成 numpy 整数数组训练前用np.array(labels)转换总是显示 unknown置信度阈值太低、光照差异大、样本不足适当提高阈值重新采集环境相近的样本陌生人被识别成熟人置信度阈值太高降低阈值收集一些反面样本做测试画面很卡每帧都在大分辨率图上检测缩小输入帧或隔帧检测label_map.get(label)取不到JSON 键是字符串不是 int加载后把键转成整数我在实际操作中最深的体会是置信度阈值不是一个可以抄作业的固定值。网上很多教程写 50 或 60但换一个摄像头、换一个房间、换一个人数值都会变。最靠谱的方法是训练完以后分别用熟人和陌生人在同样环境下跑几十帧记录两边的置信度分布再把阈值定在两类数据之间。比如我家摄像头下熟人正常在 60 以下陌生人基本在 95 以上那阈值放在 80 左右就很稳。还要提一点采集样本的时候不要只拍正脸。LBPH 这类传统算法最怕的是“训练时一个角度识别时另一个角度”。我后来追加了一次采集刻意低头、抬头、左右侧脸各拍了几十张模型对姿态变化的容忍度明显好了很多。最后再分享一个小技巧识别框不要只框人脸还可以根据w和h的相对比例判断是不是正脸。如果你在做门禁类的项目可以在识别结果里加一个条件当w h * 0.8时才放行这样能过滤掉很多侧脸误判。这个技巧不需要额外模型成本几乎为零但效果非常实在。

相关新闻

C++类型标签分发详解:编译期重载与模板元编程实践

C++类型标签分发详解:编译期重载与模板元编程实践

类型标签分发(Tag Dispatch)这个词,看着像教科书里的冷门术语,但你要是写过一点泛型代码、碰过std::advance或者自己造过迭代器,其实早就和它打过照面了。简单说,它就是靠一个“空的、标识用的类型”去触发…

2026/10/15 18:18:30 阅读更多 →
编译原理课设工程实战:从词法分析到代码生成

编译原理课设工程实战:从词法分析到代码生成

简介:这是一份面向高校计算机专业学生的编译原理课程设计资料包,适用于需要完成词法分析、语法分析及小型编译器实验的本科课程项目。内容覆盖词法分析器、基于LL(1)的简单语句分析(如ii*i)、LR(0)与SLR(1)语法分析、四元式生成、…

2026/10/15 18:18:30 阅读更多 →
共射极放大电路实验报告:静态工作点、仿真与实测误差全解析

共射极放大电路实验报告:静态工作点、仿真与实测误差全解析

简介:这是一份基于Multisim仿真完成的共射极放大电路实验报告,适用于电子技术、模拟电路课程学习者以及需要完成类似仿真实验的自动化、电子信息类专业学生。报告围绕共射极放大电路的静态工作点计算、动态指标测量与非线性失真分析展开,包含…

2026/10/15 18:18:30 阅读更多 →

最新新闻

Unity Profiler性能分析实战:从真机采集到GC优化完整指南

Unity Profiler性能分析实战:从真机采集到GC优化完整指南

1. 为什么每个Unity开发者都该把Profiler当日常工具做Unity这几年,我见过太多项目在编辑器里跑得飞起,一打包到真机就卡成幻灯片。帧率从90掉到22,发热烫手,玩家骂声一片,团队却找不到瓶颈在哪。这种场景几乎每个中大型…

2026/10/15 22:09:39 阅读更多 →
SolidWorks干涉检查假干涉排查:隐藏实体与压缩的区别及选项设置

SolidWorks干涉检查假干涉排查:隐藏实体与压缩的区别及选项设置

1. 先别急着翻设计树:干涉检查报出的"假干涉"从哪来做整机装配干涉检查的时候,我猜你肯定遇过这种场面:明明把弹簧、密封圈、线束、装饰罩这些零件都隐藏了,图纸看着清爽得很,结果一点"干涉检查"&…

2026/10/15 22:09:39 阅读更多 →
collect.js 严格比较设计指南:全方法默认 `===` 的实现原理与 Laravel 差异对照

collect.js 严格比较设计指南:全方法默认 `===` 的实现原理与 Laravel 差异对照

开发工具 【免费下载链接】collect.js 💎 Convenient and dependency free wrapper for working with arrays and objects 项目地址: https://gitcode.com/gh_mirrors/co/collect.js 点击查看 免费下载 导读 本文围绕 collect.js 的**严格比较&#x…

2026/10/15 22:09:39 阅读更多 →
6D位姿估计综述:RGB-D、点云、RGB三条路线选型与复现避坑指南

6D位姿估计综述:RGB-D、点云、RGB三条路线选型与复现避坑指南

简介:这份资源是面向机器人视觉与三维感知方向学习者、研究者的6D位姿估计综述文献总结,以思维导图形式梳理了Vision-based Robotic Grasping综述的核心脉络,帮助读者快速建立从物体定位、位姿估计到抓取估计的整体认知框架。内容覆盖基于RGB…

2026/10/15 22:09:39 阅读更多 →
memtester内存压力测试实战:从原理到脚本化压测

memtester内存压力测试实战:从原理到脚本化压测

简介:这是一份内存压力测试工具 memtester 4.1.2 的完整源码包,面向 Linux/Unix 系统管理员、运维工程师与底层软硬件开发者,用于检测内存条位翻转、数据丢失、地址错误等不稳定因素,也可作为服务器上线前压力验证和故障排查的有力…

2026/10/15 22:09:39 阅读更多 →
MySQL时间类型选型:DATETIME与TIMESTAMP底层差异与避坑指南

MySQL时间类型选型:DATETIME与TIMESTAMP底层差异与避坑指南

MySQL 的时间类型,尤其是在 DATETIME 和 TIMESTAMP 之间反复纠结的情况,我见过太多了。很多人直到线上出现“时间差8小时”或者“2038年数据写不进去”才发现自己对这两个类型的理解还是靠猜。今天把这俩家伙的底层逻辑、行为差异、性能取舍和选型思路一…

2026/10/15 22:08:39 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →