Source code for datumaro.plugins.data_formats.common_semantic_segmentation

# Copyright (C) 2022-2023 Intel Corporation
#
# SPDX-License-Identifier: MIT

import errno
import os.path as osp
from typing import List, Optional

import numpy as np

from datumaro.components.annotation import (
    AnnotationType,
    ExtractedMask,
    LabelCategories,
    MaskCategories,
)
from datumaro.components.dataset_base import DatasetItem, SubsetBase
from datumaro.components.format_detection import FormatDetectionConfidence, FormatDetectionContext
from datumaro.components.importer import ImportContext, Importer, with_subset_dirs
from datumaro.components.media import Image
from datumaro.util.image import find_images
from datumaro.util.mask_tools import generate_colormap, lazy_mask
from datumaro.util.meta_file_util import DATASET_META_FILE, is_meta_file, parse_meta_file


[docs] class CommonSemanticSegmentationPath: MASKS_DIR = "masks" IMAGES_DIR = "images"
[docs] def make_categories(label_map=None): if label_map is None: return {} categories = {} label_categories = LabelCategories() for label in label_map: label_categories.add(label) categories[AnnotationType.label] = label_categories has_colors = any(v is not None for v in label_map.values()) if not has_colors: # generate new colors colormap = generate_colormap(len(label_map)) else: # only copy defined colors label_id = lambda label: label_categories.find(label)[0] colormap = {label_id(name): (desc[0], desc[1], desc[2]) for name, desc in label_map.items()} mask_categories = MaskCategories(colormap) mask_categories.inverse_colormap # pylint: disable=pointless-statement categories[AnnotationType.mask] = mask_categories return categories
[docs] class CommonSemanticSegmentationBase(SubsetBase): def __init__( self, path: str, *, image_prefix: str = "", mask_prefix: str = "", subset: Optional[str] = None, ctx: Optional[ImportContext] = None, ): if not osp.isdir(path): raise NotADirectoryError(errno.ENOTDIR, "Can't find dataset directory", path) super().__init__(subset=subset, ctx=ctx) self._image_prefix = image_prefix self._mask_prefix = mask_prefix meta_file = osp.join(path, DATASET_META_FILE) if is_meta_file(meta_file): self._root_dir = osp.dirname(meta_file) label_map = parse_meta_file(meta_file) self._categories = make_categories(label_map) else: raise FileNotFoundError(errno.ENOENT, "Dataset meta info file was not found", path) self._items = list(self._load_items().values()) def _load_items(self): items = {} image_dir = osp.join(self._root_dir, CommonSemanticSegmentationPath.IMAGES_DIR) if osp.isdir(image_dir): images = { osp.splitext(osp.relpath(p, image_dir))[0].replace("\\", "/")[ len(self._image_prefix) : ]: p for p in find_images(image_dir, recursive=True) if osp.basename(p).startswith(self._image_prefix) } else: images = {} mask_dir = osp.join(self._root_dir, CommonSemanticSegmentationPath.MASKS_DIR) masks = [ mask_path for mask_path in find_images(mask_dir, recursive=True) if osp.basename(mask_path).startswith(self._mask_prefix) ] for mask_path in masks: item_id = osp.splitext(osp.basename(mask_path))[0][len(self._mask_prefix) :] image = images.get(item_id) if image: image = Image.from_file(path=image) annotations = [] index_mask = lazy_mask( mask_path, self._categories[AnnotationType.mask].inverse_colormap ) np_mask = index_mask() # loading mask through cache classes = np.unique(np_mask) for label_id in classes: annotations.append( ExtractedMask( index_mask=index_mask, index=label_id, label=label_id, ) ) self._ann_types.add(AnnotationType.mask) items[item_id] = DatasetItem( id=item_id, subset=self._subset, media=image, annotations=annotations ) return items @staticmethod def _lazy_extract_mask(mask, c): return lambda: mask == c
[docs] class CommonSemanticSegmentationImporter(Importer): """CommonSemanticSegmentation is introduced in the accuracy checker tool of OpenVINO™ to cover a general format of datasets for semantic segmentation task. This should have the following structure: - Dataset/ - dataset_meta.json # a list of labels - images/ - <img1>.png - <img2>.png - ... - masks/ - <img1>.png - <img2>.png - ... """
[docs] @classmethod def build_cmdline_parser(cls, **kwargs): parser = super().build_cmdline_parser(**kwargs) parser.add_argument("--image-prefix", default="", help="Image prefix (default: '')") parser.add_argument("--mask-prefix", default="", help="Mask prefix (default: '')") return parser
[docs] @classmethod def detect(cls, context: FormatDetectionContext) -> FormatDetectionConfidence: context.require_file(DATASET_META_FILE) context.require_file(osp.join(CommonSemanticSegmentationPath.IMAGES_DIR, "**", "*")) context.require_file(osp.join(CommonSemanticSegmentationPath.MASKS_DIR, "**", "*")) return FormatDetectionConfidence.MEDIUM
[docs] @classmethod def find_sources(cls, path): return [{"url": path, "format": "common_semantic_segmentation"}]
[docs] @classmethod def get_file_extensions(cls) -> List[str]: return [osp.splitext(DATASET_META_FILE)[1]]
[docs] @with_subset_dirs class CommonSemanticSegmentationWithSubsetDirsImporter(CommonSemanticSegmentationImporter): """It supports the following subset sub-directory structure for CommonSemanticSegmentation. .. code-block:: Dataset/ └─ <split: train,val, ...> ├── dataset_meta.json # a list of labels ├── images/ │ ├── <img1>.png │ ├── <img2>.png │ └── ... └── masks/ ├── <img1>.png ├── <img2>.png └── ... Then, the imported dataset will have train, val, ... CommonSemanticSegmentation subsets. """