|
|
|
import copy
|
|
import json
|
|
import os
|
|
|
|
from detectron2.data import DatasetCatalog, MetadataCatalog
|
|
from detectron2.utils.file_io import PathManager
|
|
|
|
from .coco import load_coco_json, load_sem_seg
|
|
|
|
__all__ = ["register_coco_panoptic", "register_coco_panoptic_separated"]
|
|
|
|
|
|
def load_coco_panoptic_json(json_file, image_dir, gt_dir, meta):
|
|
"""
|
|
Args:
|
|
image_dir (str): path to the raw dataset. e.g., "~/coco/train2017".
|
|
gt_dir (str): path to the raw annotations. e.g., "~/coco/panoptic_train2017".
|
|
json_file (str): path to the json file. e.g., "~/coco/annotations/panoptic_train2017.json".
|
|
|
|
Returns:
|
|
list[dict]: a list of dicts in Detectron2 standard format. (See
|
|
`Using Custom Datasets </tutorials/datasets.html>`_ )
|
|
"""
|
|
|
|
def _convert_category_id(segment_info, meta):
|
|
if segment_info["category_id"] in meta["thing_dataset_id_to_contiguous_id"]:
|
|
segment_info["category_id"] = meta["thing_dataset_id_to_contiguous_id"][
|
|
segment_info["category_id"]
|
|
]
|
|
segment_info["isthing"] = True
|
|
else:
|
|
segment_info["category_id"] = meta["stuff_dataset_id_to_contiguous_id"][
|
|
segment_info["category_id"]
|
|
]
|
|
segment_info["isthing"] = False
|
|
return segment_info
|
|
|
|
with PathManager.open(json_file) as f:
|
|
json_info = json.load(f)
|
|
|
|
ret = []
|
|
for ann in json_info["annotations"]:
|
|
image_id = int(ann["image_id"])
|
|
|
|
|
|
|
|
|
|
image_file = os.path.join(image_dir, os.path.splitext(ann["file_name"])[0] + ".jpg")
|
|
label_file = os.path.join(gt_dir, ann["file_name"])
|
|
segments_info = [_convert_category_id(x, meta) for x in ann["segments_info"]]
|
|
ret.append(
|
|
{
|
|
"file_name": image_file,
|
|
"image_id": image_id,
|
|
"pan_seg_file_name": label_file,
|
|
"segments_info": segments_info,
|
|
}
|
|
)
|
|
assert len(ret), f"No images found in {image_dir}!"
|
|
assert PathManager.isfile(ret[0]["file_name"]), ret[0]["file_name"]
|
|
assert PathManager.isfile(ret[0]["pan_seg_file_name"]), ret[0]["pan_seg_file_name"]
|
|
return ret
|
|
|
|
|
|
def register_coco_panoptic(
|
|
name, metadata, image_root, panoptic_root, panoptic_json, instances_json=None
|
|
):
|
|
"""
|
|
Register a "standard" version of COCO panoptic segmentation dataset named `name`.
|
|
The dictionaries in this registered dataset follows detectron2's standard format.
|
|
Hence it's called "standard".
|
|
|
|
Args:
|
|
name (str): the name that identifies a dataset,
|
|
e.g. "coco_2017_train_panoptic"
|
|
metadata (dict): extra metadata associated with this dataset.
|
|
image_root (str): directory which contains all the images
|
|
panoptic_root (str): directory which contains panoptic annotation images in COCO format
|
|
panoptic_json (str): path to the json panoptic annotation file in COCO format
|
|
sem_seg_root (none): not used, to be consistent with
|
|
`register_coco_panoptic_separated`.
|
|
instances_json (str): path to the json instance annotation file
|
|
"""
|
|
panoptic_name = name
|
|
DatasetCatalog.register(
|
|
panoptic_name,
|
|
lambda: load_coco_panoptic_json(panoptic_json, image_root, panoptic_root, metadata),
|
|
)
|
|
MetadataCatalog.get(panoptic_name).set(
|
|
panoptic_root=panoptic_root,
|
|
image_root=image_root,
|
|
panoptic_json=panoptic_json,
|
|
json_file=instances_json,
|
|
evaluator_type="coco_panoptic_seg",
|
|
ignore_label=255,
|
|
label_divisor=1000,
|
|
**metadata,
|
|
)
|
|
|
|
|
|
def register_coco_panoptic_separated(
|
|
name, metadata, image_root, panoptic_root, panoptic_json, sem_seg_root, instances_json
|
|
):
|
|
"""
|
|
Register a "separated" version of COCO panoptic segmentation dataset named `name`.
|
|
The annotations in this registered dataset will contain both instance annotations and
|
|
semantic annotations, each with its own contiguous ids. Hence it's called "separated".
|
|
|
|
It follows the setting used by the PanopticFPN paper:
|
|
|
|
1. The instance annotations directly come from polygons in the COCO
|
|
instances annotation task, rather than from the masks in the COCO panoptic annotations.
|
|
|
|
The two format have small differences:
|
|
Polygons in the instance annotations may have overlaps.
|
|
The mask annotations are produced by labeling the overlapped polygons
|
|
with depth ordering.
|
|
|
|
2. The semantic annotations are converted from panoptic annotations, where
|
|
all "things" are assigned a semantic id of 0.
|
|
All semantic categories will therefore have ids in contiguous
|
|
range [1, #stuff_categories].
|
|
|
|
This function will also register a pure semantic segmentation dataset
|
|
named ``name + '_stuffonly'``.
|
|
|
|
Args:
|
|
name (str): the name that identifies a dataset,
|
|
e.g. "coco_2017_train_panoptic"
|
|
metadata (dict): extra metadata associated with this dataset.
|
|
image_root (str): directory which contains all the images
|
|
panoptic_root (str): directory which contains panoptic annotation images
|
|
panoptic_json (str): path to the json panoptic annotation file
|
|
sem_seg_root (str): directory which contains all the ground truth segmentation annotations.
|
|
instances_json (str): path to the json instance annotation file
|
|
"""
|
|
panoptic_name = name + "_separated"
|
|
DatasetCatalog.register(
|
|
panoptic_name,
|
|
lambda: merge_to_panoptic(
|
|
load_coco_json(instances_json, image_root, panoptic_name),
|
|
load_sem_seg(sem_seg_root, image_root),
|
|
),
|
|
)
|
|
MetadataCatalog.get(panoptic_name).set(
|
|
panoptic_root=panoptic_root,
|
|
image_root=image_root,
|
|
panoptic_json=panoptic_json,
|
|
sem_seg_root=sem_seg_root,
|
|
json_file=instances_json,
|
|
evaluator_type="coco_panoptic_seg",
|
|
ignore_label=255,
|
|
**metadata,
|
|
)
|
|
|
|
semantic_name = name + "_stuffonly"
|
|
DatasetCatalog.register(semantic_name, lambda: load_sem_seg(sem_seg_root, image_root))
|
|
MetadataCatalog.get(semantic_name).set(
|
|
sem_seg_root=sem_seg_root,
|
|
image_root=image_root,
|
|
evaluator_type="sem_seg",
|
|
ignore_label=255,
|
|
**metadata,
|
|
)
|
|
|
|
|
|
def merge_to_panoptic(detection_dicts, sem_seg_dicts):
|
|
"""
|
|
Create dataset dicts for panoptic segmentation, by
|
|
merging two dicts using "file_name" field to match their entries.
|
|
|
|
Args:
|
|
detection_dicts (list[dict]): lists of dicts for object detection or instance segmentation.
|
|
sem_seg_dicts (list[dict]): lists of dicts for semantic segmentation.
|
|
|
|
Returns:
|
|
list[dict] (one per input image): Each dict contains all (key, value) pairs from dicts in
|
|
both detection_dicts and sem_seg_dicts that correspond to the same image.
|
|
The function assumes that the same key in different dicts has the same value.
|
|
"""
|
|
results = []
|
|
sem_seg_file_to_entry = {x["file_name"]: x for x in sem_seg_dicts}
|
|
assert len(sem_seg_file_to_entry) > 0
|
|
|
|
for det_dict in detection_dicts:
|
|
dic = copy.copy(det_dict)
|
|
dic.update(sem_seg_file_to_entry[dic["file_name"]])
|
|
results.append(dic)
|
|
return results
|
|
|
|
|
|
if __name__ == "__main__":
|
|
"""
|
|
Test the COCO panoptic dataset loader.
|
|
|
|
Usage:
|
|
python -m detectron2.data.datasets.coco_panoptic \
|
|
path/to/image_root path/to/panoptic_root path/to/panoptic_json dataset_name 10
|
|
|
|
"dataset_name" can be "coco_2017_train_panoptic", or other
|
|
pre-registered ones
|
|
"""
|
|
from detectron2.utils.logger import setup_logger
|
|
from detectron2.utils.visualizer import Visualizer
|
|
import detectron2.data.datasets
|
|
import sys
|
|
from PIL import Image
|
|
import numpy as np
|
|
|
|
logger = setup_logger(name=__name__)
|
|
assert sys.argv[4] in DatasetCatalog.list()
|
|
meta = MetadataCatalog.get(sys.argv[4])
|
|
|
|
dicts = load_coco_panoptic_json(sys.argv[3], sys.argv[1], sys.argv[2], meta.as_dict())
|
|
logger.info("Done loading {} samples.".format(len(dicts)))
|
|
|
|
dirname = "coco-data-vis"
|
|
os.makedirs(dirname, exist_ok=True)
|
|
num_imgs_to_vis = int(sys.argv[5])
|
|
for i, d in enumerate(dicts):
|
|
img = np.array(Image.open(d["file_name"]))
|
|
visualizer = Visualizer(img, metadata=meta)
|
|
vis = visualizer.draw_dataset_dict(d)
|
|
fpath = os.path.join(dirname, os.path.basename(d["file_name"]))
|
|
vis.save(fpath)
|
|
if i + 1 >= num_imgs_to_vis:
|
|
break
|
|
|