基于Detrectron2的BlendMask训练自己的coco数据集

       最近看到《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]才能读到。

猜你喜欢

转载自blog.csdn.net/qq_33047753/article/details/107266773