From 1a5064dd524a391c71046eda33c38216adc53d4d Mon Sep 17 00:00:00 2001 From: wonjulee Date: Thu, 30 Nov 2023 15:42:23 +0900 Subject: [PATCH 1/7] enable mmdet importer --- .../plugins/data_formats/coco/base.py | 22 +++++++ .../plugins/data_formats/coco/format.py | 1 + src/datumaro/plugins/data_formats/mmdet.py | 57 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 src/datumaro/plugins/data_formats/mmdet.py diff --git a/src/datumaro/plugins/data_formats/coco/base.py b/src/datumaro/plugins/data_formats/coco/base.py index 2f8a0da771..a05d128421 100644 --- a/src/datumaro/plugins/data_formats/coco/base.py +++ b/src/datumaro/plugins/data_formats/coco/base.py @@ -88,6 +88,25 @@ def find_images_dir(rootpath: str, subset: str) -> str: return osp.join(rootpath, subset) +class MmdetDirPathExtracter(DirPathExtracter): + @staticmethod + def find_rootpath(path: str) -> str: + """Find root path from annotation json file path.""" + path = osp.abspath(path) + if osp.dirname(path).endswith(CocoPath.ANNOTATIONS_DIR): + return path.rsplit(CocoPath.ANNOTATIONS_DIR, maxsplit=1)[0] + raise DatasetImportError( + f"Annotation path ({path}) should be under the directory which is named {CocoPath.ANNOTATIONS_DIR}. " + "If not, Datumaro fails to find the root path for this dataset. " + "Please follow this instruction, https://github.com/cocodataset/cocoapi/blob/master/README.txt" + ) + + @staticmethod + def find_images_dir(rootpath: str, subset: str) -> str: + """Find images directory from the root path.""" + return osp.join(rootpath, subset) + + class _CocoBase(SubsetBase): """ Parses COCO annotations written in the following format: @@ -121,6 +140,9 @@ def __init__( elif coco_importer_type == CocoImporterType.roboflow: self._rootpath = RoboflowDirPathExtracter.find_rootpath(path) self._images_dir = RoboflowDirPathExtracter.find_images_dir(self._rootpath, subset) + elif coco_importer_type == CocoImporterType.mmdet: + self._rootpath = MmdetDirPathExtracter.find_rootpath(path) + self._images_dir = MmdetDirPathExtracter.find_images_dir(self._rootpath, subset) else: raise DatasetImportError(f"Not supported type: {coco_importer_type}") diff --git a/src/datumaro/plugins/data_formats/coco/format.py b/src/datumaro/plugins/data_formats/coco/format.py index ac55b6a8a2..cd417b9284 100644 --- a/src/datumaro/plugins/data_formats/coco/format.py +++ b/src/datumaro/plugins/data_formats/coco/format.py @@ -18,6 +18,7 @@ class CocoTask(Enum): class CocoImporterType(Enum): default = auto() roboflow = auto() + mmdet = auto() class CocoPath: diff --git a/src/datumaro/plugins/data_formats/mmdet.py b/src/datumaro/plugins/data_formats/mmdet.py new file mode 100644 index 0000000000..258e447c49 --- /dev/null +++ b/src/datumaro/plugins/data_formats/mmdet.py @@ -0,0 +1,57 @@ +# Copyright (C) 2023 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from typing import Optional +from glob import glob +import os.path as osp + +from datumaro.components.importer import ImportContext +from datumaro.plugins.data_formats.coco.base import _CocoBase +from datumaro.plugins.data_formats.coco.format import CocoImporterType, CocoTask +from datumaro.plugins.data_formats.coco.importer import CocoImporter + + +class MmdetCocoImporter(CocoImporter): + def __call__(self, path, stream: bool = False, **extra_params): + subset_paths = glob(osp.join(path, "**", "instances_*.json"), recursive=True) + + sources = [] + for subset_path in subset_paths: + subset_name = osp.basename(osp.dirname(subset_path)) + + options = dict(extra_params) + options["subset"] = subset_name + + if stream: + options["stream"] = True + + sources.append( + {"url": subset_path, "format": "mmdet_coco", "options": options} + ) + + return sources + + +class MmdetCocoBase(_CocoBase): + """ + Parses Roboflow COCO annotations written in the following format: + https://cocodataset.org/#format-data + """ + + def __init__( + self, + path, + *, + subset: Optional[str] = None, + stream: bool = False, + ctx: Optional[ImportContext] = None, + ): + super().__init__( + path, + task=CocoTask.instances, + coco_importer_type=CocoImporterType.mmdet, + subset=subset, + stream=stream, + ctx=ctx, + ) From 953da4186d2d8eb4973df823be213fcb7e09beae Mon Sep 17 00:00:00 2001 From: wonjulee Date: Fri, 1 Dec 2023 10:41:51 +0900 Subject: [PATCH 2/7] fix mmdet support --- src/datumaro/plugins/data_formats/mmdet.py | 5 +- src/datumaro/plugins/specs.json | 61 +++++----------------- 2 files changed, 16 insertions(+), 50 deletions(-) diff --git a/src/datumaro/plugins/data_formats/mmdet.py b/src/datumaro/plugins/data_formats/mmdet.py index 258e447c49..e35fdac3fd 100644 --- a/src/datumaro/plugins/data_formats/mmdet.py +++ b/src/datumaro/plugins/data_formats/mmdet.py @@ -6,7 +6,9 @@ from glob import glob import os.path as osp +from datumaro.components.dataset_base import DEFAULT_SUBSET_NAME from datumaro.components.importer import ImportContext + from datumaro.plugins.data_formats.coco.base import _CocoBase from datumaro.plugins.data_formats.coco.format import CocoImporterType, CocoTask from datumaro.plugins.data_formats.coco.importer import CocoImporter @@ -18,7 +20,8 @@ def __call__(self, path, stream: bool = False, **extra_params): sources = [] for subset_path in subset_paths: - subset_name = osp.basename(osp.dirname(subset_path)) + parts = osp.splitext(osp.basename(subset_path))[0].split("instances_", maxsplit=1) + subset_name = parts[1] if len(parts) == 2 else DEFAULT_SUBSET_NAME options = dict(extra_params) options["subset"] = subset_name diff --git a/src/datumaro/plugins/specs.json b/src/datumaro/plugins/specs.json index 8fabaa6d36..2a94111582 100644 --- a/src/datumaro/plugins/specs.json +++ b/src/datumaro/plugins/specs.json @@ -1,13 +1,4 @@ [ - { - "import_path": "datumaro.plugins.accuracy_checker_plugin.ac_launcher.AcLauncher", - "plugin_name": "ac", - "plugin_type": "Launcher", - "extra_deps": [ - "openvino.tools", - "tensorflow" - ] - }, { "import_path": "datumaro.plugins.configurable_validator.ConfigurableValidator", "plugin_name": "configurable", @@ -710,6 +701,18 @@ "plugin_type": "Importer", "extra_deps": [] }, + { + "import_path": "datumaro.plugins.data_formats.mmdet.MmdetCocoBase", + "plugin_name": "mmdet_coco", + "plugin_type": "DatasetBase", + "extra_deps": [] + }, + { + "import_path": "datumaro.plugins.data_formats.mmdet.MmdetCocoImporter", + "plugin_name": "mmdet_coco", + "plugin_type": "Importer", + "extra_deps": [] + }, { "import_path": "datumaro.plugins.data_formats.mnist.MnistBase", "plugin_name": "mnist", @@ -938,22 +941,6 @@ "plugin_type": "DatasetBase", "extra_deps": [] }, - { - "import_path": "datumaro.plugins.data_formats.roboflow.base_tfrecord.RoboflowTfrecordBase", - "plugin_name": "roboflow_tfrecord", - "plugin_type": "DatasetBase", - "extra_deps": [ - "tensorflow" - ] - }, - { - "import_path": "datumaro.plugins.data_formats.roboflow.base_tfrecord.RoboflowTfrecordImporter", - "plugin_name": "roboflow_tfrecord", - "plugin_type": "Importer", - "extra_deps": [ - "tensorflow" - ] - }, { "import_path": "datumaro.plugins.data_formats.roboflow.importer.RoboflowCocoImporter", "plugin_name": "roboflow_coco", @@ -1080,30 +1067,6 @@ "plugin_type": "Importer", "extra_deps": [] }, - { - "import_path": "datumaro.plugins.data_formats.tf_detection_api.base.TfDetectionApiBase", - "plugin_name": "tf_detection_api", - "plugin_type": "DatasetBase", - "extra_deps": [ - "tensorflow" - ] - }, - { - "import_path": "datumaro.plugins.data_formats.tf_detection_api.base.TfDetectionApiImporter", - "plugin_name": "tf_detection_api", - "plugin_type": "Importer", - "extra_deps": [ - "tensorflow" - ] - }, - { - "import_path": "datumaro.plugins.data_formats.tf_detection_api.exporter.TfDetectionApiExporter", - "plugin_name": "tf_detection_api", - "plugin_type": "Exporter", - "extra_deps": [ - "tensorflow" - ] - }, { "import_path": "datumaro.plugins.data_formats.vgg_face2.VggFace2Base", "plugin_name": "vgg_face2", From 1025b1ff470da9bf277086c0cf6d052244a4705f Mon Sep 17 00:00:00 2001 From: wonjulee Date: Fri, 1 Dec 2023 10:51:12 +0900 Subject: [PATCH 3/7] add test for mmdet coco --- .../annotations/instances_train.json | 64 +++++++++ .../mmdet_coco/annotations/instances_val.json | 101 +++++++++++++++ .../coco_dataset/mmdet_coco/train/a.jpg | Bin 0 -> 631 bytes .../assets/coco_dataset/mmdet_coco/val/b.jpg | Bin 0 -> 631 bytes tests/unit/data_formats/test_mmdet_coco.py | 121 ++++++++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 tests/assets/coco_dataset/mmdet_coco/annotations/instances_train.json create mode 100644 tests/assets/coco_dataset/mmdet_coco/annotations/instances_val.json create mode 100644 tests/assets/coco_dataset/mmdet_coco/train/a.jpg create mode 100644 tests/assets/coco_dataset/mmdet_coco/val/b.jpg create mode 100644 tests/unit/data_formats/test_mmdet_coco.py diff --git a/tests/assets/coco_dataset/mmdet_coco/annotations/instances_train.json b/tests/assets/coco_dataset/mmdet_coco/annotations/instances_train.json new file mode 100644 index 0000000000..251b7634c1 --- /dev/null +++ b/tests/assets/coco_dataset/mmdet_coco/annotations/instances_train.json @@ -0,0 +1,64 @@ +{ + "licenses":[ + { + "name":"", + "id":0, + "url":"" + } + ], + "info":{ + "contributor":"", + "date_created":"", + "description":"", + "url":"", + "version":"", + "year":"" + }, + "categories":[ + { + "id":1, + "name":"a", + "supercategory":"" + }, + { + "id":2, + "name":"b", + "supercategory":"" + }, + { + "id":4, + "name":"c", + "supercategory":"" + } + ], + "images":[ + { + "id":5, + "width":10, + "height":5, + "file_name":"a.jpg", + "license":0, + "flickr_url":"", + "coco_url":"", + "date_captured":0 + } + ], + "annotations":[ + { + "id":1, + "image_id":5, + "category_id":2, + "segmentation":[ + + ], + "area":3.0, + "bbox":[ + 2.0, + 2.0, + 3.0, + 1.0 + ], + "iscrowd":0 + } + ] + } \ No newline at end of file diff --git a/tests/assets/coco_dataset/mmdet_coco/annotations/instances_val.json b/tests/assets/coco_dataset/mmdet_coco/annotations/instances_val.json new file mode 100644 index 0000000000..2def276a53 --- /dev/null +++ b/tests/assets/coco_dataset/mmdet_coco/annotations/instances_val.json @@ -0,0 +1,101 @@ +{ + "licenses":[ + { + "name":"", + "id":0, + "url":"" + } + ], + "info":{ + "contributor":"", + "date_created":"", + "description":"", + "url":"", + "version":"", + "year":"" + }, + "categories":[ + { + "id":1, + "name":"a", + "supercategory":"" + }, + { + "id":2, + "name":"b", + "supercategory":"" + }, + { + "id":4, + "name":"c", + "supercategory":"" + } + ], + "images":[ + { + "id":40, + "width":5, + "height":10, + "file_name":"b.jpg", + "license":0, + "flickr_url":"", + "coco_url":"", + "date_captured":0 + } + ], + "annotations":[ + { + "id":1, + "image_id":40, + "category_id":1, + "segmentation":[ + [ + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 2.0, + 0.0, + 2.0 + ] + ], + "area":2.0, + "bbox":[ + 0.0, + 0.0, + 1.0, + 2.0 + ], + "iscrowd":0, + "attributes":{ + "x":1, + "y":"hello" + } + }, + { + "id":2, + "image_id":40, + "category_id":2, + "segmentation":{ + "counts":[ + 0, + 20, + 30 + ], + "size":[ + 10, + 5 + ] + }, + "area":20.0, + "bbox":[ + 0.0, + 0.0, + 1.0, + 9.0 + ], + "iscrowd":1 + } + ] + } \ No newline at end of file diff --git a/tests/assets/coco_dataset/mmdet_coco/train/a.jpg b/tests/assets/coco_dataset/mmdet_coco/train/a.jpg new file mode 100644 index 0000000000000000000000000000000000000000..222682d80bf9740d8eb672035ae34a240f949592 GIT binary patch literal 631 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<c1}I=;VrF4wW9Q)H;sz?% zD!{d!pzFb!U9xX3zTPI5o8roG<0MW4oqZMDikqloVbuf*=gfJ(V&YTRE(2~ znmD<{#3dx9RMpfqG__1j&CD$#!v`*nMGf}^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<c1}I=;VrF4wW9Q)H;sz?% zD!{d!pzFb!U9xX3zTPI5o8roG<0MW4oqZMDikqloVbuf*=gfJ(V&YTRE(2~ znmD<{#3dx9RMpfqG__1j&CD$#!v`*nMGf} Date: Fri, 1 Dec 2023 10:57:02 +0900 Subject: [PATCH 4/7] update specs.json --- src/datumaro/plugins/specs.json | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/datumaro/plugins/specs.json b/src/datumaro/plugins/specs.json index 2a94111582..cf74a24512 100644 --- a/src/datumaro/plugins/specs.json +++ b/src/datumaro/plugins/specs.json @@ -1,4 +1,13 @@ [ + { + "import_path": "datumaro.plugins.accuracy_checker_plugin.ac_launcher.AcLauncher", + "plugin_name": "ac", + "plugin_type": "Launcher", + "extra_deps": [ + "openvino.tools", + "tensorflow" + ] + }, { "import_path": "datumaro.plugins.configurable_validator.ConfigurableValidator", "plugin_name": "configurable", @@ -941,6 +950,22 @@ "plugin_type": "DatasetBase", "extra_deps": [] }, + { + "import_path": "datumaro.plugins.data_formats.roboflow.base_tfrecord.RoboflowTfrecordBase", + "plugin_name": "roboflow_tfrecord", + "plugin_type": "DatasetBase", + "extra_deps": [ + "tensorflow" + ] + }, + { + "import_path": "datumaro.plugins.data_formats.roboflow.base_tfrecord.RoboflowTfrecordImporter", + "plugin_name": "roboflow_tfrecord", + "plugin_type": "Importer", + "extra_deps": [ + "tensorflow" + ] + }, { "import_path": "datumaro.plugins.data_formats.roboflow.importer.RoboflowCocoImporter", "plugin_name": "roboflow_coco", @@ -1067,6 +1092,30 @@ "plugin_type": "Importer", "extra_deps": [] }, + { + "import_path": "datumaro.plugins.data_formats.tf_detection_api.base.TfDetectionApiBase", + "plugin_name": "tf_detection_api", + "plugin_type": "DatasetBase", + "extra_deps": [ + "tensorflow" + ] + }, + { + "import_path": "datumaro.plugins.data_formats.tf_detection_api.base.TfDetectionApiImporter", + "plugin_name": "tf_detection_api", + "plugin_type": "Importer", + "extra_deps": [ + "tensorflow" + ] + }, + { + "import_path": "datumaro.plugins.data_formats.tf_detection_api.exporter.TfDetectionApiExporter", + "plugin_name": "tf_detection_api", + "plugin_type": "Exporter", + "extra_deps": [ + "tensorflow" + ] + }, { "import_path": "datumaro.plugins.data_formats.vgg_face2.VggFace2Base", "plugin_name": "vgg_face2", From 66a9f3f71108a656590acc99c424f941bc095e59 Mon Sep 17 00:00:00 2001 From: wonjulee Date: Fri, 1 Dec 2023 12:09:37 +0900 Subject: [PATCH 5/7] add doc for mmdet coco --- CHANGELOG.md | 4 ++ .../docs/data-formats/formats/index.rst | 5 +++ .../source/docs/data-formats/formats/mmdet.md | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 docs/source/docs/data-formats/formats/mmdet.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cec1c9a82..c9754dda07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## \[Unreleased\] +### New features +- Support MMDetection COCO format + () + ### Enhancements - Optimize Python import to make CLI entrypoint faster () diff --git a/docs/source/docs/data-formats/formats/index.rst b/docs/source/docs/data-formats/formats/index.rst index a15be1dcfa..c03b9c0257 100644 --- a/docs/source/docs/data-formats/formats/index.rst +++ b/docs/source/docs/data-formats/formats/index.rst @@ -32,6 +32,7 @@ Supported Data Formats mapillary_vistas market1501 mars + mmdet mnist mot mots @@ -141,6 +142,10 @@ Supported Data Formats * `Format specification `_ * `Dataset example `_ * `Format documentation `_ +* MMDet-COCO (``detection``, ``segmentation``) + * `Format specification `_ + * `Dataset example `_ + * `Format documentation `_ * MNIST (``classification``) * `Format specification `_ * `Dataset example `_ diff --git a/docs/source/docs/data-formats/formats/mmdet.md b/docs/source/docs/data-formats/formats/mmdet.md new file mode 100644 index 0000000000..e064341638 --- /dev/null +++ b/docs/source/docs/data-formats/formats/mmdet.md @@ -0,0 +1,41 @@ +# MMDetection COCO + +## Format specification + +[MMDetection](https://mmdetection.readthedocs.io/en/latest/) is a training framework for object detection and instance segmentation tasks, providing a modular and flexible architecture that supports various state-of-the-art models, datasets, and training techniques. MMDetection has gained popularity in the research community for its comprehensive features and ease of use in developing and benchmarking object detection algorithms. +MMDetection specifies their COCO format [here](https://mmdetection.readthedocs.io/en/latest/user_guides/dataset_prepare.html). + +Most of available tasks or formats are similar to the [original COCO format](./formats/coco), while only the image directories are separated with respect to subsets. +In this document, we just describe the directory structure of MMDetection COCO format as per [here](https://mmdetection.readthedocs.io/en/latest/user_guides/dataset_prepare.html). +MMDetection COCO dataset directory should have the following structure: + + +``` +└─ Dataset/ + ├── / + │ ├── + │ ├── + │ └── ... + ├── / + │ ├── + │ ├── + │ └── ... + └── annotations/ + ├── instances_.json + └── ... +``` + +### Import using CLI + +``` bash +datum project create +datum project import --format mmdet_coco +``` + +### Import using Python API + +```python +import datumaro as dm + +dataset = dm.Dataset.import_from('', 'mmdet_coco') +``` From 9f0d15585d36a12d6ce411e45aa0837a43a69b84 Mon Sep 17 00:00:00 2001 From: wonjulee Date: Fri, 1 Dec 2023 12:15:37 +0900 Subject: [PATCH 6/7] fix pre-comit --- src/datumaro/plugins/data_formats/mmdet.py | 9 +++------ tests/unit/data_formats/test_mmdet_coco.py | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/datumaro/plugins/data_formats/mmdet.py b/src/datumaro/plugins/data_formats/mmdet.py index e35fdac3fd..302721a106 100644 --- a/src/datumaro/plugins/data_formats/mmdet.py +++ b/src/datumaro/plugins/data_formats/mmdet.py @@ -2,13 +2,12 @@ # # SPDX-License-Identifier: MIT -from typing import Optional -from glob import glob import os.path as osp +from glob import glob +from typing import Optional from datumaro.components.dataset_base import DEFAULT_SUBSET_NAME from datumaro.components.importer import ImportContext - from datumaro.plugins.data_formats.coco.base import _CocoBase from datumaro.plugins.data_formats.coco.format import CocoImporterType, CocoTask from datumaro.plugins.data_formats.coco.importer import CocoImporter @@ -29,9 +28,7 @@ def __call__(self, path, stream: bool = False, **extra_params): if stream: options["stream"] = True - sources.append( - {"url": subset_path, "format": "mmdet_coco", "options": options} - ) + sources.append({"url": subset_path, "format": "mmdet_coco", "options": options}) return sources diff --git a/tests/unit/data_formats/test_mmdet_coco.py b/tests/unit/data_formats/test_mmdet_coco.py index 266b6b34ae..399e6a8d29 100644 --- a/tests/unit/data_formats/test_mmdet_coco.py +++ b/tests/unit/data_formats/test_mmdet_coco.py @@ -20,7 +20,6 @@ from tests.utils.assets import get_test_asset_path from tests.utils.test_utils import compare_datasets - DUMMY_DATASET_DIR = get_test_asset_path("coco_dataset", "mmdet_coco") From d1f57b83125b2390d8cb74275719814de56bc8f4 Mon Sep 17 00:00:00 2001 From: wonjulee Date: Fri, 1 Dec 2023 14:40:19 +0900 Subject: [PATCH 7/7] separate format detector from coco --- src/datumaro/plugins/data_formats/mmdet.py | 30 +++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/datumaro/plugins/data_formats/mmdet.py b/src/datumaro/plugins/data_formats/mmdet.py index 302721a106..3ff59f8352 100644 --- a/src/datumaro/plugins/data_formats/mmdet.py +++ b/src/datumaro/plugins/data_formats/mmdet.py @@ -7,6 +7,7 @@ from typing import Optional from datumaro.components.dataset_base import DEFAULT_SUBSET_NAME +from datumaro.components.format_detection import FormatDetectionConfidence, FormatDetectionContext from datumaro.components.importer import ImportContext from datumaro.plugins.data_formats.coco.base import _CocoBase from datumaro.plugins.data_formats.coco.format import CocoImporterType, CocoTask @@ -14,16 +15,30 @@ class MmdetCocoImporter(CocoImporter): + @classmethod + def detect( + cls, + context: FormatDetectionContext, + ) -> FormatDetectionConfidence: + ann_paths = context.require_files("annotations/instances_*.json") + + for ann_path in ann_paths: + subset_name = cls._get_subset_name(ann_path) + + with context.require_any(): + with context.alternative(): + image_files = osp.join(subset_name, "*.jpg") + context.require_file(f"{image_files}") + + return FormatDetectionConfidence.MEDIUM + def __call__(self, path, stream: bool = False, **extra_params): subset_paths = glob(osp.join(path, "**", "instances_*.json"), recursive=True) sources = [] for subset_path in subset_paths: - parts = osp.splitext(osp.basename(subset_path))[0].split("instances_", maxsplit=1) - subset_name = parts[1] if len(parts) == 2 else DEFAULT_SUBSET_NAME - options = dict(extra_params) - options["subset"] = subset_name + options["subset"] = self._get_subset_name(subset_path) if stream: options["stream"] = True @@ -32,6 +47,13 @@ def __call__(self, path, stream: bool = False, **extra_params): return sources + @classmethod + def _get_subset_name(cls, subset_path: str): + parts = osp.splitext(osp.basename(subset_path))[0].split("instances_", maxsplit=1) + subset_name = parts[1] if len(parts) == 2 else DEFAULT_SUBSET_NAME + + return subset_name + class MmdetCocoBase(_CocoBase): """