LabelImage+YOLOv8 图片单一目标检测模型训练(聚焦图片场景)
本文专为「仅检测图片中某一类目标」(如 "瓶盖""二维码""人脸" 等)设计,流程聚焦图片处理,简化冗余步骤,确保新手也能快速上手,核心是类别数 = 1,训练收敛更快、效果更稳定。
一、核心流程(图片专属)
-
环境搭建(LabelImage 标注 + YOLOv8 图片训练环境)
-
图片准备与单一目标标注(LabelImage 生成 XML)
-
XML→YOLO TXT 格式转换(类别 ID 固定为 0)
-
图片数据集划分(训练 / 验证集)
-
YOLOv8 配置文件编写(nc=1)
-
图片目标检测模型训练(参数适配图片场景)
-
模型评估(基于图片验证集)
-
新图片推理测试(检测效果可视化)
二、详细步骤(聚焦图片,简化操作)
(一)环境搭建(仅需 2 步,图片场景无需额外依赖)
1. LabelImage 安装(标注图片用)
支持 Windows/Linux/Mac,新手推荐 pip 安装:
# 安装依赖(图片标注核心依赖)
pip install pyqt5 lxml
# 安装LabelImage
pip install labelImg
# 启动工具(直接运行,无需额外配置)
labelImg
启动后默认是 VOC XML 格式(无需修改,适配图片标注)。
2. YOLOv8 环境搭建(图片训练核心)
要求 Python 3.8~3.11,核心依赖:
# 安装PyTorch(图片训练需GPU加速,无GPU选CPU版)
# GPU版(推荐,训练图片快,需提前装CUDA 11.8+)
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# CPU版(仅测试,训练100张图片约1-2小时)
pip3 install torch torchvision torchaudio
# 安装YOLOv8官方库(支持图片读写和训练)
pip install ultralytics
验证安装(仅测试图片模型加载):
from ultralytics import YOLO
model = YOLO('yolov8n.pt') # 加载轻量化图片检测模型
print("YOLOv8图片环境安装成功")
(二)图片准备与单一目标标注(核心环节)
1. 图片准备(适配 YOLOv8 图片训练)
-
格式:统一为 JPG/PNG(避免混合格式),放在
dataset/images/all文件夹。 -
数量:单一目标推荐≥50 张(最优 100 + 张),需覆盖:
-
不同角度(正面、侧面、倾斜)
-
不同大小(近景大目标、远景小目标)
-
不同背景(室内、室外、复杂背景)
-
不同光照(明亮、昏暗、逆光)
-
-
尺寸:建议图片分辨率≥640×640(太小会影响小目标检测)。
2. LabelImage 图片标注步骤(仅标单一目标)
-
启动 LabelImage,点击左上角「Open Dir」→ 选择
dataset/images/all(加载所有待标注图片)。 -
点击「Change Save Dir」→ 选择
dataset/annotations/xml(存放图片对应的 XML 标注文件)。 -
标注操作(仅针对单一目标):
-
快捷键「W」:绘制矩形框,紧贴目标边缘(不包含多余背景,不遗漏目标)。
-
弹出类别框,输入唯一目标名称(如 "bottle_cap",全程拼写一致,无大小写错误)。
-
快捷键「D」:切换到下一张图片,「A」返回上一张,每张图片中所有该目标必须标注(无漏标)。
-
若图片中无该目标,直接跳过(无需生成 XML 文件)。
- 标注完成后:
dataset/annotations/xml中会生成与图片同名的 XML 文件(如img_001.jpg→img_001.xml)。
标注避坑(图片场景重点)
-
不标背景:避免将图片中的背景物体误标为目标。
-
框选精准:图片像素清晰,矩形框需紧贴目标(如检测二维码,框选整个二维码区域,不裁剪边角)。
-
无重复标注:同一张图片中同一目标只标 1 个框,不重复标注。
(三)格式转换(XML→YOLO TXT,图片标注专属脚本)
YOLOv8 训练图片需要 TXT 格式,单一目标的类别 ID 固定为 0(仅 1 类),脚本简化如下:
创建 xml2txt_img.py(仅需修改目标类别名称):
import os
import xml.etree.ElementTree as ET
# -------------------------- 仅需修改1个参数 --------------------------
TARGET_CLASS = "bottle_cap" # 你的单一目标类别(如"qrcode"、"face")
XML_DIR = "dataset/annotations/xml" # 图片XML标注文件路径
TXT_DIR = "dataset/annotations/txt" # 转换后TXT保存路径
# ------------------------------------------------------------------
# 创建TXT文件夹(若不存在)
os.makedirs(TXT_DIR, exist_ok=True)
def convert_xml_to_yolo(xml_file):
# 解析XML文件(图片宽高、目标坐标)
tree = ET.parse(xml_file)
root = tree.getroot()
img_w = int(root.find('size/width').text)
img_h = int(root.find('size/height').text)
# 生成YOLO TXT内容
txt_content = ""
for obj in root.findall('object'):
class_name = obj.find('name').text
if class_name != TARGET_CLASS:
continue # 只保留单一目标
# XML坐标(xmin, ymin, xmax, ymax)转YOLO归一化坐标
xmin = float(obj.find('bndbox/xmin').text)
ymin = float(obj.find('bndbox/ymin').text)
xmax = float(obj.find('bndbox/xmax').text)
ymax = float(obj.find('bndbox/ymax').text)
center_x = (xmin + xmax) / (2 \* img_w)
center_y = (ymin + ymax) / (2 \* img_h)
w = (xmax - xmin) / img_w
h = (ymax - ymin) / img_h
txt_content += f"0 {center_x:.6f} {center_y:.6f} {w:.6f} {h:.6f}\n" # 类别ID=0
# 保存TXT文件(与图片同名)
txt_file = os.path.join(TXT_DIR, os.path.splitext(os.path.basename(xml_file))\[0] + ".txt")
with open(txt_file, 'w', encoding='utf-8') as f:
f.write(txt_content)
# 批量转换所有图片的XML标注
for xml_file in os.listdir(XML_DIR):
if xml_file.endswith('.xml'):
convert_xml_to_yolo(os.path.join(XML_DIR, xml_file))
print(f"转换完成!{len(os.listdir(XML_DIR))}张图片的标注已转为TXT,类别ID=0")
运行脚本:
python xml2txt_img.py
(四)图片数据集划分(自动匹配图片与标注)
创建 split_img_dataset.py,自动将图片和对应 TXT 标注划分为训练集(80%)和验证集(20%):
import os
import shutil
import random
# -------------------------- 无需修改参数 --------------------------
IMG_RAW_DIR = "dataset/images/all" # 原始图片路径
TXT_RAW_DIR = "dataset/annotations/txt" # 转换后的TXT路径
TRAIN_IMG_DIR = "dataset/images/train" # 训练集图片路径
VAL_IMG_DIR = "dataset/images/val" # 验证集图片路径
TRAIN_TXT_DIR = "dataset/labels/train" # 训练集标注路径
VAL_TXT_DIR = "dataset/labels/val" # 验证集标注路径
SPLIT_RATIO = 0.8 # 训练集比例
# ------------------------------------------------------------------
# 创建目标文件夹(自动创建,无需手动建)
for dir_path in \[TRAIN_IMG_DIR, VAL_IMG_DIR, TRAIN_TXT_DIR, VAL_TXT_DIR]:
os.makedirs(dir_path, exist_ok=True)
# 获取所有图片名称(不含后缀,确保图片和标注一一对应)
img_names = \[os.path.splitext(f)\[0] for f in os.listdir(IMG_RAW_DIR) if f.endswith(('.jpg', '.png'))]
random.shuffle(img_names) # 随机打乱图片顺序(避免同类场景集中)
# 划分训练集和验证集
train_size = int(len(img_names) \* SPLIT_RATIO)
train_names = img_names\[:train_size]
val_names = img_names\[train_size:]
# 复制训练集文件(图片+标注)
for name in train_names:
# 复制图片
img_src = os.path.join(IMG_RAW_DIR, f"{name}.jpg")
if os.path.exists(img_src):
shutil.copy(img_src, TRAIN_IMG_DIR)
# 复制标注(若存在)
txt_src = os.path.join(TXT_RAW_DIR, f"{name}.txt")
if os.path.exists(txt_src):
shutil.copy(txt_src, TRAIN_TXT_DIR)
# 复制验证集文件(图片+标注)
for name in val_names:
img_src = os.path.join(IMG_RAW_DIR, f"{name}.jpg")
if os.path.exists(img_src):
shutil.copy(img_src, VAL_IMG_DIR)
txt_src = os.path.join(TXT_RAW_DIR, f"{name}.txt")
if os.path.exists(txt_src):
shutil.copy(txt_src, VAL_TXT_DIR)
print(f"数据集划分完成!")
print(f"训练集:{len(train_names)}张图片,{len(os.listdir(TRAIN_TXT_DIR))}个标注")
print(f"验证集:{len(val_names)}张图片,{len(os.listdir(VAL_TXT_DIR))}个标注")
运行脚本:
python split_img_dataset.py
最终数据集结构(图片专属,清晰规整):
dataset/
├── images/ # 图片文件夹
│ ├── train/ # 训练集图片(80%)
│ └── val/ # 验证集图片(20%)
├── labels/ # 标注文件夹(与图片一一对应)
│ ├── train/ # 训练集TXT(类别ID=0)
│ └── val/ # 验证集TXT(类别ID=0)
└── custom_data.yaml # YOLOv8配置文件(下一步编写)
(五)编写 YOLOv8 配置文件(图片训练核心)
在 dataset 文件夹下创建 custom_data.yaml,仅 3 行关键配置,聚焦图片路径和单一目标:
# 图片数据集路径(推荐绝对路径,避免相对路径出错,如D:/dataset/images/train)
train: ../dataset/images/train
val: ../dataset/images/val
nc: 1 # 单一目标,类别数=1(必须设为1)
names: \["bottle_cap"] # 目标类别名称,与标注时完全一致(对应ID=0)
(六)图片单一目标模型训练(参数优化,快速收敛)
单一目标 + 图片场景训练效率高,无需过多轮数,创建 train_img_model.py:
from ultralytics import YOLO
# 加载轻量化预训练模型(图片检测无需大模型,训练快、占用显存少)
model = YOLO('yolov8n.pt') # 优先选yolov8n.pt(最快),需更高精度用yolov8s.pt
# 开始训练(图片场景参数优化)
results = model.train(
data='dataset/custom_data.yaml', # 配置文件路径
epochs=50, # 图片单一目标收敛快,50轮足够(多了易过拟合)
batch=8, # 根据GPU显存调整:无GPU=2,1080Ti=8,3090=16(图片批量处理更高效)
imgsz=640, # 输入图片尺寸(默认640,图片中小目标多可设为800)
device=0, # 0=GPU,-1=CPU(图片训练GPU比CPU快10倍以上)
patience=25, # 验证集mAP不提升25轮则提前停止(避免无效训练)
save=True, # 保存验证集表现最好的模型(best.pt)
project='runs/detect',
name='img_single_train', # 训练任务名称(区分其他任务)
augment=True, # 启用图片数据增强(翻转、旋转、亮度调整,提升泛化性)
pretrained=True, # 用预训练权重(迁移学习,图片训练更快收敛)
lr0=0.01, # 初始学习率(默认即可,无需修改)
)
运行训练:
python train_img_model.py
训练关键观察(图片场景专属)
-
训练日志会显示图片处理进度、损失值(loss)、验证集 mAP@0.5(核心指标)。
-
训练完成后,结果保存在
runs/detect/img_single_train,核心文件:-
best.pt:图片检测效果最好的模型(优先使用)。 -
results.csv:图片训练日志(可查看 loss 变化)。 -
confusion_matrix.png:图片验证集混淆矩阵(单一目标仅 1 类,直观查看准确率)。
-
(七)模型评估(基于图片验证集)
训练完成后自动生成评估报告,重点关注 2 个核心指标(图片场景):
-
mAP@0.5:IOU 阈值 0.5 时的平均精度(单一目标≥0.8 为优秀,≥0.7 为可用)。
-
Recall:图片中实际存在的目标被检测到的比例(≥0.8 为宜,太低则漏检多)。
手动重新评估(可选):
from ultralytics import YOLO
model = YOLO('runs/detect/img_single_train/best.pt')
# 用验证集图片评估
results = model.val(
data='dataset/custom_data.yaml',
imgsz=640,
device=0,
batch=8
)
(八)新图片推理测试(核心目标:检测图片中的单一目标)
使用训练好的 best.pt 模型检测新图片,可视化检测结果:
创建 infer_img.py:
from ultralytics import YOLO
import cv2
# 加载最佳模型
model = YOLO('runs/detect/img_single_train/best.pt')
# 待检测图片路径(替换为你的测试图片)
test_img_path = "test_img.jpg" # 如"qrcode_test.jpg"、"face_test.png"
# 图片推理(参数适配图片场景)
results = model(
test_img_path,
save=True, # 保存检测后的图片(含检测框和类别名称)
conf=0.6, # 置信度阈值(图片场景:0.5-0.7,太高漏检,太低误检)
iou=0.45, # 非极大值抑制阈值(避免图片中同一目标多框)
show_labels=True, # 显示目标类别名称
show_conf=True # 显示置信度
)
# 手动显示检测结果(弹窗查看)
for r in results:
img_with_box = r.plot() # 绘制检测框(图片格式为BGR)
cv2.imshow('Single Target Detection Result', img_with_box)
cv2.waitKey(0) # 按任意键关闭弹窗
cv2.destroyAllWindows()
print(f"检测完成!结果图片保存至:{results\[0].save_dir}")
运行推理:
python infer_img.py
推理参数调整技巧(图片场景)
-
误检多(图片中检测到不存在的目标):提高
conf至 0.7-0.8。 -
漏检多(图片中目标未被检测到):降低
conf至 0.4-0.5,或提高imgsz至 800。 -
检测框不准:调整
iou至 0.3-0.5(图片中目标密集时用 0.3)。
三、图片场景专属优化方案(解决常见问题)
1. 样本不足(图片 < 50 张)
用数据增强脚本批量生成新图片(无需重新标注):
import cv2
import os
import albumentations as A
# 图片增强脚本:augment_img.py
RAW_IMG_DIR = "dataset/images/all"
AUG_IMG_DIR = "dataset/images/augmented"
os.makedirs(AUG_IMG_DIR, exist_ok=True)
# 定义图片增强方式(不改变标注逻辑)
aug = A.Compose(\[
A.RandomRotate90(p=0.5), # 随机旋转90度
A.HorizontalFlip(p=0.5), # 水平翻转
A.RandomBrightnessContrast(p=0.5), # 随机亮度/对比度
A.Resize(height=640, width=640, p=1.0) # 统一图片尺寸
])
# 批量增强图片
for img_file in os.listdir(RAW_IMG_DIR):
if img_file.endswith(('.jpg', '.png')):
img = cv2.imread(os.path.join(RAW_IMG_DIR, img_file))
for i in range(2): # 每张原图生成2张增强图
augmented = aug(image=img)
aug_img = augmented\['image']
aug_img_name = f"{os.path.splitext(img_file)\[0]}_aug{i}.jpg"
cv2.imwrite(os.path.join(AUG_IMG_DIR, aug_img_name), aug_img)
print(f"增强完成!新增{len(os.listdir(AUG_IMG_DIR))}张图片,可直接移至dataset/images/all重新划分")
2. 图片中小目标检测差
-
训练时设置
imgsz=800(增大输入图片尺寸,让小目标更清晰)。 -
标注时确保小目标框选完整(图片放大后标注,避免漏标边角)。
3. 图片背景复杂导致误检
-
增加复杂背景的图片样本(如检测 "瓶盖",加入桌面、地面、草丛等背景的图片)。
-
推理时提高
conf至 0.7(过滤低置信度的误检框)。
四、总结
图片单一目标检测的核心是「简化类别、聚焦图片场景」:LabelImage 确保标注准确,YOLOv8 通过参数优化实现快速训练,全程无需处理视频,流程更简洁、效率更高。
关键要点:
-
类别数 = 1,标注、转换、配置全程保持一致。
-
图片样本需覆盖多样场景(角度、大小、背景)。
-
训练用轻量化模型(yolov8n.pt),推理时调整
conf阈值适配图片。