基于YOLOv8的陶瓷器鉴定系统:从数据集到部署全流程解析
2026/9/14 3:37:00 网站建设 项目流程

简介:这是一套基于YOLOv8的陶瓷器鉴定系统完整项目,面向计算机视觉、人工智能方向的毕业设计或课程设计场景。项目围绕目标检测任务展开,提供可运行的Python源码、完整数据集、可视化交互界面和部署教程,开箱即用。系统支持模型训练、视频检测与结果可视化,可输出混淆矩阵、F1曲线、精确率-召回率曲线、验证集预测结果及标签分布图等核心评估图表,方便在答辩环节直观展示实验过程与精度表现。同时内置多种预训练权重和训练/检测脚本,可切换不同模型进行对比实验,也便于二次开发。资料包共97个文件,以Python脚本为主,包含少量配置文件、模型权重和演示视频,整体仅24.21MB,轻量紧凑。当前已有29人学习,非常适合深度学习初学者进阶,也可直接作为本科毕设、课程设计或大作业的完整方案,按README指引即可快速搭建运行环境并复现结果。

1. 一个毕设级 YOLOv8 陶瓷器鉴定系统,到底要打通哪几件事?

陶瓷器鉴定通常是靠专家肉眼观察器型、釉色、纹饰和底足,主观性强,而且需要长期经验积累。如果把这个过程换成一个基于 YOLOv8 的陶瓷器鉴定系统,本质上是用目标检测算法完成两件事:在图片里定位陶瓷器物,再对器物进行分类或属性判定。这类系统最常见的落地场景是毕业设计和课程设计,图片数量不大、类别不多,但要求把训练、推理、可视化界面和部署串成完整链路。适合有一定 Python 基础、了解卷积神经网络但对 YOLO 不熟的人。下面从环境配置、数据集组织、模型训练、界面部署和最终验证五条线展开,每一步都给出可以照搬的命令和参数,也标注了毕设答辩时最容易翻车的位置。

2. 先把地基打牢:YOLOv8 环境配置与陶瓷数据集组织方式

2.1 YOLOv8 环境配置要走到哪一步才算成功

很多同学在跑 YOLOv8 时卡在第一步,不是代码不会写,而是环境里 torch、CUDA 和 numpy 的版本互相不对付。这里给出我一般会做的最简配置流程,尽量不牵扯发行版。首先建一个干净虚拟环境,避免把系统 Python 搞乱:

python -m venv yolo-env source yolo-env/bin/activate # Windows 下用 yolo-env\Scripts\activate pip install --upgrade pip pip install ultralytics

安装完成后不要急着训练,先用一行代码验证模型能不能加载。model=yolov8n.pt会从官方地址下载权重,如果下载超时,可以手动把权重文件放到当前目录再执行。这个命令同时完成权重下载、推理和结果保存,能跑通就说明基础依赖没有大问题。

yolo predict model=yolov8n.pt source=https://ultralytics.com/images/bus.jpg

接着检查 GPU 是否真的被 PyTorch 识别,这一步比看显卡型号更可靠。很多环境里pip install ultralytics会顺手装一个 CPU 版 torch,代码能跑但训练慢到让人怀疑人生。

import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU only")

如果显示False,先别急着找代码问题,回去重装 torch。常见做法是到 PyTorch 官网选一个和你的 CUDA 版本匹配的命令,重新安装torchtorchvision。我不建议自己编译源码,除非你对构建系统很熟。对 GTX 1660 Ti 这类 6G 显存级别的卡,跑 YOLOv8n 和 8s 都足够,8m 需要把 batch 调小才能塞进去。

提示:虚拟环境装好 ultralytics 和 torch 后,用pip freeze > requirements.txt固定版本。答辩时换一台机器,pip install -r requirements.txt就能复现,能省掉大量环境排查时间。

2.2 陶瓷器数据集怎么做成 YOLO 格式

YOLOv8 官方训练接口只认imageslabels分开的目录结构。拿到一套陶瓷器数据集后,你最先要做的是确认它是否长这样:

datasets/ceramic/ ├── data.yaml ├── train │ ├── images/ # 图片,jpg/png │ └── labels/ # 对应的txt,同名 └── val ├── images/ └── labels/

YOLO 的标签文件是纯文本,每行一个目标,格式为class_id x_center y_center width height,坐标和宽高都是相对图片尺寸归一化到 0~1 的小数。举例:一张 800x600 的图中,某个陶瓷器边界框左上角是 (200, 150),右下角是 (600, 450),类别 id 为 2,那么标签行是:

2 0.5 0.5 0.5 0.5

因为中心点是 ((200+600)/2/800, (150+450)/2/600) = (0.5, 0.5),宽是 (600-200)/800=0.5,高是 (450-150)/600=0.5。

如果数据集只有图片没有标签,需要人工标注。我一般用 labelImg,安装很直接,打开图形界面后指定图片目录和标签保存目录即可。它的快捷键不多,但足够完成陶瓷器框选任务。

pip install labelImg labelImg

标注陶瓷器时有个小技巧:不要只框“器物主体”,口沿、底足、裂纹这些局部特征如果在同类图片里差异很大,可以单独做类别,也可以统一作为整体目标,取决于你的鉴定目标。如果做“年代分类”,那么一整个器物就是目标框;如果做“真伪鉴别”,局部胎釉特征更有信息量,建议把口沿和底足也标注出来,模型通常能学到那些纹理差异。

同时要写好data.yaml,这是 YOLOv8 训练的唯一数据入口:

path: D:/datasets/ceramic # 或相对路径 train: train/images val: val/images names: 0: celadon # 青瓷 1: blue_white # 青花 2: enamel # 粉彩 3: zisha # 紫砂

注意names的 id 必须和标签文件第一列保持一致,顺序错一个类别,整个训练就等于在学错误映射。类别名最好用英文或拼音,中文类别名在部分版本 Ultralytics 绘图和部署时容易出编码问题。

2.3 数据集不足时先别急着 train

陶瓷器数据集很难像 COCO 那样动辄几万张,一般课程设计能有一两千张带标注图片已经很不错了。在这个量级下,直接开始训练容易过拟合。我会先做一个数据统计脚本,看看每个类别有多少目标框,判断是否严重不均衡:

from pathlib import Path from collections import Counter def count_labels(label_dir: str) -> dict: counter = Counter() for txt in Path(label_dir).glob("*.txt"): for line in txt.read_text(encoding="utf-8").splitlines(): if line.strip(): counter[int(line.split()[0])] += 1 return dict(counter) print(count_labels("datasets/ceramic/train/labels"))

如果某个类别只有几十个目标,而另一个有几千,训练出来的模型大概率偏向多数类。处理方法有几种:

  • 对少数类图片做平移、旋转、镜像,注意陶瓷器如果带朝代字样,翻转后字会反,这类样本不能镜像。
  • 用 YOLOv8 自带的数据增强,在训练时调高hsv_hhsv_sdegrees等参数,模拟不同光线和角度。
  • 如果少数类之间形态差异很大,优先补充该类别的新样本,而不是靠重采样硬凑。

下表是陶瓷器数据增强里值得优先调整的几项,YOLOv8 都支持在data.yaml里配置:

参数默认值说明
hsv_h0.015色相扰动,模拟不同釉色偏色
hsv_s0.7饱和度扰动,适应器物表面光泽变化
degrees0.0旋转角度,陶瓷器照片常存在轻微倾斜
translate0.1平移,增强器物在画面中的位置泛化
fliplr0.5水平翻转,注意文字和不对称纹饰

这里不推荐直接复制同一张图一百遍去“平衡数据集”,模型记住的是噪声,不是特征。增强参数也不是越大越好,翻转和旋转太狠会让模型学到错误的形变,反而把釉面反光当成关键特征。

3. 训练自己的 YOLOv8 陶瓷器模型:从默认参数到损失曲线诊断

3.1 选哪个 YOLOv8 变体:n/s/m/l 与硬件匹配

YOLOv8 官方提供五个尺度,参数量和推理速度差异很大。陶瓷器鉴定场景有一个特点:图片通常是正面、侧面的器物照片,背景可控,目标数量少,精度上限往往取决于数据标注质量而不是模型容量。YOLOv8 的 backbone 用 C2f 结构,相比老一代 CSP 结构,梯度分流到更多的子层,对釉面纹理和器物边缘这类细节特征更友好,这也是它在陶瓷器小数据集上仍然能取得不错效果的原因之一。

我一般按显存和部署目标选型。如果是在 16G 以上的 GPU 上做实验,直接用 8m 或 8l 训练,再蒸馏到 8s 部署;如果只有 6G 到 8G 显存,8s 是性价比最好的选择;如果最终要部署到 CPU 或老机器上,8n 值得先跑一版。

模型参数量(约)输入尺寸典型显存占用适用场景
YOLOv8n3.2M640约 1~2 GBCPU 可推理,快速验证
YOLOv8s11.2M640约 2~4 GB6G 显存训练友好
YOLOv8m25.9M640约 4~6 GB追求精度,需调小 batch
YOLOv8l43.7M640约 8~10 GB数据集大、精度优先
YOLOv8x68.2M640约 12 GB+较少用于毕设

表里的显存占用是 batch 为 1 的粗略估计,实际训练时要看batch大小。如果出现CUDA out of memory,先把 batch 减半,不要一上来就换小模型。陶瓷器类别通常只有几个到十几个,8n 都不至于欠拟合,关键是标签质量。官方网络结构图里能看到 C2f 后面接的是 SPPF 和 PAN-FPN,这部分决定了多尺度特征融合,而陶瓷器口沿和底足往往是多尺度目标,所以训练时imgsz不要压到 320,建议 640 起步。

3.2 一份可以直接训练陶瓷器数据的命令与参数

以 8s 为例,在数据集目录准备完成后,用命令行训练:

yolo train model=yolov8s.pt data=ceramic.yaml epochs=150 imgsz=640 batch=8 device=0 workers=4 optimizer=auto project=runs name=ceramic_s

这段命令里的参数对结果影响很大:

  • imgsz=640是内部缩放尺寸,不是输入原图必须 640x640,YOLOv8 会自动 letterbox。
  • batch=8建议按显存调整,显存小的减到 4。
  • workers=4在 Windows 上不要设太高,容易报 DataLoader worker 错误。
  • optimizer=auto会让 Ultralytics 自动选择优化器,省去手动调 SGD/AdamW 的麻烦。

如果你想在训练过程中动态观察,还可以加上plots=True,这样每个 epoch 结束后会自动生成标签图、预测图、混淆矩阵和损失曲线,全部存在runs/ceramic_s下。用 Python 脚本训练的好处是方便在训练前后做额外处理,比如按验证集表现自动换初始权重:

from ultralytics import YOLO model = YOLO("yolov8s.pt") results = model.train( data="ceramic.yaml", epochs=150, imgsz=640, batch=8, device=0, workers=4, optimizer="auto", project="runs", name="ceramic_s", patience=30, )

patience=30表示如果连续 30 个 epoch mAP 没有提升,自动停止训练,避免无意义地烧显卡。对课程设计来说,patience设成 20 到 30 是合理的,因为训练时间有限。C2f 结构和optimizer=auto配合时,PyTorch 会默认给不同层设置不同的学习率权重,这一点不需要手动调,但要注意如果自己改了lr0,最好同时看一下results.csv里的学习率变化曲线。

训练结束后,在runs/ceramic_s/weights/下会有last.ptbest.pt。无论最后效果如何,部署时只用best.ptlast.pt只用于断点续训。

3.3 从 results.csv 读训练状态:loss 曲线、mAP、过拟合

Ultralytics 在训练过程中会把每个 epoch 的指标写到runs/ceramic_s/results.csv,比直接跑 TensorBoard 更直观。列名基本包含train/box_losstrain/cls_losstrain/dfl_lossval/box_lossval/cls_lossval/dfl_lossmetrics/precision(B)metrics/recall(B)metrics/mAP50(B)metrics/mAP50-95(B)

我习惯写个小脚本,把 val 的 box_loss 和 mAP50 画在一张图上,判断训练是否健康:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/ceramic_s/results.csv") plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(df["epoch"], df["val/box_loss"], label="val box loss") plt.plot(df["epoch"], df["train/box_loss"], label="train box loss") plt.xlabel("epoch") plt.ylabel("loss") plt.legend() plt.title("box loss curve") plt.subplot(1, 2, 2) plt.plot(df["epoch"], df["metrics/mAP50(B)"], label="mAP50") plt.xlabel("epoch") plt.ylabel("mAP") plt.legend() plt.title("mAP50 curve") plt.tight_layout() plt.savefig("loss_curve.png", dpi=150)

典型的情况有三种:

  • train/box_loss一直下降,val/box_loss在某个 epoch 后回升,说明过拟合,需要减小模型尺寸、增加数据增强或提前用patience停止。
  • val/cls_loss下降缓慢但box_loss很低,说明模型对“器物质感”的定位没问题,但要更关注类别区分,可以检查分类分支或者增加判别性强的局部标签。
  • mAP50 高但 mAP50-95 低,说明检测框对精准边缘和 IoU 阈值敏感,常见于标注框不够贴边,需要回头修数据集。

很多人只看 mAP50,忽略了 mAP50-95。在陶瓷器检测里,如果后续要接尺寸测量或口沿位置评估,mAP50-95 更能反映框的贴合程度,应该把它的变化也纳入验收标准。

4. 给陶瓷器鉴定接上可视化界面:从结果解析到简单部署

4.1 一个可运行的推理封装类

训练好的模型要进界面,先封装成独立的预测类,这样不管后面接 Gradio、Flask 还是 PyQt,调用方式都一样。下面是我常用的写法:

from ultralytics import YOLO class CeramicDetector: def __init__(self, weights: str = "best.pt", conf: float = 0.35, iou: float = 0.45): self.model = YOLO(weights) self.conf = conf self.iou = iou self.class_names = self.model.names def predict(self, image_input): result = self.model.predict( source=image_input, conf=self.conf, iou=self.iou, verbose=False, )[0] boxes = result.boxes.xyxy.cpu().numpy() # 左上右下坐标 labels = result.boxes.cls.int().cpu().tolist() scores = result.boxes.conf.cpu().tolist() return boxes, labels, scores def result_table(self, image_input, class_names: list): boxes, labels, scores = self.predict(image_input) rows = [] for box, label, score in zip(boxes, labels, scores): rows.append({ "class": class_names[label], "score": f"{score:.2f}", "x_min": int(box[0]), "y_min": int(box[1]), "x_max": int(box[2]), "y_max": int(box[3]), }) return rows

result.boxes.xyxy返回的是像素坐标,不是归一化坐标,界面上直接画框很方便。后续如果要计算陶瓷器的长宽比、面积占比,也是在这个坐标基础上算。confiou从初始化就暴露出来,方便在界面里加两个滑块现场调节。

如果一张图里有多个陶瓷器,遍历labels时要注意类别索引和class_names对齐,否则展示出来会张冠李戴。推理封装类里尽量不要混入图像解码逻辑,因为 Gradio 传进来是 numpy 数组,Flask 传进来是文件流,PyQt 传进来是 QPixmap,解码应该由界面层负责。

4.2 用 Gradio 快速搭建可视化鉴定页面

Gradio 是最快的做法,几十行就能把“上传图片 + 显示结果”变成网页应用。对于毕设答辩,操作简单是硬指标,现场不需要安装任何客户端,浏览器打开就能用。

import gradio as gr from detector import CeramicDetector class_names = ["celadon", "blue_white", "enamel", "zisha"] detector = CeramicDetector("best.pt") def process(image, conf): import cv2 img = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) rows = detector.result_table(img, class_names) result = detector.model.predict( source=img, conf=conf, imgsz=640, device=0, verbose=False )[0] annotated = result.plot()[:, :, ::-1] # BGR转RGB return annotated, rows gr.Interface( fn=process, inputs=[ gr.Image(label="上传陶瓷器图片"), gr.Slider(minimum=0.1, maximum=0.9, value=0.35, step=0.05, label="置信度阈值"), ], outputs=[ gr.Image(label="鉴定结果"), gr.Dataframe(label="检测详情", headers=["class", "score", "x_min", "y_min", "x_max", "y_max"]), ], title="基于YOLOv8的陶瓷器鉴定系统", ).launch()

gr.Image默认传进来的图片是 RGB 的 numpy 数组,我用cv2.cvtColor转成 BGR 再走 Ultralytics 推理,因为 YOLO 在 OpenCV 约定下训练,输入 BGR 才能保证颜色空间一致。result.plot()返回的是带标注框的 BGR 图像,输出给 Gradio 前再转回 RGB,否则颜色会整体偏蓝。这个细节是很多界面“颜色不对”的元凶。

launch()不加参数时默认监听本机7860端口。如果要在服务器上展示,可以传server_name="0.0.0.0",注意先检查防火墙。下表是几种常见可视化方案的取舍,毕设通常选前两个之一:

方案优点缺点适用场景
Gradio代码量少,自带滑块和表格页面定制性差快速演示和答辩
Flask前后端分离,可套 HTML 模板需要写更多后端代码更像完整系统
PyQt桌面应用,离线可用打包体积大,跨平台成本高课程设计要求本地界面

4.3 如果想用 Web 页面部署:Flask 的最小实现

Gradio 方便但定制性差,有些指导老师希望界面看起来更像“系统”。这时候用一个 Flask 应用更合适,前端可以套现成的 HTML 模板,后端只暴露两个接口:上传图片、返回 JSON。

import base64 from io import BytesIO from flask import Flask, request, jsonify from PIL import Image from detector import CeramicDetector app = Flask(__name__) detector = CeramicDetector("best.pt") @app.route("/detect", methods=["POST"]) def detect(): file = request.files.get("image") if not file: return jsonify({"code": 400, "msg": "no image"}), 400 image = Image.open(file.stream).convert("RGB") rows = detector.result_table(image, detector.class_names) # 结果缩略图也可以返回base64,方便前端直接展示 annotated = detector.model.predict( source=image, verbose=False )[0].plot()[:, :, ::-1] buf = BytesIO() Image.fromarray(annotated).save(buf, format="JPEG") img_str = base64.b64encode(buf.getvalue()).decode("ascii") return jsonify({"code": 0, "rows": rows, "annotated": img_str}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=False)

这里的关键点是request.files.get("image")拿到的是临时文件对象,不要直接读全文再PIL.open,用file.stream可以避免大图撑爆内存。debug=False一定要保持关闭,否则 Flask 的代码热重载会在训练模型时反复加载两次,直接把显存打满。

Flask 方案的部署成本也很低,本地演示直接python app.py。正式一点可以托管到 Linux 服务器上用gunicorn -w 1 -b 0.0.0.0:5000 app:app启动。注意 worker 数量在这里不能大,每一个 worker 都会加载一份完整模型权重,4G 内存跑 4 个 worker 很容易 OOM。

5. 最后再补三道防线:陶瓷器检测的验证指标、ONNX 导出与边界样本

5.1 用yolo val得到 mAP 与混淆矩阵

训练完不要急着交。先在验证集上跑一次官方验证,拿到没有“界面背景干扰”的客观指标:

yolo val model=runs/ceramic_s/weights/best.pt data=ceramic.yaml project=final_eval name=ceramic_val

运行结束后,final_eval/ceramic_val/下会有confusion_matrix.pngPR_curve.pngF1_curve.png。陶瓷器领域最常见的混淆是青瓷和天青釉、粉彩和五彩这类釉色接近的类别,如果混淆矩阵里这两类互相污染,说明需要补充能区分釉料质感的细节标注,而不是继续堆叠训练轮数。我通常会把同一器物在不同角度、不同背景下的照片全部放进同一组,确保验证集没有出现过训练集中的同一器物,否则泛化性会被高估。

5.2 导出 ONNX,让部署环境不依赖 Python 版本

Ultralytics 的.pt权重依赖 PyTorch 环境,答辩或交付时如果对方的 GPU 驱动和 CUDA 版本不同,很容易跑不起来。把best.pt导出成 ONNX 是更稳妥的交付方式:

yolo export model=runs/ceramic_s/weights/best.pt format=onnx opset=12 dynamic=False imgsz=640

导出成功后会生成best.onnx。ONNX 格式可以用onnxruntime在只有 CPU 的机器上推理,也不需要安装 torch。如果目标机器是 NVIDIA 显卡且版本匹配,再考虑 TensorRT 导出;不匹配时反而容易因为算子不兼容折腾半天,我一般把 TensorRT 当作可选优化,不作为交付前提。

5.3 三类最容易翻车的边界样本

这里是我在跑分类检测系统时最常遇到的三类问题,提前写进排查清单里,比最后出问题再查代码快得多。

第一类是背景反射光。陶瓷器表面反光会在照片上形成高光区域,模型可能把高光当成器物边缘而框出一个错误区域。训练时加入光照扰动,或者推理时用带 alpha 的高光抑制预处理,都会有效果。

第二类是遮挡和叠放。博物馆或考古现场照片里,一个器物经常被另一个挡住一部分。YOLOv8 的框无法区分前景和背景,如果标注时把被遮挡的器物也完整框进去,box_loss会一直偏高。建议对遮挡目标只框可见部分,标签语义是“我能看到的部分”。

第三类是conf阈值设置过低。演示时如果用默认conf=0.25,模型会把很多低置信度背景框画出来,让界面看起来很不可靠。把界面里置信度滑块的默认值设为 0.4 或 0.45,再让用户在演示现场手动调低到 0.25 对比效果,反而能体现系统对阈值的敏感性。记住这个细节,比临时改模型代码更能在答辩现场救场。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询