YOLO26中的语义分割
语义分割为图像中的每个像素分配一个类标签,从而生成覆盖整个场景的密集类映射。与区分单个对象的实例分割不同,语义分割将同一类的所有像素组合在一起,而不管存在多少个不同的对象。语义分割模型的输出是一个单一的宽高类映射,其中每个像素值对应一个预测的类ID。这使得语义分割非常适合场景解析任务,如自动驾驶、医学成像和土地覆盖映射。YOLO26语义分割模型文件使用-sem后缀,例如yolo26n-sem.pt。
数据集格式:语义分割数据集使用单通道掩码图像(通常为PNG),其中每个像素值代表一个类ID。值为255的像素被视为"忽略"并从损失计算中排除。数据集YAML应指定图像及其对应掩码目录的路径。
语义分割为图像中的每个像素分配一个类别标签。与实例分割不同,语义分割不会区分同一类别的单个对象。训练目标是一个密集类掩码,其中每个像素存储一个类ID。
支持的数据集格式:支持两种标签格式。数据集加载器会根据数据集YAML是否定义了masks_dir键来选择路径。
1.PNG掩码格式:语义分割数据集每个样本使用一个图像文件和一个掩码文件。掩码是单通道图像,通常为PNG格式,其中每个像素值都是对应图像像素的类别索引。
(1).像素值0、1、2等代表数据集names映射中的类ID。
(2).像素值255被视为忽略标签,并在损失和指标计算中被排除。
(3).掩码文件应与其匹配的图像文件使用相同的文件名。
(4).掩码默认被解析为.png文件;如果缺失,也接受其他支持的图像扩展名。使用无损格式(如.png或.tiff),因为有损压缩(如.jpg)会损坏类ID像素值。
(5).默认布局将图像和掩码保留在并行文件夹中。数据集YAML中的masks_dir值会替换images路径组件以查找掩码。
2.YOLO多边形标签格式:如果你的数据集已经拥有Ultralytics YOLO多边形标签(每张图像一个.txt文件,包含<class-index> <x1> <y1> <x2> <y2> ...行),你可以直接从中训练语义分割,无需转换为PNG掩码。
当数据集YAML省略masks_dir时,会自动选择此路径。其行为如下:
(1).多边形在加载时会被转换为每张图像的语义掩码,并按面积排序,以便在重叠区域中较小的对象覆盖较大的对象。
(2).Multi-class(N > 1 in names): an extra background class is appended after your declared classes for pixels not covered by any polygon. The model is built with N + 1 output channels and the last channel is background.
(3).Single-class(N == 1 in names): still trained as 1 class. The mask is binary, with your declared class shown as 1 and pixels not covered by any polygon as 0. No extra background class is added to names.
(4).由增强填充(例如随机裁剪)添加的像素仍使用255作为忽略标签。
数据集YAML格式:语义分割数据集使用YAML文件进行配置。主要字段包括:
(1).path:数据集根目录。
(2).train:相对于path的训练图像路径,或绝对路径。
(3).val:相对于path的验证图像路径,或绝对路径。
(4).test:可选的测试图像路径。
(5).masks_dir:用于语义掩码的目录名称。省略此键可切换至YOLO多边形标签格式。
(6).names:类ID到类名称的映射。
(7).label_mapping:可选的从源数据集ID到训练ID或ignore_label的映射。当源掩码ID与连续的训练类ID不匹配时,使用label_mapping。例如官方cityscapes8.yaml文件内容如下:
# Dataset root directory
path: cityscapes8 # dataset root dir
train: images/train # train images (relative to 'path') 4 images
val: images/val # val images (relative to 'path') 4 images
masks_dir: masks # semantic mask directory
# Cityscapes 19-class labels
names:
0: road
1: sidewalk
2: building
3: wall
4: fence
5: pole
6: traffic light
7: traffic sign
8: vegetation
9: terrain
10: sky
11: person
12: rider
13: car
14: truck
15: bus
16: train
17: motorcycle
18: bicycle
# Map source label IDs to train IDs; ignore_label is converted to 255.
label_mapping:
-1: ignore_label
0: ignore_label
1: ignore_label
2: ignore_label
3: ignore_label
4: ignore_label
5: ignore_label
6: ignore_label
7: 0
8: 1
9: ignore_label
10: ignore_label
11: 2
12: 3
13: 4
14: ignore_label
15: ignore_label
16: ignore_label
17: 5
18: ignore_label
19: 6
20: 7
21: 8
22: 9
23: 10
24: 11
25: 12
26: 13
27: 14
28: 15
29: ignore_label
30: ignore_label
31: 16
32: 17
33: 18
Cityscapes数据集:是一个大规模的语义分割基准,专注于在50个欧洲城市中拍摄的城市街景。它提供了高质量的像素级标注。
(1).Cityscapes精细标注包括2975张训练图像、500张验证图像和1525张测试图像。
(2).该数据集涵盖19个评估类别,横跨道路、车辆、人类、建筑、物体、自然和天空等类别。
(3).Cityscapes提供标准化的评估指标,如用于语义分割的平均交并比(mIoU),从而实现对模型性能的有效比较。
训练测试代码如下:在Cityscapes8数据集(训练集和验证集各4幅图像)上以1024图像尺寸训练YOLO26n-sem模型
def train(task, model_name, yaml, epochs, imgsz, patience, batch, optimizer, lr0, lrf, dropout, augment):
model = YOLO(model_name) # load a pretrained model, should be a *.pt PyTorch model to run this method, n/s/m/l/x
# petience: Training stopped early as no improvement observed in last patience epochs, use patience=0 to disable EarlyStopping
model.train(data=yaml, epochs=epochs, imgsz=imgsz, patience=patience, batch=batch, optimizer=optimizer, lr0=lr0, lrf=lrf, dropout=dropout, augment=augment) # train the model, supported parameter reference, for example: runs/segment(detect)/train3/args.yaml
if task == "semantic":
metrics = model.val(data=yaml)
print(f"miou: {metrics.miou}") # mean Intersection over Union
print(f"pixel_accuracy: {metrics.pixel_accuracy}") # overall pixel accuracy
else:
metrics = model.val() # It'll automatically evaluate the data you trained, no arguments needed, dataset and settings remembered
if task == "classify":
print(f"Top-1 Accuracy:{metrics.top1:.6f}") # top1 accuracy
print(f"Top-5 Accuracy: {metrics.top5:.6f}") # top5 accuracy
else:
print(f"map50-95(B):", metrics.box.map)
print(f"map50(B):", metrics.box.map50)
print(f"map75(B):", metrics.box.map75)
model.export(format="onnx", opset=12, simplify=True, dynamic=False, imgsz=imgsz) # onnx, export the model, cannot specify dynamic=True, opencv does not support
model.export(format="torchscript", imgsz=imgsz) # libtorch
model.export(format="openvino", imgsz=imgsz, dynamic=False, half=False, int8=False) # openvino fp32
# model.export(format="openvino", imgsz=imgsz, dynamic=False, half=True) # openvino fp16
# model.export(format="openvino", imgsz=imgsz, dynamic=False, int8=True, data=yaml) # openvino int8, INT8 export requires 'data' arg for calibration
if torch.cuda.is_available():
model.export(format="engine", imgsz=imgsz, dynamic=False, verbose=False, batch=1, workspace=2) # tensorrt fp32
# model.export(format="engine", imgsz=imgsz, dynamic=False, verbose=False, batch=1, workspace=2, half=True) # tensorrt fp16
# model.export(format="engine", imgsz=imgsz, dynamic=False, verbose=False, batch=1, workspace=2, int8=True, data=yaml) # tensorrt int8
训练如下图所示:

预测测试代码如下:
def _get_images(dir):
# supported image formats
img_formats = (".bmp", ".jpeg", ".jpg", ".png", ".webp")
images = []
for file in os.listdir(dir):
if os.path.isfile(os.path.join(dir, file)):
# print(file)
_, extension = os.path.splitext(file)
for format in img_formats:
if format == extension.lower():
images.append(file)
break
return images
def predict(task, model_name, device, verbose, dir_images, dir_result):
model = YOLO(model_name) # load an model, support format: *.pt, *.onnx, *.torchscript, *.engine, openvino_model
# model.info() # display model information # only *.pt format support
if task == "detect" or task =="segment" or task == "obb" or task == "semantic":
os.makedirs(dir_result, exist_ok=True)
images = _get_images(dir_images)
for image in images:
results = model.predict(dir_images+"/"+image, verbose=verbose, device=device)
if task == "detect" or task =="segment" or task == "obb" or task == "semantic":
for result in results:
# print("result:", result)
result.save(dir_result+"/"+image)
else:
print(f"class names:{results[0].names}: top5: {results[0].probs.top5}; conf:{results[0].probs.top5conf}")
预测结果如下图所示:

更多推荐


所有评论(0)