ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv8与PyQt5的水下垃圾生物检测系统开发实战

基于YOLOv8与PyQt5的水下垃圾生物检测系统开发实战 水底海底垃圾生物探测器检测识别系统简单说就是用 YOLOv8 做目标检测再用 PyQt5 搭一个可视化的桌面程序让水下摄像头、图片和视频素材能够实时识别垃圾和生物。这个项目在海洋环保、水产养殖、水下巡检、科研采样这些场景里很实用也是很多深度学习初学者想动手练一遍的典型完整项目。最值得关注的点不是“能跑通”而是整个链路数据集怎么准备、模型怎么训练、界面怎么集成、检测结果怎么展示和导出、遇到性能问题怎么排查。下面我会按实际开发顺序拆开讲尽量把我踩过的坑和判断标准写出来方便你照着复现也方便你自己改造成更完整的系统。1. 先明确系统需求再把任务拆成四个模块做这类系统最怕一上来就写代码。先把需求定清楚后面才不会反复返工。一个完整的水底垃圾生物检测识别系统通常包含四个模块数据输入、模型推理、界面展示、结果管理。数据输入要支持三种常见来源单张图片、本地视频文件、摄像头实时画面。图片和视频适合离线分析摄像头适合实时巡检。界面展示要能显示原始画面、检测框、类别标签、置信度还要有统计信息比如当前帧共检测到几个垃圾、几个生物每种目标的计数。结果管理需要把检测结果保存下来可以是图片、视频或日志表。这个系统适合谁呢一类是做海洋环保项目的人需要自动识别水下垃圾一类是做水产养殖或水下生态监测的人需要统计鱼类、贝类等生物数量还有一类是学习目标检测和桌面应用开发的学生想找一个能体现全栈能力的项目。无论哪种场景YOLOv8 负责“看得懂图像”PyQt5 负责“让人看得懂结果”两者配合刚好覆盖了从算法到产品的完整闭环。1.1 为什么不直接只用命令行非要加 PyQt5很多人训练完 YOLOv8 就停在命令行输出结果。命令行能跑但给非技术人员用不了。PyQt5 的价值是把推理过程封装成一个小软件不需要写代码打开就能选图片、看视频、开摄像头结果直接显示在窗口里。如果你以后要接水下机器人还可以在这个界面里加控制按钮、记录日志、回传数据。PyQt5 另外一个实用点是可以做实时视频流显示。OpenCV 的cv2.imshow在简单测试里够用但要做多窗口、控件、参数调节、结果列表PyQt5 成熟得多。而且 PyQt5 跨平台Windows 和 Linux 都能跑打包成 exe 也方便。1.2 技术选型时要考虑的三个实际条件第一是 GPU。YOLOv8 训练必须用 NVIDIA 显卡显存至少要 6GB 才舒服GTX 1660 Ti 这种 6GB 卡可以训练 YOLOv8n 和 YOLOv8s但需要调小 batch size。如果没有独显也可以跑 CPU 训练但速度慢很多只适合跑很小的数据集熟悉流程。第二是 Python 版本。建议用 Python 3.8 到 3.10PyQt5 和 ultralytics 包在这几个版本上兼容性最好。最新的 Python 3.12 有一些依赖编译问题不必赶新。第三是系统环境。Windows 下要注意安装 Microsoft C Build Tools因为有些依赖需要本地编译。如果是 Linux建议用 conda 创建虚拟环境避免把系统 Python 弄乱。2. 准备数据集不要只关注标注数量更要关注类别分布和水下图像质量水下检测和普通路面检测不一样水下图像普遍存在偏色、模糊、光照不均匀、悬浮颗粒多的问题。如果直接拿普通目标检测数据集训练泛化效果会很差。所以数据集的质量直接决定系统的上限。2.1 数据集来源和标注格式公开数据集方面可以找水底垃圾、海洋垃圾、水下鱼类相关的数据集。常见的有 TrashCan、Trash-ICRA 这类水下垃圾数据集也有 Fish4Knowledge、DeepFish 这类鱼类数据集。不过这些数据集格式不一定直接是 YOLO 格式可能需要自己转换。如果你有自己的水下视频素材建议自己标注。标注工具用 LabelImg 或 LabelStudio 都可以。YOLOv8 使用的是 txt 格式标签每行代表一个目标class_id x_center y_center width height其中 x_center、y_center、width、height 都是归一化到 0 到 1 的坐标值。标注完成后目录结构建议整理成这样dataset/ images/ train/ val/ labels/ train/ val/YOLOv8 支持这种标准结构也支持一个data.yaml文件来指定路径和类别。比如path: D:/water_trash_dataset train: images/train val: images/val nc: 2 names: [trash, biota]这里类别要根据你的实际需求来定义。如果还想细分鱼类、塑料袋、渔网、玻璃瓶可以继续增加类别但每个类别的样本量要足够否则模型容易漏检。2.2 水下图增强策略水下图像对比度低、颜色偏蓝绿色训练时不要直接套用通用数据增强要先做图像预处理。常见做法是使用直方图均衡化、白平衡校正、对比度增强。这些操作可以在训练前做也可以在数据加载时做。YOLOv8 自带的增强参数有 hsv_h、hsv_s、hsv_v、translate、scale、fliplr 等。对水下场景可以适当调高 hsv_s 和 hsv_v模拟不同光照条件。但不要一上来就开满增强否则模型训练不稳定。我一般先使用默认增强跑几十轮看 loss 下降情况再逐步调整。有一个经验水下图像经常有大量相似背景比如沙地、岩石、水草。如果训练集和验证集来自同一段视频的不同帧很容易过拟合。建议按视频片段切分数据而不是按帧随机切分这样验证集更真实。2.3 类别不平衡怎么处理水下垃圾中“塑料瓶”可能很多但“废旧渔网”很少模型会偏向多数的类别。处理办法有三个优先级先尝试增加少数类样本比如从不同视频里截取更多帧如果样本不够再选择降低置信度阈值或者使用加权损失最后才考虑在线增强去复制少数类样本注意不要复制太多导致重复学习。判断类别平衡的方法很简单统计每类目标在训练集中的数量最少类别数量不应该低于最多类别的 20%。如果差太多要么补数据要么合并相似类别。3. YOLOv8 环境安装和训练流程这个环节是大多数人最容易卡住的地方。我先给出一个稳妥的安装顺序再解释训练参数怎么调。3.1 环境安装顺序第一步创建虚拟环境conda create -n yolo8 python3.9 -y conda activate yolo8第二步安装 PyTorch。如果你的机器有 NVIDIA GPU先看显卡驱动支持的 CUDA 版本然后安装对应版本的 PyTorchpip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果没 GPU安装 CPU 版也可以只是训练会很慢。判断 torch 是否能用 GPUimport torch print(torch.cuda.is_available())输出 True 表示可用。第三步安装 YOLOv8 依赖pip install ultralytics这个过程会自动安装 OpenCV、matplotlib、pandas 等常用库。第四步安装 PyQt5pip install pyqt5 pyqt5-tools如果遇到 pyqt5 安装失败通常是 Python 版本太高或者缺少 C 编译工具。可以试试换成 Python 3.9。3.2 训练命令和参数解释YOLOv8 训练最简单的命令是yolo train datadata.yaml modelyolov8n.pt epochs50 imgsz640 batch8这个命令会从默认权重yolov8n.pt开始训练。yolov8n是 nano 版本文件最小、速度最快适合低配置机器。如果显存足够可以用yolov8s.pt或yolov8m.pt精度更高但推理更慢。epochs训练轮数我一般先设 50 轮验证流程再根据 loss 是否收敛增加。imgsz输入图像尺寸640 是默认值如果你检测的目标很小可以调到 960 或 1280但要注意显存和推理速度。batch批大小取决于显存。GTX 1660 Ti 6GB 用 batch8 跑 yolov8n 没问题改用 yolov8s 就要降到 4。训练过程中要关注两个文件runs/train/exp/下的results.png和weights/best.pt。best.pt是在验证集上精度最好的权重作为最终使用版本。3.3 训练效果怎么判断不要只看准确率还要看 loss 曲线和 PR 曲线。YOLOv8 训练日志里会有box_loss、cls_loss、dfl_loss这三项。如果 loss 持续下降并趋于平缓说明训练正常。如果 loss 震荡很大可能是学习率太高或者 batch size 太小。验证集上的mAP50大于 0.8 算不错如果只有 0.5 到 0.7说明还需要调。还要看每个类别的mAP50有些类别高有些低需要针对性补数据或调阈值。如果训练轮数不够模型可能欠拟合轮数过多则可能过拟合。判断过拟合的方法是看训练集 loss 还在降但验证集 mAP 不再上升甚至下降这时要早停。YOLOv8 默认会保存 best.pt所以不用担心最后参数不对。3.4 训练时常见报错处理显存不足CUDA out of memory时优先降低 batch size比如从 8 降到 4 或 2。也可以降低 imgsz从 640 降到 512。如果还不行改用更小的模型比如 yolov8n。数据集路径错误是最常见的。YOLOv8 要求 data.yaml 里的路径可以是绝对路径也可以是相对路径但相对路径默认基于当前工作目录。建议用绝对路径避免运行目录不一样导致读取失败。如果训练结果都是空白框先检查标注文件。用 Python 读一个 txt 标签确认坐标值在 0 到 1 之间类别 ID 在类别列表范围内。4. PyQt5 界面设计从单线程到多线程避免卡死PyQt5 界面最核心的问题不是画控件而是实时检测时界面卡顿。如果你在按钮点击事件里直接加载模型、循环读取视频并做推理窗口会直接无响应因为主线程被推理过程占住了。4.1 整体界面布局一个可用的检测界面至少包含这些区域左侧是图像显示区域右侧是控制面板。控制面板里放“打开图片”“打开视频”“打开摄像头”三个按钮再加一个“开始检测”和“停止”按钮下方放检测结果列表或统计标签比如“当前帧垃圾 2生物 5”。下面是一个简化版窗口布局伪代码class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(水下垃圾生物检测系统) self.image_label QLabel() self.open_img_btn QPushButton(打开图片) self.open_video_btn QPushButton(打开视频) self.open_camera_btn QPushButton(打开摄像头) self.start_btn QPushButton(开始检测) self.stop_btn QPushButton(停止) self.result_label QLabel(未检测) # 布局略使用 QVBoxLayout 和 QHBoxLayout打开图片后用QFileDialog.getOpenFileName选择文件再用cv2.imread读图。但 OpenCV 读进来的是 BGR 格式显示到 PyQt5 的 QLabel 里要先转换成 RGB再转换成 QImage。def show_image(self, cv_img): rgb_img cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB) h, w, ch rgb_img.shape bytes_per_line ch * w q_img QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888) self.image_label.setPixmap(QPixmap.fromImage(q_img))注意如果图像较大QLabel需要设置缩放模式。默认是左上角直接裁剪最好设置setScaledContents(True)或自己算缩放比例避免拉伸变形。4.2 多线程处理视频流视频检测不能把推理放在主线程。正确做法是创建QThread子类在线程的run方法里读取视频帧并调用模型推理然后通过信号把结果图像传给主线程更新界面。定义一个工作线程class DetectThread(QThread): frame_signal pyqtSignal(object) def __init__(self, model, source): super().__init__() self.model model self.source source self.running True def run(self): cap cv2.VideoCapture(self.source) while self.running and cap.isOpened(): ret, frame cap.read() if not ret: break results self.model(frame) annotated results[0].plot() self.frame_signal.emit(annotated) cap.release()主线程连接信号self.detect_thread DetectThread(self.model, 0) self.detect_thread.frame_signal.connect(self.update_image)这里self.source如果是摄像头索引比如 0就表示默认摄像头如果是视频文件路径就读取本地视频。要注意的一点是线程停止逻辑。不要在按钮里直接强制终止线程正确做法是设置self.running False然后等线程自然退出。4.3 PyQt5 中的几个常见坑第一个是“下拉框闪退”或“界面无响应”经常是因为在子线程里操作了主线程的 UI 控件。解决办法所有 UI 操作只允许在主线程的信号槽里完成不要在子线程直接访问QComboBox.setCurrentText或QLabel.setText。第二个是摄像头无法打开。Windows 下如果摄像头被其他软件占用会打不开。可以先关掉其他软件再用cap.isOpened()判断是否成功。第三个是视频播放进度条没有或者检测延迟高。原因通常是推理时间超过帧间隔。普通摄像头 30 帧每秒如果推理需要 100 毫秒那就是 10 帧每秒画面会掉帧。解决办法是降低输入图像尺寸或者只在每两帧中取一帧做检测。第四个是模型加载时间太长。YOLOv8 模型加载一般需要几秒建议在程序启动时就加载而不是每次点“开始”才加载。加载完成后可以往状态栏输出一条提示避免用户觉得程序卡死。5. 模型推理集成输入输出处理和生产化优化界面能跑通之后还要注意推理模块的输入输出规范。很多人模型放在一边界面放在另一边结果是一张图片检测完想保存标注信息却拿不到坐标。5.1 YOLOv8 的推理结果对象YOLOv8 的model(frame)返回一个Results对象里面包含多个属性boxes.xyxy检测框左上角和右下角的坐标格式是 tensor。boxes.conf每个框的置信度。boxes.cls每个框的类别 ID。names类别 ID 对应的名称。例如要拿到所有检测目标并绘图results self.model(frame) boxes results[0].boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() conf box.conf[0].cpu().numpy() cls int(box.cls[0].cpu().numpy()) label f{results[0].names[cls]} {conf:.2f}这里为什么要.cpu().numpy()因为推理时数据可能在 GPU 上必须移动到 CPU 才能转换成 numpy 数组。5.2 检测结果的保存和统计检测保存可以使用cv2.imwrite保存带标注框的图片。如果要保存坐标信息可以做成 CSVframe, class, confidence, x1, y1, x2, y2 001, trash, 0.92, 130, 55, 276, 198 001, fish, 0.85, 400, 122, 560, 340统计计数可以在推理线程内维护一个字典count_dict {} for cls in class_ids: name model.names[cls] count_dict[name] count_dict.get(name, 0) 1然后在信号里把这个字典一起发到主线程更新界面上的计数标签。5.3 提升推理速度的几个手段如果你的机器性能不够优先考虑以下几点使用更小的模型YOLOv8n 比 YOLOv8x 快几倍精度差不太多。降低推理分辨率imgsz从 640 降到 480速度提升明显。关闭不必要的预处理比如已经做过的图像增强不要重复做。使用torch.cuda.synchronize()测试实际推理耗时不要只凭感觉。如果视频输入源分辨率是 1920x1080可以先缩放成 1280x720 再推理因为缩放后的目标在 640 输入下更合适。这里还要注意model(frame)内部默认会把输入缩放到训练时的 imgsz。如果你传入更小的图像速度更快但小目标可能检测不到。所以要根据目标大小来平衡。5.4 模型验证与可信度判断模型做出来之后要拿一段没有参与训练的视频去验证而不是只看训练集精度。真实水下视频往往有运动模糊和遮挡模型检测置信度会比测试集低。这时可以把置信度阈值从默认的 0.25 降到 0.15但要注意误检也会增加。如果检测框频繁抖动可以用“帧间平滑”策略连续若干帧中同一目标的位置取平均值。这个改造不难但对视频检测效果改善很大。简单实现是先做目标跟踪再结合检测结果。不过 YOLOv8 原生不包含跟踪需要引入 ByteTrack 或自己写 IoU 匹配逻辑。如果只是学习可以先把检测做稳再加跟踪。6. 系统测试与典型案例排查系统整合完成后一定要按“图片-视频-摄像头-批量”这个顺序挨个测试。别直接上摄像头否则出问题很难分清是摄像头、模型还是界面卡住。6.1 测试用例设计测试项输入方式预期结果通过标准单张图片检测打开一张水下图片图片显示并画框目标类别正确置信度可接受界面不卡本地视频检测打开一段 MP4视频逐帧播放有检测框播放流畅不掉帧严重可以停止摄像头实时检测打开 0 号摄像头实时画面显示画面无严重延迟停止按钮有效批量图片检测选择文件夹自动遍历所有图片并保存结果输出文件命名无冲突失败有日志结果导出点击导出生成 CSV坐标和类别与显示一致6.2 五个高频问题排查顺序问题一打开摄像头黑屏或报错。 排查顺序先检查摄像头是否被占用再用 Python 单独执行cv2.VideoCapture(0)测试。如果 OpenCV 能打开说明是 PyQt5 线程或索引问题。如果 OpenCV 也打不开说明摄像头驱动或权限问题。问题二检测框位置不对。 排查顺序先看输入图像是否经过缩放或裁剪。PyQt5 显示时为了适配窗口会缩放但传给模型的应该是原始比例。如果图像从 QLabel 取出来再送进模型就会出错。正确做法是把原始frame送进模型只把结果绘制图拿去显示。问题三检测结果没有计数。 排查顺序先检查类别字典是否在每次循环时被重置。如果你把count_dict放在while循环外部每次检测应该更新同一个字典如果放在内部每次都重新计数自然无法累计。另外注意信号槽里的参数类型默认只能传 Python 对象传 dict 没问题但要确保没有其他线程同时修改这个 dict。问题四程序关闭时崩溃。 排查顺序线程退出清理没做好。关闭窗口时要先设置self.detect_thread.running False再wait()等待线程结束最后才释放摄像头和模型。如果直接sys.exit子线程还在占用摄像头就会报错。问题五打包成 exe 后模型路径找不到。 排查顺序PyInstaller 打包时不会自动包含.pt文件。你需要手动把模型文件放到打包目录或在代码中使用相对路径。还有一个坑ultralytics 在运行时需要使用一些资源文件PyInstaller 打包时可以用--add-data参数带上ultralytics包里的配置目录。如果不是特别需要建议先用--onedir模式打包方便补文件。6.3 性能调优的实际判断标准不要只看 FPS。如果视频检测掉帧先看推理耗时。在推理前后记录时间差import time t0 time.time() results model(frame) t1 time.time() print(f推理耗时: {t1-t0:.3f}s)如果推理耗时小于 0.1 秒理论上能到 10 FPS界面更新需要控制在 0.05 秒以内。如果耗时超过 0.3 秒就必须缩小 imgsz 或换小模型。摄像头实时检测的视觉流畅度底线是 15 FPS低于这个值操作体验会明显下降。内存占用方面批量检测时如果图片很多不要一次性把所有结果存进列表应该边检测边保存否则程序会越来越卡。可以用一个队列保存待处理图片路径处理完立刻写入磁盘释放内存。7. 系统扩展从桌面程序到边缘部署如果只是课程设计或毕业设计桌面程序已经够用。但如果想做成实际产品还可以考虑几个扩展方向。第一个是接入水下机器人或遥控潜水器。摄像头画面通过有线或无线传输到电脑PyQt5 界面里显示检测结果同时把检测到的目标坐标转换成机器人的控制指令。这个改造需要另加通信模块比如串口或 UDP 协议。第二个是批量离线分析。把水下视频录制好然后把视频文件导入系统自动识别并生成统计报表。这种场景不需要实时显示可以先把模型推理和 GUI 分开写成命令行工具再在 PyQt5 里调用。第三个是模型轻量化。如果最终要部署到 NVIDIA Jetson 或者 RK3588 这类嵌入式平台需要把 YOLOv8 模型转换成 TensorRT 或 RKNN 格式。转换时要确认目标平台的算子支持情况。这部分工作量和桌面端差别很大建议单独做一个项目。第四个是引入更先进的数据增强和注意力机制。热搜词里提到 “yolov8引入多头注意力机制MHSA”这属于模型改进方向。你可以在 YOLOv8 的 backbone 或 neck 中加入注意力模块但前提是先把基础版本跑通否则很难判断改进是否有效。我先不建议在初期就改模块先拿官方模型跑出 baseline再考虑涨点。8. 部署打包与用户体验细节PyQt5 程序写好后要给别人用通常要打包成 exe。PyInstaller 是最常用的工具但 YOLOv8 和 PyQt5 体积很大打包后可能超过 300MB。这是正常的不用太纠结。8.1 打包步骤建议pip install pyinstaller pyinstaller -F -w main.py --add-data best.pt;. --add-data data.yaml;.-F生成单个 exe-w表示不显示控制台窗口--add-data把模型和配置一起打包。注意 Windows 下分隔符是;Linux 下是:。打包后先测试两种启动方式正常双击启动以及在命令行里运行 exe。如果没有任何反应可能是缺少依赖库先用非-w模式打包一次看控制台报错。8.2 用户体验改进在界面底部加状态栏显示“模型加载完成”“检测中”“已暂停”。支持拖拽图片到窗口省去每次打开文件对话框。在“关于”对话框里写明模型训练时间和数据集来源方便后续维护。如果检测目标很少可以开启“只显示高置信度”选项避免画面太乱。这些都是小事但对实际使用者来说比花哨的算法改进更能提升满意度。9. 一些更个人的建议如果只允许我留一句话先做一个极简版本用 YOLOv8n 和十几张图片把整个流程跑通再逐步加数据和功能。很多人卡在“一定要先做一个完美数据集”上结果几周都在标注图片连模型长什么样都没见过。合理的节奏是先下载一个公开数据集训练一轮跑通命令行推理然后写一个最简单的 PyQt5 窗口能显示一张检测后的图片再把视频和摄像头加进去最后才整理自己的数据和调参。这样每一步都有反馈出了问题也好定位。训练时不要盲目追求 mAP 高几个点。水下检测场景更看重的是漏检率。如果“垃圾”被漏掉比误检一条鱼更严重。所以训练集里要多放一些背景复杂的图片把目标放在不同位置、不同光照下。验证时也要重点关注低置信度样例不要只看最高分的那一批。最后保存模型文件和训练参数时建议把 data.yaml、训练命令、每个类别的样本数一起保存到一个exp_info.txt里。后续换机器、换界面、换数据集时你会感谢当时的自己。这个项目最大的坑从来不是 YOLOv8 不够强而是中间过程没有记录改到后面自己都不知道模型是在什么数据上训练的。
返回列表