这几年计算机类的毕业设计选题,目标检测算得上一个大热门,尤其是监控视频里的车辆检测与识别,既有落地场景又有数据来源,做出来还容易演示。我自己搭过这套基于YOLOv8的监控视频车辆检测识别项目,从数据整理到模型训练,再到视频流推理,踩过的坑不少,但跑通之后发现整套链路其实特别适合作为深度学习入门的完整实战。这篇文章就按我自己的实操路径,把关键环节和注意点原原本本写出来,给正在做毕设或者想复现类似项目的同学做个参考。
1. 项目核心需求与整体设计思路
1.1 毕设项目到底想解决什么问题
监控视频里的车辆检测与识别,说白了就是让计算机自己从画面中把车的位置和类别圈出来。作为毕设,通常要解决四层问题:模型选型、数据准备、训练调优、视频推理。很多人以为麻烦点在模型,实际做了之后才发现,数据准备和视频推理才是最容易消耗时间的地方。
我这次把检测目标设定为“轿车、SUV、卡车、公交车”四类,输出目标边界框和置信度,最终在测试视频上给出可视化的检测结果。考虑到监控场景往往视角固定、背景复杂,不同时间段的光照差异又大,模型还要具备一定的鲁棒性,比如夜间车灯照射、雨天反光、树木阴影遮挡这些情况都不能大面积漏检。这些都是需求分析阶段就要明确的点,否则做到后面容易变成“能跑就行”的演示项目。
功能边界上我建议做减法。车辆检测是核心,跟踪、计数、测速是加分项。如果时间够,可以再加一个简单的跨帧跟踪和流量统计,能让答辩演示明显更有说服力;如果时间紧,先把检测做扎实,效果图和指标都不会差。毕设答辩的核心逻辑永远是“问题定义是否清晰、技术路线是否合理、结果是否可靠”,而不是功能堆得多花哨。
1.2 为什么选择YOLOv8而不是其他模型
YOLO系列发展到v8,一个很大的变化是官方库把训练、验证、预测封装得非常干净,几乎不需要写太多底层代码。之前的Faster R-CNN固然稳定,但两阶段结构在视频推理速度上吃亏;YOLOv5作为单阶段检测器已经很成熟,不过v8在Backbone里引入了C2f结构,Neck部分换成PAN-FPN,检测头改成了解耦头,这些改动让它在精度和收敛速度上都有提升,而且复现成本并没有增加。
可能有同学会问,既然v9、v10都出来了,为什么不紧跟最新版本?毕设的核心目标是稳。v8生态成熟,资料多、报错好查,预训练权重丰富,对显存的要求也更低,踩坑成本小。说句实在话,模型版本之间的差距在车辆检测这个相对简单的任务上,远没有数据和参数的影响大。与其追新,不如把v8吃透。
补充一个选型判断标准:如果你的显卡显存只有6~8G,yolov8n和yolov8s是首选;显存在12G以上可以考虑yolov8m;想冲到yolov8l以上,建议先确认一下显存和训练时间是否撑得住,否则训练中断一次就够折腾半天。我做毕设那会儿用的就是普通消费级显卡,yolov8s训练100轮大约需要七八个小时,时间成本完全可控。
1.3 系统整体架构和模块划分
我习惯把整个项目拆成三个独立模块:数据模块、训练模块、推理模块。数据模块负责视频抽帧、图像筛选、标注、数据集划分;训练模块负责模型配置、训练、评估导出;推理模块负责读取视频、逐帧检测、可视化绘制、结果输出。三个模块之间有清晰的输入输出边界,比如数据模块产出的yaml配置和标注文件夹,直接作为训练模块的输入;训练模块产出的best.pt,直接用于推理模块加载。
独立模块最大的好处是调试方便。标注结果不对,不会牵扯到训练代码;训练效果不好,也不会影响推理脚本的稳定性。答辩讲解时还能按模块逐个展开,逻辑非常清晰,比把大量逻辑写在一个notebook或单脚本里更好呈现。下面这张表可以直观看出每个模块的职责:
| 模块 | 核心功能 | 关键产物 |
|---|---|---|
| 数据模块 | 视频抽帧、筛选、标注、划分数据集 | 标注txt、数据集yaml |
| 训练模块 | 加载预训练权重、训练、评估 | best.pt、训练日志 |
| 推理模块 | 视频读取、检测、绘制、输出 | 标注视频、检测统计 |
模块边界清晰之后,你会发现整个项目的推进节奏很容易控制。先跑通数据模块,再跑训练,最后做推理,每一步都有可以验证的中间产物,不会到了最后才发现问题埋在很前面的环节。
2. 环境搭建与数据准备的关键细节
2.1 深度学习环境配置与版本匹配
配置环境的坑很少,但版本匹配很关键。我使用的是Python 3.10、PyTorch 2.x,配合YOLO官方工具库,显卡是消费级8G显存卡。强烈建议用虚拟环境来装依赖,不要直接在系统环境里pip install,否则后续其他项目很容易出现依赖冲突。
基础步骤是这样的:先安装CUDA和cuDNN,再创建虚拟环境,运行pip install torch,然后安装YOLO官方库和相关依赖。安装完成后,用一小段代码确认GPU是否可用,很多同学训练速度异常慢,绝大多数原因是torch装成了CPU版本,这一步测试能帮你提前发现问题。
关于YOLO库为什么推荐用官方版而非第三方封装,核心原因是版本迭代太快,很多第三方教程和脚本都是基于旧版接口写的,参数名对不上会造成各种奇怪报错。直接用官方库,训练、验证、导出的API保持统一,遇到问题搜解决方案也更容易命中。另外建议把环境依赖记录在requirements.txt里,方便换机器或者另外的同学复现,这也是毕设文档里一个加分细节。
2.2 监控视频车辆数据集的构建与标注规范
这部分是项目里最容易返工、也最影响最终结果的环节。很多同学直接下载一个公开通用目标检测数据集凑合用,里面虽然包含car、bus、truck这些类别,但监控视角的样本比例少,实际场景中的俯拍视角、远距离小目标、夜间图像等数据严重不足,训练出来的模型在自测视频上会明显水土不服。
我当时的处理方式是从多段监控视频中抽帧筛选,每隔若干秒保存一帧,然后挑出光线清晰、目标有代表性的画面进入标注流程。注意不要只从一个机位抽帧,否则模型容易过拟合到固定背景上。我补充了白天、夜晚、雨天三类场景,每类几百张图,再配合部分通用数据作为底座,模型泛化能力才基本够用。
标注规范值得多说几句。我用的格式是YOLO的txt格式,每行代表一个目标:类别编号、x_center、y_center、width、height,后四个值都是归一化坐标,需要除以图片宽高。很多人自己生成标注时会把坐标写成像素值,训练时loss直接爆炸,这是新手最容易踩的坑。标注工具建议用常见的图形化框式标注工具,导出时选YOLO格式即可。
遮挡目标的标注也是一门学问。两个车重叠时,是框出可见部分还是完整车身?我的经验是尽量框出完整车身,因为模型能从轮廓中学习到遮挡关系,只在严重到不可辨认时才按可见部分标注。另外,夜间车灯造成的反光区域不要标注成目标,否则会严重干扰类别判断。这些规范最好在标注开始前就定下来,不然标到一半再统一修改,工作量会翻倍。
数据集划分上,建议按7:2:1分成训练、验证、测试三部分,同时保证同一段视频的连续帧不要同时进入训练集和验证集,否则会数据泄漏,最终评估结果虚高而不自知。我当时就吃过这个亏,第一次验证集map明显高于后面实际测试效果,后来检查发现就是连续帧串了数据集。
2.3 数据增强策略与训练配置
YOLO库自带的马赛克增强、平移、翻转、色彩调整等策略对车辆检测基本够用,不需要额外写太多增强逻辑。我额外加入了轻度噪声和亮度抖动,目的是模拟监控画面常见的噪点和不同时段的亮度变化。增强参数不要调得太猛,马赛克增强在训练后期建议关闭或降低比例,否则小目标检测能力反而不稳定。
训练配置上,我建议先按官方推荐的超参来一轮,重点调epochs、batch和imgsz。对于八到十类以内的目标检测任务,100轮以内基本足够。batch设成8在8G显存跑640分辨率是没有问题的,如果想提高模型精度,可以先把imgsz提高到960试试,但显存占用会成倍上升,需要同步降低batch。这里有个实际经验:分辨率提高带来的精度提升,在小目标较多的监控场景里是肉眼可见的,但代价是训练时间变长,需要自己权衡。
3. YOLOv8模型训练、评估与推理完整实操
3.1 训练脚本关键参数与调参心得
训练命令本身很简洁,关键是理解每个参数背后的意义。我用的模型配置是yolov8s预训练权重,数据集配置指向自己准备的yaml,训练100轮,batch为8,输入分辨率640。
from ultralytics import YOLO model = YOLO("yolov8s.pt") model.train(data="vehicle.yaml", epochs=100, batch=8, imgsz=640)参数解析我简单列一下:
- model:选择模型结构或预训练权重路径,如果写yolov8s.pt,会自动下载预训练权重并开始微调;
- data:数据集配置yaml的路径,里面写好train、val路径以及类别列表;
- epochs:训练多少轮,不是越多越好,要结合验证集表现判断;
- batch:一次进入显存的图片数量,显存不够就调小;
- imgsz:输入图像尺寸,直接影响显存占用和检测精度。
调参方面,我的观点是先用默认参数跑通,再针对自己的数据做小范围改动。学习率、权重衰减、动量这些超参,YOLO官方给的默认值已经比较可靠,乱调反而容易不收敛。要优先观察训练日志里的loss曲线和各指标变化,如果验证集指标在后期停滞或下降,说明过拟合,可以调低epochs或开启早停。
训练完成后会保存两个权重文件:best.pt和last.pt。best.pt是在验证集上表现最好的模型,推理时直接用best.pt即可;last.pt则用于中断后继续训练。很多同学误用last.pt做最终推理,跑出来的效果比best差,还以为是模型问题,这个细节注意一下就好。
3.2 模型评估:map指标到底该怎么读
看完训练日志,很多人只盯着一个mAP数字,这其实不够。训练结束后,验证集上会输出mAP50、mAP50-95、precision、recall等指标,其中mAP50是IoU阈值取0.5时的平均精度,通俗说就是框得不够准但类别对了也算检测正确;mAP50-95则是在0.5到0.95多个IoU阈值下的平均结果,对框的位置精度要求更高,也更严厉。
我这次车辆四分类任务,100轮训练后mAP50大概在0.87左右,mAP50-95在0.63上下。但真正要看的不是均值,而是每个类别的单独指标,比如卡车通常很好检,但夜间路灯下的轿车容易出现漏检,这类问题只看总体mAP是发现不了的。把验证集的预测结果可视化输出,逐张查看漏检和误检样例,比盯着指标数字更有价值。
评估阶段还有一个容易被忽略的点:测试集一定不要和训练集、验证集有重叠。很多时候我们觉得模型效果不错,但换一段完全没见过的视频就崩了,大概率就是评估时数据串了。哪怕没有重叠,不同摄像头的视角差异也可能导致效果波动,所以最好在最终测试视频上也跑一遍,把结果录下来作为项目演示素材。
3.3 视频流检测识别完整实现:抽帧、推理、可视化、输出
监控视频推理的核心是两个问题:如何保证速度、如何让结果稳定输出。我用的方案是OpenCV读取视频流,逐帧送入模型检测,再把绘制好的结果写入新视频文件。
如果要严苛地追求实时性,可以做跳帧推理,每隔一帧或两帧推理一次,中间帧直接沿用上一帧结果,在1080P视频上基本能保持流畅。不过跳帧只适合演示场景,如果毕设要求严谨的车流量统计,建议配合跟踪模块而不是简单跳帧。
用YOLO官方库做推理时,检测结果是一个包含boxes、classes、scores等信息的对象,可以直接调用plot方法在原始图像上绘制。如果想控制字体、边框粗细和颜色,也可以手动用OpenCV在循环里绘制:读取框坐标、类别编号、置信度,然后画矩形和文字。手动绘制能做出更统一的输出样式,我在输出视频时选择手绘方案。
下面这段代码是推理循环的简化版,可以把整个流程跑通:
import cv2 from ultralytics import YOLO model = YOLO("best.pt") cap = cv2.VideoCapture("test.mp4") fourcc = cv2.VideoWriter_fourcc(*"mp4v") fps = int(cap.get(cv2.CAP_PROP_FPS)) w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer = cv2.VideoWriter("output.mp4", fourcc, fps, (w, h)) while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model.predict(frame, conf=0.4, iou=0.5, imgsz=640) annotated = results[0].plot() writer.write(annotated) cap.release() writer.release()视频写出时要注意编码器设置,常用的MP4格式需要指定编码参数,并确保每一帧的尺寸和写入顺序一致,否则输出视频可能出现画面拉伸或关键帧丢失。绘制过程中在画面左上角加上当前FPS,一方面方便演示,另一方面也能直观感受模型的推理开销。
4. 常见问题、采坑记录与性能优化实战
4.1 显存不足、训练不稳定、漏检误检典型排查思路
显存不足几乎是人人都会遇到的问题,尤其在Windows环境下,8G显存跑默认配置可能直接OOM。排查思路很简单:先降batch到2,再不行把imgsz降到480,如果还不稳定,检查后台是不是有其它程序占用了显存。训练不稳定一般跟学习率和数据集质量关系更大,loss曲线剧烈震荡时,先确认标注数据有没有明显错误,比如类别漏标、框位置偏差大,这些都会让模型学得混乱。
漏检误检方面,监控视频里最常见的现象是远处小目标漏检和夜间误检。漏检优先考虑加大输入分辨率,或者把图像切块检测;误检则要看训练数据里是否缺少对应场景的负样本。可以专门收集一批没有车辆的背景帧加入训练,让模型学会区分场景和真实目标。下面我把高频问题整理成了一张速查表,方便对号入座:
| 现象 | 常见原因 | 优先排查方向 |
|---|---|---|
| 训练时显存溢出 | batch或imgsz过大 | 降batch、降分辨率 |
| 训练和验证loss不一致 | 数据泄漏、增强过强 | 检查数据集划分、关掉部分增强 |
| 小目标漏检严重 | 输入分辨率不足、样本少 | 提高imgsz、切片推理 |
| 夜间误检多 | 负样本缺失、噪声干扰 | 补充背景帧、夜间数据 |
| 推理视频卡顿 | 模型过大、逐帧推理 | 换轻量模型、跳帧推理 |
4.2 小目标车辆检测优化实战
小目标问题在监控场景下特别突出,摄像头安装高、远处车辆在画面里可能只有几十个像素。我试过三种有效手段:多尺度训练、切片推理、小目标数据增强。多尺度训练是在训练时随机改变输入尺寸,让模型看到更多不同大小的目标;切片推理是把大图切分成多个子区域分别检测,再合并结果,效果立竿见影,但推理时间也成倍上涨,只能作为精度优先时的备选方案。
还有一个容易被忽视的点:原始视频分辨率。如果拿到的是720P监控视频,直接放大到1080P再检测并不会提升精度,反而增加计算负担;反过来,如果训练时用640分辨率,推理时也尽量保持一致,过大的分辨率差异反而可能导致性能下降。我一开始就踩过这个坑,训练用640,推理视频却是4K拉伸,结果检测框全部偏移,后来把推理分辨率统一回640才正常。
切片推理的实现思路也简单:把一帧图像按固定格子切分成四块,每块送入模型检测,再把检测框坐标映射回原图坐标。需要注意的是,目标可能横跨切片边界,切分时要有一定重叠区域,否则边界处的车辆会被拦腰截断。重叠比例我用的是10%到20%,效果比较稳,但推理开销也会相应增加,所以一般只在离线分析时使用。
4.3 实时性优化与模型导出部署
如果项目要做到边缘设备或实时演示,模型导出是关键一步。YOLO官方库支持导出ONNX和TensorRT格式,导出后推理速度明显提升。ONNX导出的好处是跨平台,CPU上也可以用加速推理工具,缺点是某些自定义算子需要额外处理。TensorRT在NVIDIA显卡上速度最快,但环境配置比较麻烦,需要根据显卡算力选择版本。
我实测下来,ONNX推理速度比PyTorch原生模型快30%~50%,在1080P视频上如果只做隔帧推理,能跑出比较流畅的演示效果。导出时注意动态尺寸设置,如果固定成640输入,后续想改分辨率还要重新导出,不太灵活。如果答辩要求里写了“部署到嵌入式设备”,那就要提前预留模型量化的时间,TensorRT的FP16量化能进一步压缩显存占用和延迟,不过精度会有小幅下降,需要自己多测几组视频确认可接受。
另外,如果想要更完整的演示效果,可以给检测结果加上跨帧跟踪和车流量统计。跟踪模块我建议直接用通用的跨帧关联逻辑,因为车辆在固定视角下运动轨迹相对简单,不需要引入太复杂的关联算法。每辆车分配一个持续ID,检测框跨帧保持稳定,再统计进出检测区域的数量,演示效果会非常直观,也算是在检测之外多了一个有价值的扩展点。
我个人在实际操作中的体会是,基于YOLOv8的监控视频车辆检测项目,真正的难点从来不在模型本身,而是数据准备和工程细节的串联。最后再分享一个小技巧:准备演示视频时,不要选场景变化太剧烈的片段,模型在不同光照下的表现波动本来就大,答辩现场很容易被随机帧打乱节奏。最好提前挑好白天、夜间、雨天各一段典型监控片段,多跑几遍确认输出稳定,再准备一两段难度较高的挑战片段作为补充说明。这样整个项目从技术实现到演示呈现都会完整很多,祝各位毕设顺利。