最近看到《BlendMask: Top-Down Meets Bottom-Up for Instance Segmentation》论文,目标的mask提取效果要比maskrcnn的要提高很多,边缘效果提取的也很不错,详细介绍,可以参考这个博主写的,内容写的很赞,我就不介绍了,代码也已经开源,开源地址:https://github.com/lmquan2000/BlendMask/blob/master/configs/BlendMask/README.md#citing-blendmask。所以想实现自己数据集。
具体实现过程其实跟实现Detrectron2训练自己的数据集过程差不多,对于大佬来说很简单,对我这个菜鸟就不一样了,出现了很多bug,如果不知道如何实现Detrectron2训练自己的数据集,可以参考该博主的。这里就是记录自己在实现过程中出现的bug,我原先做的是pointrend算法,提取的效果也不错,但是当目标前面有遮挡导致目标不完整的时候,会把目标提取成两个部分,这样导致后面没法使用,所以换成blendmask,我自己主要的bug都是在读取数据集的部分。
一,制作自己的数据集
1、根据上面的要求,先制作自己的coco数据,该部分网上也有很多,这里就不细述了,我都数据结果如下:
imagedata
--data
instance_train.json
instance_val.json
--train
a.png
....
--val
b.png
....
2、将数据集放到/root/detectron2/AdelaiDet/datasets里,可以指定放在其他目录下,但是过后运行同样要指定,我就比较懒了。
我的就是转换成coco数据集了,所以运行datasets里的prepare_thing_sem_from_instance.py,但是要需要该几个地方的内容
1)_process_instance_to_semantic函数
def _process_instance_to_semantic(anns, output_semantic, img, categories):
img_size = (img["height"], img["width"])
output = np.zeros(img_size, dtype=np.uint8)
for ann in anns:
mask = annToMask(ann, img_size)
output[mask == 1] = categories[ann["category_id"]] + 1
# save as compressed npz
np.savez_compressed(output_semantic, mask=output)
# Image.fromarray(output).save(output_semantic)
output[mask == 1] = categories[ann["category_id"]] + 1,我的数据集只有一个标签,每次运行到这里都会报错,而ann["category_id"]应该是0,不对应,所以这里改成output[mask == 1] = categories[ann["category_id"]+1] + 1,具体是什么原因,我后面在来研究吧。
2)get_parser函数
def get_parser():
parser = argparse.ArgumentParser(description="Keep only model in ckpt")
parser.add_argument(
"--dataset-name",
default="coco",
help="dataset to generate",
)
return parser
我把其他的都删除了,当然可以根据需求保留。
3) 根据我自己的数据路径修改
if __name__ == "__main__":
args = get_parser().parse_args()
dataset_dir = os.path.join(os.path.dirname(__file__), args.dataset_name)
if args.dataset_name == "imagedata":
thing_id_to_contiguous_id = _get_coco_instances_meta()["thing_dataset_id_to_contiguous_id"]
split_name = 'train'
annotation_name = "data/instance_{}.json"
else:
thing_id_to_contiguous_id = {1: 0}
split_name = 'train'
annotation_name = "annotations/{}_person.json"
for s in ["train"]:
create_coco_semantic_from_instance(
os.path.join(dataset_dir, "data/instance_{}.json".format(s)),
os.path.join(dataset_dir, "thing_{}".format(s)),
thing_id_to_contiguous_id
)
现在运行python prepare_thing_sem_from_instance.py --dataset-name /root/detectron2/AdelaiDet/datasets,在路径下生成thing_train文件夹,里面放的都是每个图片对应的pnz格式的文件。
二、训练自己的数据集
训练自己数据集跟在Detectron2训练自己的coco数据集步骤差不多,都是要先注册自己的数据集。我就对tools/train_net.py进行修改了。
1、注册自己的数据集
from detectron2.data import DatasetCatalog
from detectron2.data.datasets.coco import load_coco_json
#声明类别,尽量保持
CLASS_NAMES =["AA"]#如果没有标注background类,就不要写了,要不然后面提取报错
DATASET_ROOT = '/root/detectron2/AdelaiDet/datasets/imagedata'
ANN_ROOT = os.path.join(DATASET_ROOT, 'data')
TRAIN_PATH = os.path.join(DATASET_ROOT, 'train')
VAL_PATH = os.path.join(DATASET_ROOT, 'val')
TRAIN_JSON = os.path.join(ANN_ROOT, 'instance_train.json')
VAL_JSON = os.path.join(ANN_ROOT, 'instance_val.json')
# 声明数据集的子集
PREDEFINED_SPLITS_DATASET = {
"coco_my_train": (TRAIN_PATH, TRAIN_JSON),
"coco_my_val": (VAL_PATH, VAL_JSON),
}
# 注册数据集和元数据
def plain_register_dataset():
#训练集
DatasetCatalog.register("coco_my_train", lambda: load_coco_json(TRAIN_JSON, TRAIN_PATH))
MetadataCatalog.get("coco_my_train").set(thing_classes=CLASS_NAMES, # 可以选择开启,但是不能显示中文,这里需要注意,中文的话最好关闭
evaluator_type='coco', # 指定评估方式
json_file=TRAIN_JSON,
image_root=TRAIN_PATH)
#验证/测试集
DatasetCatalog.register("coco_my_val", lambda: load_coco_json(VAL_JSON, VAL_PATH))
MetadataCatalog.get("coco_my_val").set(thing_classes=CLASS_NAMES, # 可以选择开启,但是不能显示中文,这里需要注意,中文的话最好关闭
evaluator_type='coco', # 指定评估方式
json_file=VAL_JSON,
image_root=VAL_PATH)
2、在setup上添加自己的数据集和一些配置,可以加其他的。
def setup(args):
"""
Create configs and perform basic setups.
"""
cfg = get_cfg()
args.config_file='/root/detectron2/AdelaiDet/configs/BlendMask/R_50_1x.yaml'
cfg.merge_from_file(args.config_file)
cfg.merge_from_list(args.opts)
# 更改配置参数
cfg.DATASETS.TRAIN = ("coco_my_train",) # 训练数据集名称
cfg.DATASETS.TEST = ("coco_my_val",)
# cfg.MODEL.WEIGHTS = "/root/detectron2/AdelaiDet/model/R_50_1x.pth"
cfg.freeze()
default_setup(cfg, args)
rank = comm.get_rank()
setup_logger(cfg.OUTPUT_DIR, distributed_rank=rank, name="adet")
return cfg
3、在main函数的下面添加plain_register_dataset(),就OK了,但是这样还无法正常训练,还需要修改/AdelaiDet/adet/data下面的dataset_mapper里180行if的内容。
源代码是:
if self.basis_loss_on and self.is_train:
# load basis supervisions
if self.ann_set == "coco":
basis_sem_path = (
dataset_dict["file_name"]
.replace("train2017", "thing_train2017")
.replace("image/train", "thing_train")
)
修改成
if self.basis_loss_on and self.is_train:
# load basis supervisions
if self.ann_set == "coco":
basis_sem_path = (
dataset_dict["file_name"]
.replace("train", "thing_train")
)
将.replace("image/train", "thing_train")删除,要不然路径报错。
然后运行python tools/train_net.py --num-gpu 2(因为我就2个GPU,就是运行一次太慢了,要几天,后期还是要加GPU啊,跑不动)。
7.15更新
添加一个内容:
在注册数据的时候还可以在AdelaiDet/adet/data里builtin.py注册
_PREDEFINED_SPLITS_PIC = {
"your_train": ("yourpath/train", "yourpath/train_person.json"),
"your_val": ("yourpath/val", "yourpath/val_person.json"),
}
metadata_pic = {
"thing_classes": ["AAAA"]
}
其实这样还是挺方便的,不过一定要查看你的json文件里category_id是从即开始的,默认是从1开始,这样就没事,可是有的自己的数据集里没有标注背景,category_id就从0开始,这样在detectron2/data/datasets/coco.py的183行会报错,因此需要修改成
if id_map:
obj["category_id"] = id_map[obj["category_id"]]
#修改成
if id_map:
obj["category_id"] = id_map[obj["category_id"]+1]
这样id_map["category_id"+1]才能读到。