简介:YOLOv8瞳孔识别目标检测项目代码,基于Ultralytics YOLOv8框架开发,面向需要快速落地瞳孔定位任务的计算机视觉开发者和学习者。项目聚焦瞳孔这一小目标检测场景,可应用于人机交互视线估计、医疗辅助诊断、疲劳驾驶监测等领域。资源以zip压缩包形式提供,大小约28.29MB,内含环境依赖文件requirements.txt及核心检测代码,按文件说明配置依赖即可直接运行,显著降低复现门槛。目前已有203人学习下载,适合作为YOLOv8实战练习或算法改造的参考。通过该项目代码,读者可以学习YOLOv8数据加载、模型训练与推理的具体实现,理解在特定目标检测任务中的调参与改进思路,并能够将代码迁移到其他自定义检测场景。项目结构简洁,注释清晰,便于快速定位关键模块;若需配套数据集,资源描述中已给出数据集下载与详细介绍链接,可结合使用。
1. 为什么执着于用目标检测做瞳孔识别
1.1 瞳孔识别的真实场景与需求
项目名起得挺简单——YOLOv8瞳孔识别。但真正动手前,得先搞清楚一个问题:你要的到底是瞳孔检测、瞳孔分割、还是瞳孔中心点定位?这三件事看起来像,技术路线却差得远。
我做这个项目最初的动机来自一个疲劳驾驶预警的原型验证。传统方案多用红外设备加Dlib人脸关键点,或者用霍夫变换找圆,但实测下来都不够稳。瞳孔这东西,说白了一个黑色椭圆区域,光线稍微变一下、眼睛眨一下、头偏一下,传统图像处理就崩了。换成目标检测思路后,整个流程变得简单粗暴:输入一张人脸图,模型直接告诉我瞳孔在哪、范围多大。这背后的核心诉求是鲁棒性,是要在非受控环境下也能稳定输出。
这个方案能辐射的场景其实很广。除了疲劳驾驶,还有视线追踪、注意力检测、医疗辅助诊断(比如瞳孔对光反射的自动评估)、人机交互里的眼动控制。哪怕是做科研或者毕业设计,一个端到端的瞳孔检测模型也比调一堆传统算法的上限高得多。适合来看这篇内容的人,是那种手头有数据集或者能自己采集数据,想快速跑通一个目标检测模型,但又不确定该怎么处理小目标、怎么调参、怎么判断模型好坏的开发者。
1.2 传统瞳孔检测方案的痛点
先说说我踩过的传统方案的坑,这有助于理解为什么最后选了YOLOv8。
最经典的做法是霍夫圆检测。思路挺直观:瞳孔近似圆形,边缘梯度明显,用Canny提取边缘后再找圆。但问题在于,霍夫变换对参数极其敏感。瞳孔在图像里可能只有几十个像素直径,光照稍微不均匀,边缘就断了;眼睫毛一挡,轮廓就缺了一块;戴眼镜的话,镜片反光会直接把瞳孔边缘打成碎片。调参调到头秃,换一段视频又废了。
另一个常见方案是阈值分割加轮廓查找。这个方法在红外图像下效果还行,因为红外下瞳孔和虹膜的对比度很高。但在可见光图像下,深色虹膜和黑色瞳孔的灰度差很小,阈值设高了把瞳孔淹没,设低了把眉毛和睫毛全选进来。不同人种、不同光照下的最优阈值还不一样,写成规则就是一场灾难。
深度学习方案的逻辑完全不同。它不依赖人工设计的特征,而是让模型自己从大量标注样本里学“瞳孔长什么样”。这带来一个好处:模型能隐式地学到瞳孔在不同条件下的形态变化,比如部分遮挡、反光、模糊,泛化能力远超手工特征。
1.3 选型YOLOv8的核心逻辑
市面上目标检测模型一大把,为什么我选了YOLOv8,而不是Faster R-CNN或者SSD?
第一个原因是速度与精度的平衡。Faster R-CNN这种两阶段检测器精度不错,但在低算力设备上跑实时推理有点吃力。YOLO是单阶段检测器,直接在特征图上回归目标的类别和边框,天然适合实时场景。瞳孔识别如果用在驾驶行为分析或者眼动追踪上,实时性不是加分项,是硬指标。
第二个原因是YOLOv8本身的架构设计。它的Backbone用了CSPDarknet结构,颈部是PAN-FPN,Head换成了主流的Decoupled Head。重点说这个Decoupled Head——分类和回归分支各自独立,对小目标的分类和定位精度都有提升。而C2f模块替换了早期YOLOv5里的C3模块,引入了更丰富的梯度流,简单理解就是特征提取更充分了,这对小目标很关键。
第三个原因是工程化便利。Ultralytics这个框架把训练、验证、导出、部署全链路封装得相当完善。不是每个人都愿意从头写训练循环、NMS、数据增强那一大堆代码。YOLOv8开箱即用,同时保留了足够的自定义空间,适合拿来快速验证想法,也适合后期做模型改进。
2. 瞳孔数据集的准备与标注细节
2.1 数据集从哪来、怎么凑
模型好不好,数据占七成。瞳孔检测的数据集比通用目标检测的数据集少得多,需要自己想办法。
几个可行的渠道:一是公开数据集,比如一些眼动追踪领域的公开数据,像OpenEDS、NVGaze,不过这些大多是合成图像或者特定设备(如VR头显)采集的,迁移到普通摄像头场景需要考虑域差异。二是自己采集,用普通RGB摄像头对准人脸,尽量覆盖不同光照条件、不同头部姿态、戴不戴眼镜的样本。三是混合方案——公开数据集做预训练,自己采集的数据做微调。
我当时大概收集了8000张左右的人脸图像,包含约15000个瞳孔标注。说实话数量不算多,但关键在于多样性。我个人体会,样本多样性比样本数量更重要。同一个人的1000张图,远不如100个人的各10张图有用。标注时类别就一个:pupil。
2.2 小目标标注的细节技巧
瞳孔在整张图中的占比非常小,可能只有几十乘几十像素,这在目标检测里属于典型的小目标。标注这种小目标有几个细节要注意。
第一,边界框要贴边。稍微多框一点背景或者少框一点瞳孔边缘,都会对后续的回归训练产生噪声。如果用的是LabelImg这类工具,建议把图像放大到400%再精确调整边界。
第二,边界框的形状问题。瞳孔近似圆形,但YOLO的边界框是矩形。标注时应该用外接正方形还是最小外接矩形?实践中我推荐外接正方形。因为瞳孔是圆的,正方形外接框的中心更接近瞳孔中心,而且避免宽高比极端变化给回归头带来额外负担。
第三,注意异常样本的处理。比如闭眼状态的图像要不要标注?我的建议是不标注。闭眼时瞳孔不可见,硬标一个框会让模型学会在眼皮上输出虚假检测。睡眠检测场景另说——那种情况你应该标注的是“闭眼状态”,而不是“瞳孔位置”。
第四,遮挡的处理。轻微遮挡(比如睫毛挡住一小部分)正常标注完整瞳孔范围。严重遮挡(比如超过一半被眼睑覆盖)建议直接丢弃。中间地带的取舍标准是:人眼能准确判断瞳孔中心位置的,就标;判断不了的,不标。
2.3 数据增强策略与常见误区
YOLOv8默认自带Mosaic增强、随机仿射变换等,对小目标数据集默认设置并不一定是最优的。
Mosaic增强会把四张图拼成一张,变相扩大batch size,对提升小目标检测效果帮助较大,但要注意一件事:瞳孔太小,在Mosaic拼接时会被进一步缩小,有些甚至缩到几个像素,变成无效学习。如果发现loss降不下去,可以试试把Mosaic关闭或者只在训练后期开启。
我自己后续用的增强策略是:
- 保留YOLOv8默认的hsv_h、hsv_s、hsv_v,因为光照变化是瞳孔识别的核心干扰项。
- 加大随机旋转范围到正负30度,因为头部姿态变化会导致瞳孔在图像中倾斜。
- 增加随机平移和缩放比例,模拟摄像头在不同距离下的成像效果。
增强的目的是让模型见过足够多的“变体”。但如果增强过猛导致样本失真,比如旋转90度后瞳孔看起来像外星人眼睛,那是帮倒忙。增强之后的样本,仍需保证人眼能认出瞳孔,这是底线。
3. 环境配置与模型训练实战
3.1 需要什么硬件,GTX 1660 Ti够不够
总有人问“GTX 1660 Ti能不能跑YOLOv8”。能,完全能。我最初就是在6GB显存的GTX 1660 Ti上完成训练的。YOLOv8n(nano版本)在这种显卡上训练COCO级别的通用数据集大约需要几十个小时,但瞳孔数据集很小,训练时间其实可控。
关于显存占用,几个关键因素:
- 模型尺寸:YOLOv8有n/s/m/l/x五个版本,参数从300万到6800万不等。瞳孔数据集不大,n和s完全够用,m是为了对比实验才跑的。
- 输入分辨率:默认640x640,在显存不够时可以降到512甚至416。瞳孔足够小,分辨率反而不能太低。
- batch size:6GB显存跑YOLOv8s建议从batch=8起步,显存不够就等比例缩小。
如果没有GPU,纯CPU训练也不是不行,但会特别折磨。用CPU训练一个YOLOv8s模型,一个epoch可能就要十几分钟,跑几百个epoch要命。有条件还是建议搞一个GPU,哪怕是云GPU也行。
3.2 环境安装与数据组织
环境安装没什么玄学,就是conda建环境、pip装ultralytics。需要注意版本锁定,ultralytics迭代快,不同版本的API和默认配置有差异。我的建议是创建一个独立conda环境,装好ultralytics和对应版本的torch,之后就一直用,别随便升级。
数据组织按YOLO格式来,目录结构如下:
datasets/ pupil/ images/ train/ val/ labels/ train/ val/标注文件是txt格式,每行一个目标,格式为“class x_center y_center width height”,坐标全部归一化到0到1。比如一张640x480的图像里,瞳孔中心在(320, 240),宽高都是40像素,则标注为“0 0.5 0.5 0.0625 0.0833”。特别注意,这里的宽高是相对图像宽高的比例,不是绝对像素值。我第一次写脚本时在这里踩了坑,算出的归一化坐标老是不对,害得训练时loss根本不收敛。
3.3 训练参数的选择思路
Ultralytics框架训练命令非常简单:
yolo detect train data=pupil.yaml model=yolov8s.pt epochs=200 imgsz=640 batch=16 device=0但参数背后的逻辑值得琢磨。首先是预训练权重的选择。用yolov8s.pt作为起点,而不是从零训练。COCO预训练模型已经学到了丰富的底层特征,迁移到瞳孔检测上相当于站在巨人肩膀上。对于小数据集,用预训练模型微调几乎是必须的,否则很难收敛。
另外yaml配置文件的写法也要注意。我当时按Ultralytics要求做了一个pupil.yaml,内容大概如下:
path: datasets/pupil train: images/train val: images/val nc: 1 names: ['pupil']这个文件里的类别数、类别名必须和标注文件对应,否则训练直接报错。训练时加入验证集,epochs先设200,配合early stopping机制(patience=50),模型如果在50个epoch内没有明显提升就直接停止,节省时间。
3.4 训练过程中的监控与调整
训练过程中需要盯几个东西:loss曲线、验证集的精度指标、以及偶尔的预测可视化。
Loss一般包含三部分:box回归损失、分类损失、DFL损失。正常的训练状态是这三条曲线都平稳下降,最终趋于平台期。如果loss曲线震荡剧烈不收敛,优先检查数据标注有没有错、学习率是不是太高。
学习率方面,Ultralytics默认使用AdamW优化器和余弦退火调度器,默认的学习率是0.002。经验上如果数据集很小,可以适当降低到0.001,防止在预训练权重的基础上“走过头”。当时我用默认参数训练,出现了轻微的过拟合——训练集loss很低,但验证集mAP上不去。把学习率调低后,情况明显改善。
训练的另一个坑是pupil小目标本身Anchor匹配的难度。YOLOv8是Anchor-Free的,但它还是依赖目标尺寸和特征层感受野的匹配。640x640的输入下,瞳孔通常只有10x10像素到50x50像素,这需要模型深层特征有足够的空间分辨率。如果发现mAP一直上不去,可以试试提高输入分辨率到768或者1024,或者加入P2小目标检测头。P2检测头就是把Backbone更浅层的特征也拿来做检测,分辨率更高、对小目标更友好,代价是显存和计算量增加。
4. 训练完成后的关键验证与评价
4.1 评价指标怎么读
训练结束后,Ultralytics会在验证集上跑出一堆指标,新手最需要关注的几个是Precision、Recall、mAP50和mAP50-95。
Precision衡量的是“检测出来的目标里有多少是真正的瞳孔”,Recall衡量的是“真正的瞳孔有多少被检测出来了”。瞳孔检测如果用在疲劳预警上,Recall比Precision重要——漏检一次可能就错过了一次危险操作。但也不能为了Recall牺牲太多Precision,否则屏幕上到处都是误报框也没法用。
mAP50是指IoU阈值为0.5时的平均精度,mAP50-95是IoU从0.5到0.95步长0.05的平均精度。对瞳孔这种小目标,mAP50相对容易达到不错的水准,但mAP50-95会比较低,因为它更考验边界框预测的精确度——这不完全是我们模型的锅,小目标本身的IoU对几个像素的偏移都很敏感。
我当时的训练结果在验证集上大约是Precision 0.93、Recall 0.91、mAP50 0.95、mAP50-95 0.68。mAP50-95偏低的主要原因是框的位置还不够准,尤其是在戴眼镜和遮挡的场景下。
4.2 推理测试与边界情况
模型训练完,拿一段全新视频来测试,不能只看验证集指标。我习惯准备三段视频:正常光照场景、强反光场景、戴眼镜场景,分别观察模型的框稳定性和置信度变化。
测试时有一个参数需要调整,就是置信度阈值(conf)。默认0.25在瞳孔检测上略低,会导致很多低置信度的误报。我调到0.35左右,效果好很多。另外NMS的IoU阈值保持默认0.7就行,瞳孔之间在空间上不会互相重叠太多,这个参数影响不大。
边界情况里最需要注意的是快速眨眼和运动模糊。眨眼瞬间瞳孔是不完整的,模型很可能给出低置信度的小框,这种结果应该在后处理里根据时序做平滑——上一帧有框、这一帧置信度骤降的,直接用上一帧的结果补充或做kalmn滤波。后处理逻辑虽然简单,但能让最终系统在真实场景里的体验提升一大截。
4.3 导出模型用于部署
训练完的模型格式是PyTorch的.pt,部署时通常要转成TensorRT或其它推理格式。Ultralytics提供了便捷的导出命令:
yolo export model=best.pt format=engine device=0TensorRT导出后会生成一个engine文件,推理速度能提升数倍。我在这次项目中还专门在RK3588的板子上部署过,整条链路是:PyTorch模型转ONNX,再转RKNN,在NPU上跑推理。边缘设备部署和训练是完全不同的思路,训练时要追求精度上限,部署时要在精度和速度之间找平衡点。
5. 最常见的训练问题与排查思路
5.1 小目标漏检怎么办
漏检是瞳孔检测最让人头疼的问题。排查时先想清楚漏的是训练集里的还是新场景里的。如果是训练集里的某些样本漏检,大概率是标注问题或样本不均衡。用yolo detect val跑一版验证集的结果,把漏检的图像翻出来看,挨个检查标注框有没有问题。
如果是新场景里的漏检,那就是泛化问题。处理方法有几种:增加该场景的样本、加大数据增强的强度、提高输入分辨率、或者尝试加入小目标检测头。当时我发现红外摄像头下的瞳孔位置总是偏下,因为红外图像里瞳孔边缘的灰度特征和可见光不同,单纯靠增强不够,最后还是补了一批红外样本,才算把漏检率压下来。
5.2 误检频繁出框
误检的场景通常出现在眼眶附近,模型把眼睫毛、眼影、眼镜框边缘当成瞳孔。这背后其实反映了特征混淆——模型没有学到足够强的“瞳孔区域与周围区域”的判别特征。
我的处理经验是分三步走。第一步增加负样本(不含瞳孔的人脸图像),让模型看到“没有瞳孔长什么样”。第二步检查标注的边界框是否过大,如果框里包含太多虹膜区域,模型会倾向于把深色虹膜也当成目标的一部分。第三步考虑增大输入分辨率,让瞳孔区域对应的特征图更大,模型能更精细地建模边界。
5.3 Loss不下降或NaN
这是新手最容易慌的问题。我的排查顺序是:先检查标注文件路径对不对、标注坐标有没有超出0到1的范围。如果标注没问题,试试加载预训练模型而不是从零训练。从零训练在数据量不足时,loss在很长时间内不下降是正常的。如果出现NaN,优先怀疑学习率过大或者数据里混进了异常值(比如宽度或高度为0的标注框),把学习率降到0.0005再看看。
5.4 关于训练耗时的另一种选择
如果你确实没有GPU,或者只想快速验证一个思路,Ultralytics还支持在Colab或者Kaggle这类在线平台上训练。瞳孔数据集很小,用免费GPU训练完整个流程大概不会超过1小时。环境配置和本地一致,用!pip install ultralytics先装上,再把数据传上去就行。我有时会在本地做小规模调试,到云端跑全量训练,两边的工作流保持同步。
最后分享一点实操体会
做了几次瞳孔检测项目之后,其实最大的感受是:在目标检测里,算法的坑反而没有数据的坑多。YOLOv8的代码、权重、训练流程都已经很成熟,真正决定模型上限的,是你喂给它的标注数据质量,以及你对场景边界条件的理解。网上很多人说“train一发就完事了”,用在小目标场景就是自欺欺人。把一条视频放到模型面前,盯住所有漏检误检的帧,逐帧追问原因,这个流程永远避不开。
最后再掏一个细节技巧:瞳孔识别做完之后,别急着收工。把模型的输出框中心点连成一条轨迹,看一下时间序列的稳定性。很多时候单帧检测结果看着还行,但画成轨迹就是一抖一抖的。这时候在输出端加一个简单的滑动平均或者卡尔曼滤波,系统体验会提升很多。这类后处理技巧不会出现在模型论文里,但它才是从“能跑demo”走向“能用”的关键一步。
本文还有配套的精品资源,点击获取