基于YOLOv8-seg的矿物颗粒分割完整技术文档
一、环境准备
1.1 依赖库安装
# 安装Labelme(数据标注工具,兼容Python 3.8)
pip install labelme==4.5.6
# 安装YOLOv8及核心依赖(含PyTorch、OpenCV等)
pip install ultralytics==8.3.20 opencv-python numpy pillow
1.2 工作目录结构(固定)
F:/animalrec/test/ # 根目录
├─ mineral_dataset/ # 数据集文件夹
│ ├─ annotations/ # Labelme标注JSON文件存放处
│ └─ train/ # YOLO训练集(自动生成)
│ ├─ images/ # 训练图像
│ └─ labels/ # YOLO格式标签
├─ yolov8x-seg.pt # YOLOv8-seg预训练模型(需提前下载)
├─ 1234.jpg # 待分割的矿物电镜图(示例图像)
├─ json2yolo.py # Labelme JSON转YOLO格式脚本
├─ data.yaml # YOLO数据集配置文件
├─ train_mineral.py # 模型训练脚本
└─ infer_mineral.py # 推理与颗粒提取脚本
1.3 预训练模型下载
从官方链接下载yolov8x-seg.pt,放入根目录:
https://github.com/ultralytics/assets/releases/download/v8.3.0/yolov8x-seg.pt
二、数据标注(Labelme)
2.1 启动Labelme
# 1. 打开PowerShell,切换到根目录
cd F:/animalrec/test
# 2. 启动Labelme
labelme
2.2 标注步骤
- 点击菜单栏「File」→「Open」,选择根目录的
1234.jpg; - 点击左侧工具栏「多边形图标」(快捷键
P),沿矿物颗粒边缘点击生成顶点(边缘复杂处多点击); - 双击闭合多边形,弹出「Label」输入框,输入
mineral,点击「OK」; - 点击菜单栏「File」→「Save」,选择
mineral_dataset/annotations/,保存为1234.json(与图像同名)。
三、Labelme JSON转YOLO格式(关键步骤)
3.1 创建转换脚本(json2yolo.py)
import json
import numpy as np
import cv2
import base64
import os
from labelme import utils
# 路径配置(无需修改,匹配工作目录)
json_path = "mineral_dataset/annotations/1234.json"
output_dir = "mineral_dataset/train"
os.makedirs(output_dir, exist_ok=True)
os.makedirs(os.path.join(output_dir, "images"), exist_ok=True)
os.makedirs(os.path.join(output_dir, "labels"), exist_ok=True)
# 读取JSON并解码图像
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
image_bytes = base64.b64decode(data["imageData"])
image_np = np.frombuffer(image_bytes, dtype=np.uint8)
img = cv2.imdecode(image_np, cv2.IMREAD_COLOR)
# 保存图像到train/images
img_save_path = os.path.join(output_dir, "images", "1234.jpg")
cv2.imwrite(img_save_path, img)
print(f"图像已保存:{img_save_path}")
# 转换标注为YOLO格式(归一化坐标)
img_h, img_w = img.shape[0], img.shape[1]
yolo_labels = []
for shape in data["shapes"]:
if shape["label"] == "mineral":
points = shape["points"]
normalized = []
for x, y in points:
normalized.append(x / img_w)
normalized.append(y / img_h)
label_line = f"0 {' '.join(map(str, normalized))}\n"
yolo_labels.append(label_line)
# 保存TXT标签到train/labels
label_save_path = os.path.join(output_dir, "labels", "1234.txt")
with open(label_save_path, "w", encoding="utf-8") as f:
f.writelines(yolo_labels)
print(f"YOLO标签已保存:{label_save_path}")
# 验证转换结果
with open(label_save_path, "r") as f:
print("\nYOLO标签内容:")
print(f.read())
3.2 执行转换
python json2yolo.py
转换成功标志:
- 输出“图像已保存:F:/animalrec/test/mineral_dataset/train/images/1234.jpg”
- 输出“YOLO标签已保存:F:/animalrec/test/mineral_dataset/train/labels/1234.txt”
四、YOLO数据集配置文件(data.yaml)
# 数据集根目录(绝对路径,Windows用/或\\)
path: F:/animalrec/test/mineral_dataset
# 训练集/验证集路径(单图场景:val与train一致,自动拆分)
train: train/images
val: train/images
# 类别配置(仅1类:mineral)
nc: 1
names:
0: mineral
五、YOLOv8-seg模型训练
5.1 创建训练脚本(train_mineral.py)
from ultralytics import YOLO
# 加载预训练模型
model = YOLO("yolov8x-seg.pt")
# 启动训练(参数适配单图场景,无需修改)
results = model.train(
data="data.yaml",
epochs=8, # 训练轮数(单图8轮足够)
batch=1, # CPU必设1,避免内存溢出
imgsz=640, # 输入图像尺寸
lr0=0.0005, # 初始学习率(CPU训练降低)
val=True, # 开启验证
split=0.2, # 20%训练集拆分为验证集
save_period=2, # 每2轮保存模型
device="cpu", # 有GPU改"0"
augment=True, # 开启数据增强
patience=3 # 早停机制
)
# 输出训练结果
val_results = model.val()
print("\n=== 训练结果汇总 ===")
print(f"分割mAP50:{val_results.mask.map50:.3f}")
print(f"分割mAP50-95:{val_results.mask.map:.3f}")
print(f"精确率(P):{val_results.mask.p:.3f}")
print(f"召回率(R):{val_results.mask.r:.3f}")
print(f"最优模型路径:runs/segment/train/weights/best.pt")
5.2 执行训练
python train_mineral.py
训练成功标志:
- 损失逐步下降(如
seg_loss从6+降至3+) - 最终输出“8 epochs completed”
- 验证指标
mask.map50>0.9
六、模型推理与矿物颗粒提取
6.1 创建推理脚本(infer_mineral.py)
from ultralytics import YOLO
import cv2
import os
import numpy as np
# 基础配置(无需修改,匹配工作目录)
model_path = "runs/segment/train/weights/best.pt"
img_path = "mineral_dataset/train/images/1234.jpg"
output_root = "mineral_infer_results"
# 创建结果目录
os.makedirs(output_root, exist_ok=True)
overall_dir = os.path.join(output_root, "overall_result")
single_dir = os.path.join(output_root, "single_particles")
os.makedirs(overall_dir, exist_ok=True)
os.makedirs(single_dir, exist_ok=True)
# 加载模型并推理
model = YOLO(model_path)
results = model.predict(
img_path,
save=True,
conf=0.2,
iou=0.5,
save_dir=overall_dir
)
# 读取原始图像
raw_img = cv2.imread(img_path)
img_h, img_w = raw_img.shape[:2]
# 按全图框选提取单个矿物
boxes = results[0].boxes
if boxes is not None:
for idx, box in enumerate(boxes):
# 提取边界框坐标
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
# 坐标处理(转整数+防溢出)
x1 = int(max(0, x1))
y1 = int(max(0, y1))
x2 = int(min(img_w, x2))
y2 = int(min(img_h, y2))
# 过滤极小框
if (x2 - x1) < 20 or (y2 - y1) < 20:
continue
# 裁剪并保存
single_particle = raw_img[y1:y2, x1:x2]
save_path = os.path.join(single_dir, f"mineral_{idx}.jpg")
cv2.imwrite(save_path, single_particle)
print(f"已保存单个颗粒:{save_path}")
print(f"\n推理完成!")
print(f"- 全图结果:{os.path.join(overall_dir, 'predict')}")
print(f"- 单个颗粒:{single_dir}")
6.2 执行推理
python infer_mineral.py
推理成功标志:
- 输出“已保存单个颗粒:F:/animalrec/test/mineral_infer_results/single_particles/mineral_*.jpg”
- 全图结果目录生成带绿色框的图像,单个颗粒目录生成无偏差裁剪图
七、常见问题排查
| 问题现象 | 解决方案 |
|---|---|
| Labelme启动报错“TypeError: 'type' object is not subscriptable” | 执行:pip uninstall labelme -y && pip install labelme==4.5.6
|
| 训练报错“Dataset 'data.yaml' images not found” | 确保data.yaml中path为绝对路径(如F:/animalrec/test/mineral_dataset) |
| 推理时单个颗粒截取偏差 | 确保推理脚本使用results[0].boxes.xyxy坐标(参考6.1节脚本) |
| 训练损失下降缓慢 | 降低lr0=0.0005,确保augment=True
|