第一次接触实例分割时,我盯着屏幕上的Mask R-CNN论文发呆了半小时——那些密密麻麻的边界框和彩色掩膜到底是怎么生成的?后来在工业质检项目里才明白,这技术能精准定位每个零件的同时,还能像小孩涂色一样把物体轮廓描出来。实例分割相比普通目标检测多了像素级定位能力,相比语义分割又能区分同类物体的不同个体。
为什么选择mmdetection?三年前我用原版Mask R-CNN代码训练模型时,光是数据加载器就调试了两周。而mmdetection把数据增强、模型架构、训练流程都模块化了,就像乐高积木一样能自由组合。最新版的mmdetection 3.x支持超过80种检测分割模型,连最新的Swin Transformer架构都内置了。有次我给客户演示,用以下代码5分钟就跑通了demo:
python复制from mmdet.apis import init_detector, inference_detector
config = 'configs/mask_rcnn/mask_rcnn_r50_fpn_1x_coco.py'
checkpoint = 'checkpoints/mask_rcnn_r50_fpn_1x_coco_20200205.pth'
model = init_detector(config, checkpoint)
result = inference_detector(model, 'test.jpg')
实际项目中会遇到些有趣的细节问题。比如工业场景的金属反光会影响分割边缘,这时在config里把FPN层的out_channels从256改成512,配合更大的img_scale参数,mask精度就能提升3-5个点。不过要注意GPU显存消耗会成倍增加,我的经验是11G显存的2080Ti最多扛得住1333x800的输入尺寸。
标注工具选型上踩过不少坑。早期用VGG Image Annotator (VIA)时,标注1000张齿轮图片后才发现导出的JSON格式和COCO不兼容。现在我的工具箱里常备三件套:Labelme做多边形标注,CVAT支持团队协作,LabelImg快速打矩形框。对于精密零件标注,分享两个实用技巧:
Ctrl+鼠标滚轮可以无极缩放图像,配合E键修边缘比手动点击精准得多数据转换时这个Python脚本能自动处理90%的格式问题:
bash复制python tools/data_converters/labelme2coco.py \
--img-dir ./images \
--labels labels.txt \
--out ./annotations/instances_train.json
但真实场景总有意外——上周处理医疗影像时,DICOM格式的CT切片直接转换会丢失层间距信息。这时候就得先用SimpleITK做预处理:
python复制import SimpleITK as sitk
image = sitk.ReadImage("CT_scan.dcm")
sitk.WriteImage(image, "converted.png")
数据增强我习惯用Albumentations库,它的MaskCompose能确保图像变换和标注mask同步处理。有个容易忽略的细节:对于带有文字的方向性物体(如仪表盘),随机旋转可能导致识别错误,这时候要在pipeline里限制旋转角度:
python复制transform = A.Compose([
A.Rotate(limit=15, p=0.5), # 限制在±15度内旋转
A.RandomBrightnessContrast(p=0.2),
], additional_targets={'mask': 'mask'})
第一次看到mmdetection的配置文件时,那层层嵌套的Python字典让我头皮发麻。其实理解其设计哲学后,修改配置就像调汽车座椅——每个参数都有明确的人体工学考量。以Mask R-CNN为例,关键配置模块包括:
num_outs决定检测不同尺度物体的能力roi_layer里的featmap_stride影响小目标检测效果这是我优化过的工业质检配置片段:
python复制model = dict(
backbone=dict(
depth=101,
init_cfg=dict(checkpoint='torchvision://resnet101')),
neck=dict(
type='PAFPN',
in_channels=[256, 512, 1024, 2048],
out_channels=256,
num_outs=5),
roi_head=dict(
bbox_head=dict(num_classes=8),
mask_head=dict(num_classes=8)))
训练策略的调整更有意思。有次客户抱怨模型在夜班拍摄的图片上表现差,我们通过动态调整学习率解决了问题:
python复制lr_config = dict(
policy='CosineAnnealing',
warmup='linear',
warmup_iters=500,
warmup_ratio=0.001,
min_lr_ratio=1e-5)
实际测试发现,当训练集存在光照差异时,配合ColorJitter增强效果更佳:
python复制train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='LoadAnnotations'),
dict(
type='Albu',
transforms=[
dict(type='RandomBrightnessContrast', brightness_limit=0.3),
dict(type='RGBShift', r_shift_limit=20, g_shift_limit=20)
])
]
启动训练后千万别走开!我习惯用mmdetection的hook机制监控关键指标。在config里添加这些配置能实时捕捉异常:
python复制custom_hooks = [
dict(type='NumClassCheckHook'),
dict(
type='EarlyStoppingHook',
monitor='segm_mAP',
patience=5,
min_delta=0.005)
]
遇到loss震荡时,先检查数据标注质量。有次发现验证集mAP突然暴跌,最后定位到是标注员把生锈零件全标成了"defect"类。这时候可以用analyze_logs.py脚本可视化训练过程:
bash复制python tools/analysis_tools/analyze_logs.py plot_curve \
log.json --keys loss_mask --out mask_loss.png
模型部署时有个隐藏陷阱——ONNX导出可能失败。最近在TensoRT上部署时发现这个问题:
python复制torch.onnx.export(model, dummy_input, "model.onnx")
# 报错:Unsupported: ONNX export of operator roi_align
解决方案是在config里显式指定deploy模式:
python复制model.test_cfg = dict(
rcnn=dict(
score_thr=0.05,
nms=dict(type='nms', iou_threshold=0.5),
max_per_img=100,
mask_thr_binary=0.5))
最后分享一个性能优化技巧:对于2000x2000以上的大图,在test_pipeline里添加slice_inference能降低显存消耗:
python复制test_pipeline = [
dict(type='LoadImageFromFile'),
dict(
type='MultiScaleFlipAug',
img_scale=(2048, 2048),
flip=False,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(type='Pad', size_divisor=32),
dict(
type='DefaultFormatBundle',
pad_val=dict(img=(114, 114, 114))),
dict(type='ImageToTensor', keys=['img'])
])
]