1. 为什么需要自定义语义分割数据集
当你想要识别图片中特定物体的精确轮廓时,现成的公开数据集往往无法满足需求。比如你想区分农田里的不同作物,或者识别工业场景中的特定零件,这时候就需要构建自己的语义分割数据集。Deeplabv3+作为当前最先进的语义分割框架之一,对数据格式有着特定要求,而Labelme恰好是最适合个人开发者使用的标注工具。
我在处理农业遥感图像时就遇到过这个痛点。公开数据集里的"植物"类别根本无法区分玉米和小麦,只能自己动手标注。整个过程最耗时的不是模型训练,而是前期数据准备——从原始图片标注到生成Deeplabv3+可识别的PNG掩码,中间要经历多次格式转换。下面我就把踩坑后总结的最佳实践分享给大家。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 标注工具的选择与使用技巧
2.1 Labelme的安装与基础操作
推荐使用Anaconda快速搭建Python3.7环境:
bash复制conda create -n labelme python=3.7
conda activate labelme
pip install labelme
启动标注界面只需执行:
bash复制labelme
右键菜单中最重要的三个功能:
- Create Polygon:绘制多边形轮廓
- Edit Polygon:调整已有标注点
- Create Rectangle:快速标注矩形区域(适合规则物体)
实测标注效率提升技巧:
- 对同类物体标注时,按空格键可复用当前标签
- 使用Ctrl+Z撤销错误标注
- 对对称物体可先标注一半,然后复制粘贴再水平翻转
2.2 高级标注策略
对于复杂场景,建议采用分层标注法:
- 第一轮粗标:用大多边形快速覆盖所有目标区域
- 第二轮精修:逐个调整边缘至像素级精度
- 第三轮校验:开启"View -> Show Labels"检查是否有漏标
处理遮挡关系的黄金法则:
- 被遮挡物体按实际可见部分标注
- 在标签名称中添加"occluded"后缀(如"car_occluded")
- 同一物体的不同部分应使用相同标签名
3. 数据格式转换全流程
3.1 JSON到PNG的转换原理
Labelme生成的JSON文件其实包含三个关键信息:
- 原始图片的Base64编码
- 每个多边形的顶点坐标
- 多边形对应的类别标签
转换脚本的核心工作就是:
- 解析JSON重建标注区域
- 根据label.txt中的类别映射关系
- 生成单通道的PNG掩码图(每个像素值对应类别ID)
3.2 实战转换代码详解
改进版的转换脚本增加了以下实用功能:
- 自动校验标签完整性
- 支持批量处理子文件夹
- 生成可视化校验图
python复制def validate_labels(json_path, label_names):
"""检查是否有未定义标签"""
with open(json_path) as f:
data = json.load(f)
for shape in data['shapes']:
if shape['label'] not in label_names:
raise ValueError(f"发现未定义标签: {shape['label']}")
def batch_convert(input_dir, output_dir):
"""处理嵌套文件夹结构"""
for root, _, files in os.walk(input_dir):
rel_path = os.path.relpath(root, input_dir)
os.makedirs(os.path.join(output_dir, rel_path), exist_ok=True)
for file in files:
if file.endswith('.json'):
convert_single_file(
os.path.join(root, file),
os.path.join(output_dir, rel_path)
)
3.3 常见报错解决方案
错误1:KeyError: '__ignore__'
- 原因:label.txt第一行必须是
__ignore__ - 修正:检查label.txt文件开头内容
错误2:生成的PNG全是黑色
- 排查步骤:
- 确认label.txt中的类别顺序
- 检查JSON中label名称是否完全匹配
- 查看SegmentationClassVisualization中的预览图
错误3:内存不足
- 优化方案:
python复制# 在处理大图时添加 import gc gc.collect()
4. 构建Deeplabv3+数据目录
4.1 标准目录结构详解
必须包含的文件夹:
code复制PV/
├── ImageSets/
│ └── Segmentation/
│ ├── train.txt
│ ├── val.txt
│ └── trainval.txt
├── JPEGImages/
│ ├── 0001.jpg
│ └── 0002.jpg
└── SegmentationClass/
├── 0001.png
└── 0002.png
进阶目录建议添加:
SegmentationClassRaw/:存储未经过颜色映射的单通道PNGAnnotations/:备份原始JSON文件preview/:存放标注可视化对比图
4.2 数据集划分策略
不同于分类任务,语义分割的数据集划分需要特别注意:
- 确保同一场景的不同视角图片划分到同一集合
- 对连续帧视频数据应按片段划分
- 各类别在训练集中的比例应接近实际分布
改进版划分代码示例:
python复制def stratified_split(files, class_distribution, test_ratio=0.2):
"""保持类别分布的划分方法"""
from collections import defaultdict
class_groups = defaultdict(list)
# 按类别分组
for f in files:
class_id = get_dominant_class(f) # 需要实现该函数
class_groups[class_id].append(f)
# 分层抽样
train_files = []
test_files = []
for class_id, items in class_groups.items():
split_idx = int(len(items) * (1 - test_ratio))
train_files.extend(items[:split_idx])
test_files.extend(items[split_idx:])
return train_files, test_files
5. 与Deeplabv3+框架的集成
5.1 修改配置文件的关键点
在datasets/目录下新建PV.py时,需要特别注意三个参数:
python复制class VOCSegmentation(data.Dataset):
NUM_CLASSES = 5 # 包含背景类
CLASS_NAMES = ['background', 'road', 'car', 'person', 'building']
COLOR_MAP = [(0,0,0), (128,0,0), (0,128,0), (128,128,0), (0,0,128)]
调试技巧:
- 先用单个样本测试数据加载
- 可视化
__getitem__的输出 - 检查
palette与COLOR_MAP是否一致
5.2 数据增强方案配置
推荐使用Albumentations库进行高效增强:
python复制import albumentations as A
train_transform = A.Compose([
A.RandomCrop(512, 512),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.2),
A.ShiftScaleRotate(scale_limit=0.1, rotate_limit=10),
A.OneOf([
A.GaussNoise(),
A.Blur(),
], p=0.3),
])
特别注意:所有空间变换必须同时作用于图像和掩码
python复制transformed = train_transform(image=img, mask=mask)
img, mask = transformed['image'], transformed['mask']
6. 实战中的疑难解答
6.1 标注与训练效果优化
现象:边缘分割不精确
- 解决方案:
- 在Labelme中开启"View -> Show Edge"检查标注
- 训练时增加边缘权重:
python复制criterion = nn.CrossEntropyLoss( weight=torch.tensor([1.0, 3.0]) # 边缘类别权重更高 )
现象:小目标识别效果差
- 改进措施:
- 使用HRNet作为backbone
- 在数据增强中添加随机放大
- 采用OHEM损失函数
6.2 性能优化技巧
加速数据加载的三种方法:
- 使用LMDB存储数据集
python复制env = lmdb.open('dataset.lmdb', map_size=1e12) with env.begin(write=True) as txn: txn.put(key.encode(), img_png.tobytes()) - 启用多进程加载
python复制DataLoader(..., num_workers=4, pin_memory=True) - 预先生成缩放后的图片版本
内存优化配置:
python复制torch.backends.cudnn.benchmark = True # 加速卷积运算
torch.cuda.empty_cache() # 训练循环开始前释放缓存
7. 从标注到部署的全链路验证
建立质量检查闭环:
- 标注阶段:随机抽查10%的标注文件
- 转换阶段:对比JSON与生成的PNG文件
- 训练阶段:可视化验证集的预测结果
- 部署阶段:对边缘case进行再标注
自动化测试脚本示例:
python复制def test_pipeline():
# 1. 生成虚拟标注
create_mock_data()
# 2. 执行格式转换
run_conversion()
# 3. 验证输出格式
verify_output()
# 4. 训练微型模型
train_dummy_model()
# 5. 检查预测结果
assert evaluate() > 0.9
在工业质检项目中,这套流程帮我们发现了3类典型问题:标注工具版本不一致导致的解析错误、图像EXIF方向信息丢失、以及中文路径引起的编码问题。
