ARTICLE DETAIL

资讯详情

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

YOLOv8文物识别系统:从数据标注到模型部署全流程解析

YOLOv8文物识别系统:从数据标注到模型部署全流程解析 简介面向计算机视觉相关专业学生与毕业设计开发者这套基于YOLOv8的考古文物识别系统是一套可直接运行的完整项目覆盖目标检测算法训练、评估与可视化展示可解决毕设选题落地难、代码调不通的常见问题。压缩包共97个文件以70个Python脚本、12个pyc缓存、4个模型权重pt、5个配置文件及说明文本等组成约24.21MBpy文件涵盖检测服务、数据处理、模型训练与可视化页面pt为训练好的权重配合mp4演示视频与README按部署说明即可上手。目前已有76人学习下载。压缩包内含源码、完整数据集、可视化界面和部署教程可产出核心指标曲线、混淆矩阵、F1曲线、精确率-召回率曲线、验证集预测结果与标签分布图适合答辩展示运行后即可复现也可在此基础上二次修改。1. 一个考古文物识别系统凭什么用 YOLOv8 来做如果你拿到的是“基于YOLOv8的考古文物识别系统”这个毕设项目包那它本质上是由三块拼起来的一个 YOLOv8 目标检测模型、一批带标注的文物图片、一个能选图出结果的可视化界面。这类项目最反直觉的地方在于模型不是卡点数据才是。公开的文物类目标注集少得可怜常见做法是每个类目自己凑一两百张图而 YOLOv8 在几百张小样本上就能训练出可演示的效果这也正是它在毕设和课设里被反复选择的原因。适合谁文博或计算机方向的毕业生以及想把“识别展品/文物碎片”做成馆内演示程序的同学。这篇按我能落地的方式从环境、数据、训练、界面到部署逐层讲清。2. 先把 YOLOv8 跑通CPU 环境搭建与最小推理命令2.1 先看推理链路你拿到的到底是个什么模型YOLOv8 不是黑匣子但很多人是当黑匣子用的。一张文物图片进去先按 640×640 做 letterbox 缩放保持宽高比并填充灰边然后进入 backbone 提取特征head 同时输出每个位置的类别概率和边界框回归值最后经过 NMS 去掉重复框剩下带置信度的预测结果。YOLOv8 的 head 是 anchor-free 的相比 YOLOv5 少了一套 anchor 先验参数训练时省心一点对边缘设备也更友好。你要做的毕设系统不需要改网络结构只需要准备数据、训练权重、把权重接进界面。真正值得花时间的是环境能不能一次配好以及训练数据干不干净。2.2 Ubuntu 20.04 或 Windows 下搭环境CPU 版也能跑不管你是 Ubuntu 20.04 还是 Windows我都建议先用 conda 建独立环境别把 ultralytics 装进 base 环境里后面版本冲突会让你想删掉重来。CPU 版本完全能跑推理只是训练慢。conda create -n yolo8 python3.10 -y conda activate yolo8 pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cpupython 3.10 是目前和 ultralytics 兼容最稳的版本3.11、3.12 在部分 Linux 机器上会碰到 opencv 或 numpy 的预编译问题。第二条 pip 命令用 PyTorch 官方的 CPU 索引避免把 CUDA 版 torch 装到没显卡的机器上白白多占 2GB 空间。有 N 卡的同学把最后一行换成 cu118 或 cu121 后缀即可但不装也能先跑通流程。2.3 用官方权重跑一次推理验证环境的最小命令环境配好后别一上来就训练先拿官方权重跑一次推理确认模型能加载、图片能输出。yolo predict modelyolov8n.pt source./test_pic.jpg devicecpu conf0.25 saveTrue或者用 Python 方式方便后面接进界面from ultralytics import YOLO model YOLO(yolov8n.pt) results model.predict(sourcetest_pic.jpg, conf0.25, saveTrue) print(results[0].boxes)第一次执行会自动下载 yolov8n.pt 权重如果下载不下来找同事拷贝一份放到项目根目录就行权重文件很小。conf0.25 表示只保留置信度高于 25% 的框saveTrue 会在 runs/detect/predict 下生成带框的结果图。这一步跑通说明环境没问题后面训练、界面、部署都建立在这个基础上。2.4 没显卡怎么办CPU 推理参数与 GTX 1660 Ti 的取舍CPU 推理一张 640 文物图片大约 2 到 5 秒做成毕设演示完全能接受。训练就不一样了CPU 一个 epoch 可能跑几分钟甚至十几分钟不建议硬扛。常见配置是 GTX 1660 Ti 6G 显存这个卡跑 YOLOv8s 足够。我的习惯是显存 6G 用 yolov8sbatch 设 8显存 8G 用 yolov8mbatch 设 84G 以下老老实实用 yolov8n。显存不足时优先减 batch而不是换更小模型因为小模型在文物这类类间差异小的数据上精度下降会很明显。3. 文物数据集从哪来采集、标注与 YOLO 格式转换3.1 文物图片怎么凑公开图库、博物馆拍摄与类目设计公开的文物目标检测数据集非常少常见做法是三个渠道混着来博物馆官网的高清展品图、文物数字资源平台的公开图集以及自己拍摄的实物或翻拍画册。别只盯着一类青铜器要尽量覆盖你系统要识别的全部类目。类目设计是第一个坑。比如“青铜鼎”和“青铜簋”外轮廓相似模型很容易混。我的建议是控制类目在 8 到 12 类之间每类至少 150 到 300 张。如果两类的照片连人眼都分不清就合并成一个粗类比如“青铜礼器”。毕设系统的演示效果比类目数量更重要。图片收集时注意背景多样性。全挑白背景博物馆图模型学到的可能只是“白底加器物”而不是器物本身。多留一些展柜反光、偏斜角度、部分遮挡的照片这些才是真实场景。3.2 Labelme 手动标注多边形还是矩形文物外形不规则用 Labelme 画多边形比矩形框更贴近真实轮廓但 YOLO 训练只需要矩形框。常见做法是 Labelme 里画多边形转换时取多边形外接矩形如果器物密集排列、互相遮挡直接画矩形框更快。安装很简单pip install labelme打开后选 open dir 指向图片文件夹用 create polygons 或 create rectangle 标注。每张图的标注结果是一个同名 json 文件里面记录 imageWidth、imageHeight 和 shapes 数组。标注完成后要做的不是直接把 json 丢给 YOLO而是转成 txt 格式。3.3 转成 YOLO txt转换脚本与四个边界坑import json import os import glob from PIL import Image CLASS_NAMES [bronze_ding, pottery_jar, jade_disc] # 固定顺序训练时与 yaml 保持一致 def labelme_to_yolo(json_path, out_dir, class_names): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] base_name os.path.splitext(os.path.basename(json_path))[0] lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue cls_id class_names.index(label) pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # YOLO 格式class cx cy w h全部归一化到 [0,1] cx ((x_min x_max) / 2.0) / img_w cy ((y_min y_max) / 2.0) / img_h bw (x_max - x_min) / img_w bh (y_max - y_min) / img_h # 越界裁剪防止标注点在图像边缘外时产生 1 的宽高 cx max(0.0, min(cx, 1.0)) cy max(0.0, min(cy, 1.0)) bw max(0.0, min(bw, 1.0)) bh max(0.0, min(bh, 1.0)) lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, base_name .txt) # 即使没有有效标注也要生成空 txt否则训练端会警告标签文件缺失 with open(out_path, w, encodingutf-8) as f: f.write(\n.join(lines)) # 批量转换 os.makedirs(labels, exist_okTrue) for jp in glob.glob(json_files/*.json): labelme_to_yolo(jp, labels, CLASS_NAMES) print(converted:, os.path.basename(jp))这里聚集了四个最容易翻车的点。第一归一化坐标要用 json 里记录的 imageWidth/imageHeight不能直接用图片文件尺寸Labelme 有时会记录缩略图尺寸两者不一致会导致框全部偏移。第二yaml 里的类别顺序必须和这里 CLASS_NAMES 完全一致类别 id 从 0 开始顺序错了模型学到的名字全是错的。第三如果一个框跨图片边缘算出来宽度可能大于 1训练时边界框坐标会异常所以要 clip 到 [0,1]。第四txt 文件名必须和图片主文件名完全一致否则图片找不到对应标注训练会跳过它。3.4 数据划分随机之外还要防泄漏YOLO 训练需要 images/train、images/val 和 labels/train、labels/val 四个目录图片和 txt 一一对应。常规做法是 8:2 随机划分但要注意一个隐藏问题如果你从同一张文物图里裁剪出多张局部图它们必须进同一个集合否则模型在验证集上会“见过”训练集的同源样本mAP 虚高答辩时被问到就会露馅。import os import random import shutil files os.listdir(images) random.seed(42) random.shuffle(files) train_files files[: int(len(files) * 0.8)] val_files files[int(len(files) * 0.8):] for split, split_files in [(train, train_files), (val, val_files)]: os.makedirs(fdatasets/wenwu/images/{split}, exist_okTrue) os.makedirs(fdatasets/wenwu/labels/{split}, exist_okTrue) for f in split_files: base os.path.splitext(f)[0] shutil.copy(fimages/{f}, fdatasets/wenwu/images/{split}/{f}) shutil.copy(flabels/{base}.txt, fdatasets/wenwu/labels/{split}/{base}.txt)random.seed 固定下来保证每次复现同一个划分结果。注意 labels 里可能存在没有对应 txt 的图片划分前先检查一遍把缺标注的图片剔除否则训练时静默跳过你都不知道模型少学了几张图。4. 训练自己的文物检测模型参数、损失曲线与权重选择4.1 数据配置 YAML路径、类别名与 id 对齐训练前要写一个数据 yamlYOLOv8 通过它找到图片目录和类别名。这里我吃过亏path 用相对路径时换个终端就找不到数据集直接写绝对路径最稳。path: /home/user/projects/wenwu/datasets/wenwu train: images/train val: images/val names: 0: bronze_ding 1: pottery_jar 2: jade_discpath 指向数据集根目录train 和 val 是相对 path 的图片目录。yaml 里的 names 顺序必须和第 3 章转换脚本里的 CLASS_NAMES 一致。这个文件放哪都行训练命令里用绝对路径引用它。4.2 模型尺寸选择n、s、m 怎么选模型体积6G 显存可否训练推荐场景yolov8n最小可以batch 16CPU 推理、快速验证流程yolov8s小可以batch 8毕设主力精度和速度均衡yolov8m中勉强batch 4类别差异极小时用yolov8l大不建议显存不足训练太慢文物数据样本少通常几百张我的经验是从 yolov8s 起步。n 模型在类间相似度高时误检明显m 模型在 6G 卡上训练要频繁处理 OOMs 是最稳的平衡点。如果你的数据只有一百张以内那就用 n避免过拟合。4.3 训练命令与关键参数epochs、imgsz、batch、lryolo train \ modelyolov8s.pt \ data/home/user/projects/wenwu/wenwu.yaml \ epochs100 \ imgsz640 \ batch8 \ device0 \ patience20 \ project./runs \ namewenwu_trainmodelyolov8s.pt 是预训练权重会从 COCO 的 80 类知识迁移过来比从零训练收敛快得多。epochs 设 100配合 patience20即验证集指标连续 20 轮不提升就自动早停防止无效的空转。imgsz 保持 640和预训练权重一致改大反而可能掉点。batch8 是 6G 显存的稳妥值显存不够就先减到这个值的 1/2。device0 表示第一张显卡CPU 训练改成 devicecpu但要有心理准备100 轮可能要跑十几个小时。学习率一般不用动YOLOv8 默认的 lr00.01 在小数据集上是够用的。如果你发现 loss 曲线剧烈震荡可以调低到 0.005。权重衰减、动量这些参数按默认即可在小数据集上乱调这些只会增加玄学。4.4 看损失曲线判断状态画出来的才是真相训练过程中 YOLOv8 会把每个 epoch 的指标写进 runs/wenwu_train/results.csv我习惯每训练几轮就把它画出来看一次不要等训练完才后悔药。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/wenwu_train/results.csv) plt.figure(figsize(10, 6)) plt.plot(df[epoch], df[train/box_loss], labeltrain_box_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.legend() plt.savefig(wenwu_train_loss.png)正常的特征是 train/box_loss 前 20 轮快速下降之后缓慢收敛val/box_loss 和 mAP50 同步稳定。如果 train loss 一直降但 val loss 回升是过拟合说明数据量撑不起模型容量换小模型或加数据增强。如果 loss 变成 nan第一反应是检查数据里有没有全零的标注文件或者学习率是不是被调太高。4.5 选哪个权重best.pt 与 last.pt 的差别训练结束 runs/wenwu_train/weights 下会有 best.pt 和 last.pt。best.pt 是验证集 mAP 最高的一版用于演示、评估、转 ONNXlast.pt 是最后一轮的结果主要用于训练中断后续训。我的习惯是答辩演示一律用 best.pt并且把 best.pt 改名成更容易调用的名字复制到项目根目录避免到时候路径找不着。5. 可视化界面与部署避坑从检测脚本到可交付的毕设系统5.1 界面技术选型PyQt5、Streamlit 还是 Flask方案形态打包难度适用场景PyQt5桌面程序中可用 PyInstaller要求 exe 交付的毕设StreamlitWeb 页面低现场演示、局域网访问FlaskWeb API低前后端分离、手机端调用毕设最稳的是 PyQt5因为最后能交一个独立 exe双击就能跑。Streamlit 做演示最快十几分钟就能把上传图片、显示检测框串起来适合时间紧先交差但老师如果要求“系统”而不是“网页”还是得回到桌面方案。Flask 适合你已经想好了要扩展到手机端或展示端的情况。5.2 PyQt5 接入 YOLOv8 的最小代码加载模型、选图、显示结果import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QVBoxLayout, QFileDialog from PyQt5.QtGui import QPixmap, QImage from ultralytics import YOLO model YOLO(best.pt) # 在界面启动前先加载模型 class MainWindow(QWidget): def __init__(self): super().__init__() self.setWindowTitle(文物识别系统) self.label QLabel(请选择图片) self.btn QPushButton(选择图片) self.btn.clicked.connect(self.select_image) layout QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.btn) self.setLayout(layout) def select_image(self): path, _ QFileDialog.getOpenFileName(self, 选择图片, , Images (*.jpg *.png *.jpeg)) if not path: return # 推理结果默认是 RGB 格式转成 QImage 显示 results model.predict(path, conf0.25, imgsz640, devicecpu) img results[0].plot() # 已经画好检测框的 BGR 图像 rgb_img img[:, :, ::-1] # BGR 转 RGB h, w, c rgb_img.shape qimg QImage(rgb_img.data, w, h, c * w, QImage.Format_RGB888) pix QPixmap.fromImage(qimg).scaled(self.label.width(), self.label.height()) self.label.setPixmap(pix) if __name__ __main__: app QApplication(sys.argv) win MainWindow() win.show() sys.exit(app.exec_())这段代码为了让逻辑直接推理与界面放在同一个线程演示没问题但选大图时界面会卡一两秒。要上档次就加一个 QThread 处理推理结果通过信号回传。plot() 返回的图像是 BGR 顺序不转 RGB 直接显示颜色会偏蓝这是最常见的界面显示翻车点。5.3 打包成 exe三个关键步骤打包最好在干净的 conda 环境里做不要在装了一堆包的系统环境里打包否则 exe 体积会很夸张。pip install pyinstaller pyinstaller -F -w --name wenwu_gui main.py --hidden-import cv2 --hidden-import torch-F 打成一个单文件-w 去掉控制台黑窗口。打包完成后把 best.pt 复制到 exe 同目录下程序启动时用绝对路径调YOLO(best.pt)或YOLO(./best.pt)。不建议把权重打进 exe 内部那样每次换模型都要重新打包一次而且 pyinstaller 解压临时目录的路径处理会让你多踩一个坑。首次启动慢是正常的因为 PyInstaller 单文件模式要把全部依赖解压到临时目录。如果杀毒软件误删把它加入白名单即可这不是代码问题。5.4 常见部署翻车现场现象、原因与解决界面打开后点选图片直接卡死。原因是推理在 UI 主线程同步执行大图推理期间界面事件循环被阻塞。解决办法是把 predict 放进 QThread推理完通过 signal 把结果图传回主线程更新 QLabel。这是毕设答辩时最容易暴露的问题。exe 提示找不到 best.pt。原因是程序在 exe 解压临时目录运行相对路径找不到当前目录下的模型文件。解决办法是启动时先取 exe 所在目录绝对路径再拼接模型路径不要依赖 os.getcwd()。打包后体积巨大或提示缺少 opencv 依赖。原因是 PyInstaller 默认静态分析漏掉了 cv2 的隐式导入。解决办法是打包命令里显式加--hidden-import cv2 --hidden-import torch如果还报错用--collect-all ultralytics把 ultralytics 的资源和子模块全部收进去。摄像头或视频画面全黑推理却有结果。原因多数是 OpenCV 的 VideoCapture 与 PyQt 的图像格式转换没对齐。解决办法是先用cap cv2.VideoCapture(0)单独测试再逐帧把 BGR 转 RGB再转 QImage 显示。不要上来就合并写。同一个 best.pt服务器上效果好本机部署后漏检严重。原因往往是推理时 imgsz 和训练时不一致或者图片有 EXIF 旋转信息没被处理。解决办法是作推理代码里固定imgsz640并用 PIL 打开图片后再转 numpy把 EXIF 问题提前消化掉。6. 验证效果与进阶技巧评估、混淆矩阵与边缘部署6.1 用模型自带的验证命令看 mAP训练结束只贴 loss 曲线还不够要跑一次正式的验证得到 mAP 数值。用下面这段代码把 best.pt 在验证集上的表现量化出来。from ultralytics import YOLO model YOLO(runs/wenwu_train/weights/best.pt) metrics model.val(data/home/user/projects/wenwu/wenwu.yaml, imgsz640, splitval) print(mAP50:, metrics.box.map50) print(mAP50-95:, metrics.box.map)对文物识别这种类目少、样本也少的小任务mAP50 比 mAP50-95 更能说明演示效果mAP50 到 0.7 以上就足够撑起一个毕设系统。6.2 混淆矩阵与漏检处理验证跑完后会在 runs/detection/val 下生成混淆矩阵图重点看哪两个类互相误检。如果“青铜鼎”和“青铜簋”混得很厉害说明这两个类的训练样本外观太接近要么补样本要么合并类目。别试图靠调参解决类间混淆这是数据问题不是模型问题。6.3 转 ONNX 做边缘部署进阶方向如果以后想把这套东西跑在 RK3588 这类边缘设备上先转 ONNX 验证精度是否保留。yolo export modelbest.pt formatonnx imgsz640转完用 onnxruntime 加载跑一遍验证集对比原权重 mAP 是否明显下降。正常下降幅度很小如果掉点明显先检查输入尺寸和归一化方式。进一步转 RKNN 时再做 int8 量化校准这一步才是精度丢失最多的地方需要拿一批真实场景图做校准集否则模型基本废掉。我的习惯是把验证集的 mAP 结果、混淆矩阵截图全部留在 runs 目录里答辩时直接展示这比任何口述都有说服力。希望帮到你。本文还有配套的精品资源点击获取
返回列表