diff --git a/.dev_scripts/benchmark_inference_fps.py b/.dev_scripts/benchmark_inference_fps.py index 81dcd6b1a1f..6099ed139b2 100644 --- a/.dev_scripts/benchmark_inference_fps.py +++ b/.dev_scripts/benchmark_inference_fps.py @@ -3,9 +3,10 @@ import os import os.path as osp -import mmcv -from mmcv import Config, DictAction -from mmcv.runner import init_dist +from mmengine.config import Config, DictAction +from mmengine.dist import init_dist +from mmengine.fileio import dump +from mmengine.utils import mkdir_or_exist from terminaltables import GithubFlavoredMarkdownTable from tools.analysis_tools.benchmark import repeat_measure_inference_speed @@ -164,7 +165,7 @@ def results2markdown(result_dict): result_dict[cfg_path] = dict(fps=0, ms_times_pre_image=0) if args.out: - mmcv.mkdir_or_exist(args.out) - mmcv.dump(result_dict, osp.join(args.out, 'batch_inference_fps.json')) + mkdir_or_exist(args.out) + dump(result_dict, osp.join(args.out, 'batch_inference_fps.json')) results2markdown(result_dict) diff --git a/.dev_scripts/benchmark_test_image.py b/.dev_scripts/benchmark_test_image.py index e45d4577c9e..add682b4391 100644 --- a/.dev_scripts/benchmark_test_image.py +++ b/.dev_scripts/benchmark_test_image.py @@ -6,6 +6,7 @@ import mmcv from mmengine.config import Config from mmengine.logging import MMLogger +from mmengine.utils import mkdir_or_exist from mmdet.apis import inference_detector, init_detector from mmdet.registry import VISUALIZERS @@ -58,7 +59,7 @@ def inference_model(config_name, checkpoint, visualizer, args, logger=None): out_file = None if args.out_dir is not None: out_dir = args.out_dir - mmcv.mkdir_or_exist(out_dir) + mkdir_or_exist(out_dir) out_file = osp.join( out_dir, diff --git a/.dev_scripts/download_checkpoints.py b/.dev_scripts/download_checkpoints.py index facc5c18d00..fa5ef9dc016 100644 --- a/.dev_scripts/download_checkpoints.py +++ b/.dev_scripts/download_checkpoints.py @@ -6,9 +6,9 @@ import os.path as osp from multiprocessing import Pool -import mmcv import torch -from mmcv import Config +from mmengine.config import Config +from mmengine.utils import mkdir_or_exist def download(url, out_file, min_bytes=math.pow(1024, 2), progress=True): @@ -52,7 +52,7 @@ def parse_args(): if __name__ == '__main__': args = parse_args() - mmcv.mkdir_or_exist(args.out) + mkdir_or_exist(args.out) cfg = Config.fromfile(args.config) diff --git a/.dev_scripts/gather_models.py b/.dev_scripts/gather_models.py index 42e615c7154..841dace7350 100644 --- a/.dev_scripts/gather_models.py +++ b/.dev_scripts/gather_models.py @@ -7,9 +7,11 @@ import subprocess from collections import OrderedDict -import mmcv import torch import yaml +from mmengine.config import Config +from mmengine.fileio import dump +from mmengine.utils import mkdir_or_exist, scandir def ordered_yaml_dump(data, stream=None, Dumper=yaml.SafeDumper, **kwds): @@ -49,12 +51,12 @@ def process_checkpoint(in_file, out_file): def is_by_epoch(config): - cfg = mmcv.Config.fromfile('./configs/' + config) + cfg = Config.fromfile('./configs/' + config) return cfg.runner.type == 'EpochBasedRunner' def get_final_epoch_or_iter(config): - cfg = mmcv.Config.fromfile('./configs/' + config) + cfg = Config.fromfile('./configs/' + config) if cfg.runner.type == 'EpochBasedRunner': return cfg.runner.max_epochs else: @@ -71,7 +73,7 @@ def get_best_epoch_or_iter(exp_dir): def get_real_epoch_or_iter(config): - cfg = mmcv.Config.fromfile('./configs/' + config) + cfg = Config.fromfile('./configs/' + config) if cfg.runner.type == 'EpochBasedRunner': epoch = cfg.runner.max_epochs if cfg.data.train.type == 'RepeatDataset': @@ -142,7 +144,7 @@ def get_dataset_name(config): WIDERFaceDataset='WIDER Face', OpenImagesDataset='OpenImagesDataset', OpenImagesChallengeDataset='OpenImagesChallengeDataset') - cfg = mmcv.Config.fromfile('./configs/' + config) + cfg = Config.fromfile('./configs/' + config) return name_map[cfg.dataset_type] @@ -226,10 +228,10 @@ def main(): args = parse_args() models_root = args.root models_out = args.out - mmcv.mkdir_or_exist(models_out) + mkdir_or_exist(models_out) # find all models in the root directory to be gathered - raw_configs = list(mmcv.scandir('./configs', '.py', recursive=True)) + raw_configs = list(scandir('./configs', '.py', recursive=True)) # filter configs that is not trained in the experiments dir used_configs = [] @@ -261,7 +263,7 @@ def main(): log_json_path = list( sorted(glob.glob(osp.join(exp_dir, '*.log.json'))))[-1] log_txt_path = list(sorted(glob.glob(osp.join(exp_dir, '*.log'))))[-1] - cfg = mmcv.Config.fromfile('./configs/' + used_config) + cfg = Config.fromfile('./configs/' + used_config) results_lut = cfg.evaluation.metric if not isinstance(results_lut, list): results_lut = [results_lut] @@ -292,7 +294,7 @@ def main(): publish_model_infos = [] for model in model_infos: model_publish_dir = osp.join(models_out, model['config'].rstrip('.py')) - mmcv.mkdir_or_exist(model_publish_dir) + mkdir_or_exist(model_publish_dir) model_name = osp.split(model['config'])[-1].split('.')[0] @@ -328,7 +330,7 @@ def main(): models = dict(models=publish_model_infos) print(f'Totally gathered {len(publish_model_infos)} models') - mmcv.dump(models, osp.join(models_out, 'model_info.json')) + dump(models, osp.join(models_out, 'model_info.json')) pwc_files = convert_model_info_to_pwc(publish_model_infos) for name in pwc_files: diff --git a/.dev_scripts/gather_test_benchmark_metric.py b/.dev_scripts/gather_test_benchmark_metric.py index 07c6bf4258b..951bfe6f0dd 100644 --- a/.dev_scripts/gather_test_benchmark_metric.py +++ b/.dev_scripts/gather_test_benchmark_metric.py @@ -3,8 +3,9 @@ import glob import os.path as osp -import mmcv -from mmcv import Config +from mmengine.config import Config +from mmengine.fileio import dump, load +from mmengine.utils import mkdir_or_exist def parse_args(): @@ -49,7 +50,7 @@ def parse_args(): if len(json_list) > 0: log_json_path = list(sorted(json_list))[-1] - metric = mmcv.load(log_json_path) + metric = load(log_json_path) if config in metric.get('config', {}): new_metrics = dict() @@ -86,9 +87,8 @@ def parse_args(): print(f'{config} not exist dir: {metric_json_dir}') if metrics_out: - mmcv.mkdir_or_exist(metrics_out) - mmcv.dump(result_dict, - osp.join(metrics_out, 'batch_test_metric_info.json')) + mkdir_or_exist(metrics_out) + dump(result_dict, osp.join(metrics_out, 'batch_test_metric_info.json')) if not args.not_show: print('===================================') for config_name, metrics in result_dict.items(): diff --git a/.dev_scripts/gather_train_benchmark_metric.py b/.dev_scripts/gather_train_benchmark_metric.py index f9c6c804fcc..3d4c9cfcb41 100644 --- a/.dev_scripts/gather_train_benchmark_metric.py +++ b/.dev_scripts/gather_train_benchmark_metric.py @@ -3,8 +3,10 @@ import glob import os.path as osp -import mmcv from gather_models import get_final_results +from mmengine.config import Config +from mmengine.fileio import dump +from mmengine.utils import mkdir_or_exist try: import xlrd @@ -78,7 +80,7 @@ def parse_args(): result_path = osp.join(root_path, config_name) if osp.exists(result_path): # 1 read config - cfg = mmcv.Config.fromfile(config) + cfg = Config.fromfile(config) total_epochs = cfg.runner.max_epochs final_results = cfg.evaluation.metric if not isinstance(final_results, list): @@ -136,9 +138,8 @@ def parse_args(): # 4 save or print results if metrics_out: - mmcv.mkdir_or_exist(metrics_out) - mmcv.dump(result_dict, - osp.join(metrics_out, 'model_metric_info.json')) + mkdir_or_exist(metrics_out) + dump(result_dict, osp.join(metrics_out, 'model_metric_info.json')) if not args.not_show: print('===================================') for config_name, metrics in result_dict.items(): diff --git a/.dev_scripts/test_init_backbone.py b/.dev_scripts/test_init_backbone.py index 862f4afbf20..3bb26a8bc89 100644 --- a/.dev_scripts/test_init_backbone.py +++ b/.dev_scripts/test_init_backbone.py @@ -5,8 +5,9 @@ from os.path import dirname, exists, join import pytest -from mmcv import Config, ProgressBar -from mmcv.runner import _load_checkpoint +from mmengine.config import Config +from mmengine.runner import CheckpointLoader +from mmengine.utils import ProgressBar from mmdet.models import build_detector @@ -28,7 +29,6 @@ def _get_config_directory(): def _get_config_module(fname): """Load a configuration as a python module.""" - from mmcv import Config config_dpath = _get_config_directory() config_fpath = join(config_dpath, fname) config_mod = Config.fromfile(config_fpath) @@ -91,8 +91,8 @@ def _check_backbone(config, print_cfg=True): """Check out backbone whether successfully load pretrained model, by using `backbone.init_cfg`. - First, using `mmcv._load_checkpoint` to load the checkpoint without - loading models. + First, using `CheckpointLoader.load_checkpoint` to load the checkpoint + without loading models. Then, using `build_detector` to build models, and using `model.init_weights()` to initialize the parameters. Finally, assert weights and bias of each layer loaded from pretrained @@ -120,7 +120,7 @@ def _check_backbone(config, print_cfg=True): if init_cfg is None or init_cfg.get('type') != 'Pretrained': init_flag = False if init_flag: - checkpoint = _load_checkpoint(init_cfg.checkpoint) + checkpoint = CheckpointLoader.load_checkpoint(init_cfg.checkpoint) if 'state_dict' in checkpoint: state_dict = checkpoint['state_dict'] else: diff --git a/demo/create_result_gif.py b/demo/create_result_gif.py index 60b47967dfc..8e56a33a1a3 100644 --- a/demo/create_result_gif.py +++ b/demo/create_result_gif.py @@ -7,6 +7,7 @@ import matplotlib.pyplot as plt import mmcv import numpy as np +from mmengine.utils import scandir try: import imageio @@ -80,7 +81,7 @@ def create_frame_by_matplotlib(image_dir, images_list = [] for dir_names in result_dir_names: - images_list.append(mmcv.scandir(osp.join(image_dir, dir_names))) + images_list.append(scandir(osp.join(image_dir, dir_names))) frames = [] for paths in _generate_batch_data(zip(*images_list), nrows): diff --git a/demo/video_demo.py b/demo/video_demo.py index b876772808c..85ade13179d 100644 --- a/demo/video_demo.py +++ b/demo/video_demo.py @@ -4,6 +4,7 @@ import cv2 import mmcv from mmcv.transforms import Compose +from mmengine.utils import track_iter_progress from mmdet.apis import inference_detector, init_detector from mmdet.registry import VISUALIZERS @@ -60,7 +61,7 @@ def main(): args.out, fourcc, video_reader.fps, (video_reader.width, video_reader.height)) - for frame in mmcv.track_iter_progress(video_reader): + for frame in track_iter_progress(video_reader): result = inference_detector(model, frame, test_pipeline=test_pipeline) visualizer.add_datasample( name='video', diff --git a/demo/video_gpuaccel_demo.py b/demo/video_gpuaccel_demo.py index dd89cc74a22..13a9006e749 100644 --- a/demo/video_gpuaccel_demo.py +++ b/demo/video_gpuaccel_demo.py @@ -7,6 +7,7 @@ import torch import torch.nn as nn from mmcv.transforms import Compose +from mmengine.utils import track_iter_progress from mmdet.apis import init_detector from mmdet.registry import VISUALIZERS @@ -113,7 +114,7 @@ def main(): with torch.no_grad(): for i, (frame_resize, frame_origin) in enumerate( - zip(mmcv.track_iter_progress(video_resize), video_origin)): + zip(track_iter_progress(video_resize), video_origin)): data = pack_data(frame_resize, batch_input_shape, ori_shape) result = model.test_step([data])[0] diff --git a/mmdet/datasets/builder.py b/mmdet/datasets/builder.py index 787ea8bd256..309fb50a7f6 100644 --- a/mmdet/datasets/builder.py +++ b/mmdet/datasets/builder.py @@ -49,7 +49,10 @@ def _concat_dataset(cfg, default_args=None): # TODO: Need to refactor later def build_dataset(cfg, default_args=None): - from .dataset_wrappers import ClassBalancedDataset, MultiImageMixDataset + from mmengine.dataset import ClassBalancedDataset + + from .dataset_wrappers import MultiImageMixDataset + if cfg['type'] == 'ClassBalancedDataset': dataset = ClassBalancedDataset( build_dataset(cfg['dataset'], default_args), cfg['oversample_thr']) diff --git a/mmdet/datasets/transforms/__init__.py b/mmdet/datasets/transforms/__init__.py index 860fdbfab00..a3b0dd8bdbb 100644 --- a/mmdet/datasets/transforms/__init__.py +++ b/mmdet/datasets/transforms/__init__.py @@ -3,8 +3,7 @@ from .colorspace import (AutoContrast, Brightness, Color, ColorTransform, Contrast, Equalize, Invert, Posterize, Sharpness, Solarize, SolarizeAdd) -from .formatting import (ImageToTensor, PackDetInputs, ToDataContainer, - ToTensor, Transpose) +from .formatting import ImageToTensor, PackDetInputs, ToTensor, Transpose from .geometric import (GeomTransform, Rotate, ShearX, ShearY, TranslateX, TranslateY) from .instaboost import InstaBoost @@ -19,16 +18,16 @@ from .wrappers import MultiBranch, RandomOrder __all__ = [ - 'PackDetInputs', 'ToTensor', 'ImageToTensor', 'ToDataContainer', - 'Transpose', 'LoadImageFromNDArray', 'LoadAnnotations', - 'LoadPanopticAnnotations', 'LoadMultiChannelImageFromFiles', - 'LoadProposals', 'Resize', 'RandomFlip', 'RandomCrop', 'Normalize', - 'SegRescale', 'MinIoURandomCrop', 'Expand', 'PhotoMetricDistortion', - 'Albu', 'InstaBoost', 'RandomCenterCropPad', 'AutoAugment', 'CutOut', - 'ShearX', 'ShearY', 'Rotate', 'Color', 'Equalize', 'Brightness', - 'Contrast', 'TranslateX', 'TranslateY', 'RandomShift', 'Mosaic', 'MixUp', - 'RandomAffine', 'YOLOXHSVRandomAug', 'CopyPaste', 'FilterAnnotations', - 'Pad', 'GeomTransform', 'ColorTransform', 'RandAugment', 'Sharpness', - 'Solarize', 'SolarizeAdd', 'Posterize', 'AutoContrast', 'Invert', - 'MultiBranch', 'RandomErasing', 'LoadEmptyAnnotations', 'RandomOrder' + 'PackDetInputs', 'ToTensor', 'ImageToTensor', 'Transpose', + 'LoadImageFromNDArray', 'LoadAnnotations', 'LoadPanopticAnnotations', + 'LoadMultiChannelImageFromFiles', 'LoadProposals', 'Resize', 'RandomFlip', + 'RandomCrop', 'Normalize', 'SegRescale', 'MinIoURandomCrop', 'Expand', + 'PhotoMetricDistortion', 'Albu', 'InstaBoost', 'RandomCenterCropPad', + 'AutoAugment', 'CutOut', 'ShearX', 'ShearY', 'Rotate', 'Color', 'Equalize', + 'Brightness', 'Contrast', 'TranslateX', 'TranslateY', 'RandomShift', + 'Mosaic', 'MixUp', 'RandomAffine', 'YOLOXHSVRandomAug', 'CopyPaste', + 'FilterAnnotations', 'Pad', 'GeomTransform', 'ColorTransform', + 'RandAugment', 'Sharpness', 'Solarize', 'SolarizeAdd', 'Posterize', + 'AutoContrast', 'Invert', 'MultiBranch', 'RandomErasing', + 'LoadEmptyAnnotations', 'RandomOrder' ] diff --git a/mmdet/datasets/transforms/augment_wrappers.py b/mmdet/datasets/transforms/augment_wrappers.py index cacfc6caccf..8a3b106bb80 100644 --- a/mmdet/datasets/transforms/augment_wrappers.py +++ b/mmdet/datasets/transforms/augment_wrappers.py @@ -2,9 +2,9 @@ from typing import List, Optional, Union import numpy as np -from mmcv import ConfigDict from mmcv.transforms import RandomChoice from mmcv.transforms.utils import cache_randomness +from mmengine.config import ConfigDict from mmdet.registry import TRANSFORMS diff --git a/mmdet/datasets/transforms/formatting.py b/mmdet/datasets/transforms/formatting.py index d256845048a..207bcd84e31 100644 --- a/mmdet/datasets/transforms/formatting.py +++ b/mmdet/datasets/transforms/formatting.py @@ -1,6 +1,5 @@ # Copyright (c) OpenMMLab. All rights reserved. import numpy as np -from mmcv.parallel import DataContainer as DC from mmcv.transforms import to_tensor from mmcv.transforms.base import BaseTransform from mmengine.data import InstanceData, PixelData @@ -224,45 +223,6 @@ def __repr__(self): f'(keys={self.keys}, order={self.order})' -@TRANSFORMS.register_module() -class ToDataContainer: - """Convert results to :obj:`mmcv.DataContainer` by given fields. - - Args: - fields (Sequence[dict]): Each field is a dict like - ``dict(key='xxx', **kwargs)``. The ``key`` in result will - be converted to :obj:`mmcv.DataContainer` with ``**kwargs``. - Default: ``(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))``. - """ - - def __init__(self, - fields=(dict(key='img', stack=True), dict(key='gt_bboxes'), - dict(key='gt_labels'))): - self.fields = fields - - def __call__(self, results): - """Call function to convert data in results to - :obj:`mmcv.DataContainer`. - - Args: - results (dict): Result dict contains the data to convert. - - Returns: - dict: The result dict contains the data converted to \ - :obj:`mmcv.DataContainer`. - """ - - for field in self.fields: - field = field.copy() - key = field.pop('key') - results[key] = DC(results[key], **field) - return results - - def __repr__(self): - return self.__class__.__name__ + f'(fields={self.fields})' - - @TRANSFORMS.register_module() class WrapFieldsToLists: """Wrap fields of the data dictionary into lists for evaluation. diff --git a/mmdet/datasets/transforms/loading.py b/mmdet/datasets/transforms/loading.py index 51c50b4b8ef..a12f5971f27 100644 --- a/mmdet/datasets/transforms/loading.py +++ b/mmdet/datasets/transforms/loading.py @@ -8,6 +8,7 @@ from mmcv.transforms import BaseTransform from mmcv.transforms import LoadAnnotations as MMCV_LoadAnnotations from mmcv.transforms import LoadImageFromFile +from mmengine.fileio import FileClient from mmdet.registry import TRANSFORMS from mmdet.structures.bbox import get_box_type @@ -87,7 +88,7 @@ class LoadMultiChannelImageFromFiles(BaseTransform): See :func:``mmcv.imfrombytes`` for details. Defaults to 'cv2'. file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. + See :class:`mmengine.fileio.FileClient` for details. Defaults to ``dict(backend='disk')``. """ @@ -102,7 +103,7 @@ def __init__( self.color_type = color_type self.imdecode_backend = imdecode_backend self.file_client_args = file_client_args.copy() - self.file_client = mmcv.FileClient(**self.file_client_args) + self.file_client = FileClient(**self.file_client_args) def transform(self, results: dict) -> dict: """Transform functions to load multiple images and get images meta @@ -235,7 +236,7 @@ class LoadAnnotations(MMCV_LoadAnnotations): See :fun:``mmcv.imfrombytes`` for details. Defaults to 'cv2'. file_client_args (dict): Arguments to instantiate a FileClient. - See :class:``mmcv.fileio.FileClient`` for details. + See :class:``mmengine.fileio.FileClient`` for details. Defaults to ``dict(backend='disk')``. """ @@ -501,7 +502,7 @@ class LoadPanopticAnnotations(LoadAnnotations): See :fun:``mmcv.imfrombytes`` for details. Defaults to 'cv2'. file_client_args (dict): Arguments to instantiate a FileClient. - See :class:``mmcv.fileio.FileClient`` for details. + See :class:``mmengine.fileio.FileClient`` for details. Defaults to ``dict(backend='disk')``. """ diff --git a/mmdet/datasets/transforms/transforms.py b/mmdet/datasets/transforms/transforms.py index 8ab951f4e58..caa03ba0797 100644 --- a/mmdet/datasets/transforms/transforms.py +++ b/mmdet/datasets/transforms/transforms.py @@ -14,6 +14,7 @@ from mmcv.transforms import Resize as MMCV_Resize from mmcv.transforms.utils import avoid_cache_randomness, cache_randomness from mmengine.dataset import BaseDataset +from mmengine.utils import is_str from numpy import random from mmdet.registry import TRANSFORMS @@ -1362,7 +1363,7 @@ def albu_builder(self, cfg: dict) -> albumentations: assert isinstance(cfg, dict) and 'type' in cfg args = cfg.copy() obj_type = args.pop('type') - if mmcv.is_str(obj_type): + if is_str(obj_type): if albumentations is None: raise RuntimeError('albumentations is not installed') obj_cls = getattr(albumentations, obj_type) diff --git a/mmdet/datasets/wider_face.py b/mmdet/datasets/wider_face.py index f3f70476bd7..432f3274cdf 100644 --- a/mmdet/datasets/wider_face.py +++ b/mmdet/datasets/wider_face.py @@ -2,7 +2,7 @@ import os.path as osp import xml.etree.ElementTree as ET -import mmcv +from mmengine.fileio import list_from_file from mmdet.registry import DATASETS from .xml_style import XMLDataset @@ -33,7 +33,7 @@ def load_annotations(self, ann_file): """ data_infos = [] - img_ids = mmcv.list_from_file(ann_file) + img_ids = list_from_file(ann_file) for img_id in img_ids: filename = f'{img_id}.jpg' xml_path = osp.join(self.img_prefix, 'Annotations', diff --git a/mmdet/datasets/xml_style.py b/mmdet/datasets/xml_style.py index 88faec823f7..8a748b0b7bc 100644 --- a/mmdet/datasets/xml_style.py +++ b/mmdet/datasets/xml_style.py @@ -5,7 +5,7 @@ import mmcv from mmengine.dataset import BaseDataset -from mmengine.fileio import FileClient +from mmengine.fileio import FileClient, list_from_file from mmdet.registry import DATASETS @@ -52,7 +52,7 @@ def load_data_list(self) -> List[dict]: } data_list = [] - img_ids = mmcv.list_from_file( + img_ids = list_from_file( self.ann_file, file_client_args=self.file_client_args) for img_id in img_ids: file_name = osp.join(self.img_subdir, f'{img_id}.jpg') diff --git a/mmdet/engine/hooks/set_epoch_info_hook.py b/mmdet/engine/hooks/set_epoch_info_hook.py index 97e25efd1dd..183f3167445 100644 --- a/mmdet/engine/hooks/set_epoch_info_hook.py +++ b/mmdet/engine/hooks/set_epoch_info_hook.py @@ -1,6 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. -from mmcv.parallel import is_module_wrapper -from mmcv.runner import Hook +from mmengine.hooks import Hook +from mmengine.model.wrappers import is_model_wrapper from mmdet.registry import HOOKS @@ -12,6 +12,6 @@ class SetEpochInfoHook(Hook): def before_train_epoch(self, runner): epoch = runner.epoch model = runner.model - if is_module_wrapper(model): + if is_model_wrapper(model): model = model.module model.set_epoch(epoch) diff --git a/mmdet/engine/hooks/visualization_hook.py b/mmdet/engine/hooks/visualization_hook.py index b3d9459e785..c1798c7bafe 100644 --- a/mmdet/engine/hooks/visualization_hook.py +++ b/mmdet/engine/hooks/visualization_hook.py @@ -4,8 +4,10 @@ from typing import Optional, Sequence import mmcv +from mmengine.fileio import FileClient from mmengine.hooks import Hook from mmengine.runner import Runner +from mmengine.utils import mkdir_or_exist from mmengine.visualization import Visualizer from mmdet.registry import HOOKS @@ -41,7 +43,7 @@ class DetVisualizationHook(Hook): test_out_dir (str, optional): directory where painted images will be saved in testing process. file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. + See :class:`mmengine.fileio.FileClient` for details. Defaults to ``dict(backend='disk')``. """ @@ -88,7 +90,7 @@ def after_val_iter(self, runner: Runner, batch_idx: int, return if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) + self.file_client = FileClient(**self.file_client_args) # There is no guarantee that the same batch of images # is visualized for each evaluation. @@ -128,10 +130,10 @@ def after_test_iter(self, runner: Runner, batch_idx: int, if self.test_out_dir is not None: self.test_out_dir = osp.join(runner.work_dir, runner.timestamp, self.test_out_dir) - mmcv.mkdir_or_exist(self.test_out_dir) + mkdir_or_exist(self.test_out_dir) if self.file_client is None: - self.file_client = mmcv.FileClient(**self.file_client_args) + self.file_client = FileClient(**self.file_client_args) for input_data, output in zip(data_batch, outputs): self._test_index += 1 diff --git a/mmdet/engine/hooks/wandblogger_hook.py b/mmdet/engine/hooks/wandblogger_hook.py deleted file mode 100644 index e988b13debb..00000000000 --- a/mmdet/engine/hooks/wandblogger_hook.py +++ /dev/null @@ -1,586 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import importlib -import os.path as osp -import sys -import warnings - -import mmcv -import numpy as np -import pycocotools.mask as mask_util -from mmcv.runner import HOOKS -from mmcv.runner.dist_utils import master_only -from mmcv.runner.hooks.checkpoint import CheckpointHook -from mmcv.runner.hooks.logger.wandb import WandbLoggerHook -from mmcv.utils import digit_version - -from mmdet.structures.mask import polygon_to_bitmap - - -# TODO: delete -@HOOKS.register_module() -class MMDetWandbHook(WandbLoggerHook): - """Enhanced Wandb logger hook for MMDetection. - - Comparing with the :cls:`mmcv.runner.WandbLoggerHook`, this hook can not - only automatically log all the metrics but also log the following extra - information - saves model checkpoints as W&B Artifact, and - logs model prediction as interactive W&B Tables. - - - Metrics: The MMDetWandbHook will automatically log training - and validation metrics along with system metrics (CPU/GPU). - - - Checkpointing: If `log_checkpoint` is True, the checkpoint saved at - every checkpoint interval will be saved as W&B Artifacts. - This depends on the : class:`mmcv.runner.CheckpointHook` whose priority - is higher than this hook. Please refer to - https://docs.wandb.ai/guides/artifacts/model-versioning - to learn more about model versioning with W&B Artifacts. - - - Checkpoint Metadata: If evaluation results are available for a given - checkpoint artifact, it will have a metadata associated with it. - The metadata contains the evaluation metrics computed on validation - data with that checkpoint along with the current epoch. It depends - on `EvalHook` whose priority is more than MMDetWandbHook. - - - Evaluation: At every evaluation interval, the `MMDetWandbHook` logs the - model prediction as interactive W&B Tables. The number of samples - logged is given by `num_eval_images`. Currently, the `MMDetWandbHook` - logs the predicted bounding boxes along with the ground truth at every - evaluation interval. This depends on the `EvalHook` whose priority is - more than `MMDetWandbHook`. Also note that the data is just logged once - and subsequent evaluation tables uses reference to the logged data - to save memory usage. Please refer to - https://docs.wandb.ai/guides/data-vis to learn more about W&B Tables. - - For more details check out W&B's MMDetection docs: - https://docs.wandb.ai/guides/integrations/mmdetection - - ``` - Example: - log_config = dict( - ... - hooks=[ - ..., - dict(type='MMDetWandbHook', - init_kwargs={ - 'entity': "YOUR_ENTITY", - 'project': "YOUR_PROJECT_NAME" - }, - interval=50, - log_checkpoint=True, - log_checkpoint_metadata=True, - num_eval_images=100, - bbox_score_thr=0.3) - ]) - ``` - - Args: - init_kwargs (dict): A dict passed to wandb.init to initialize - a W&B run. Please refer to https://docs.wandb.ai/ref/python/init - for possible key-value pairs. - interval (int): Logging interval (every k iterations). Defaults to 50. - log_checkpoint (bool): Save the checkpoint at every checkpoint interval - as W&B Artifacts. Use this for model versioning where each version - is a checkpoint. Defaults to False. - log_checkpoint_metadata (bool): Log the evaluation metrics computed - on the validation data with the checkpoint, along with current - epoch as a metadata to that checkpoint. - Defaults to True. - num_eval_images (int): The number of validation images to be logged. - If zero, the evaluation won't be logged. Defaults to 100. - bbox_score_thr (float): Threshold for bounding box scores. - Defaults to 0.3. - """ - - def __init__(self, - init_kwargs=None, - interval=50, - log_checkpoint=False, - log_checkpoint_metadata=False, - num_eval_images=100, - bbox_score_thr=0.3, - **kwargs): - super(MMDetWandbHook, self).__init__(init_kwargs, interval, **kwargs) - - self.log_checkpoint = log_checkpoint - self.log_checkpoint_metadata = ( - log_checkpoint and log_checkpoint_metadata) - self.num_eval_images = num_eval_images - self.bbox_score_thr = bbox_score_thr - self.log_evaluation = (num_eval_images > 0) - self.ckpt_hook: CheckpointHook = None - self.eval_hook = None - - def import_wandb(self): - try: - import wandb - from wandb import init # noqa - - # Fix ResourceWarning when calling wandb.log in wandb v0.12.10. - # https://github.com/wandb/client/issues/2837 - if digit_version(wandb.__version__) < digit_version('0.12.10'): - warnings.warn( - f'The current wandb {wandb.__version__} is ' - f'lower than v0.12.10 will cause ResourceWarning ' - f'when calling wandb.log, Please run ' - f'"pip install --upgrade wandb"') - - except ImportError: - raise ImportError( - 'Please run "pip install "wandb>=0.12.10"" to install wandb') - self.wandb = wandb - - @master_only - def before_run(self, runner): - super(MMDetWandbHook, self).before_run(runner) - - # Save and Log config. - if runner.meta is not None: - src_cfg_path = osp.join(runner.work_dir, - runner.meta.get('exp_name', None)) - if osp.exists(src_cfg_path): - self.wandb.save(src_cfg_path, base_path=runner.work_dir) - self._update_wandb_config(runner) - else: - runner.logger.warning('No meta information found in the runner. ') - - # Inspect CheckpointHook and EvalHook - for hook in runner.hooks: - if isinstance(hook, CheckpointHook): - self.ckpt_hook = hook - # if isinstance(hook, (EvalHook, DistEvalHook)): - # self.eval_hook = hook - - # Check conditions to log checkpoint - if self.log_checkpoint: - if self.ckpt_hook is None: - self.log_checkpoint = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log checkpoint in MMDetWandbHook, `CheckpointHook` is' - 'required, please check hooks in the runner.') - else: - self.ckpt_interval = self.ckpt_hook.interval - - # Check conditions to log evaluation - if self.log_evaluation or self.log_checkpoint_metadata: - if self.eval_hook is None: - self.log_evaluation = False - self.log_checkpoint_metadata = False - runner.logger.warning( - 'To log evaluation or checkpoint metadata in ' - 'MMDetWandbHook, `EvalHook` or `DistEvalHook` in mmdet ' - 'is required, please check whether the validation ' - 'is enabled.') - else: - self.eval_interval = self.eval_hook.interval - self.val_dataset = self.eval_hook.dataloader.dataset - # Determine the number of samples to be logged. - if self.num_eval_images > len(self.val_dataset): - self.num_eval_images = len(self.val_dataset) - runner.logger.warning( - f'The num_eval_images ({self.num_eval_images}) is ' - 'greater than the total number of validation samples ' - f'({len(self.val_dataset)}). The complete validation ' - 'dataset will be logged.') - - # Check conditions to log checkpoint metadata - if self.log_checkpoint_metadata: - assert self.ckpt_interval % self.eval_interval == 0, \ - 'To log checkpoint metadata in MMDetWandbHook, the interval ' \ - f'of checkpoint saving ({self.ckpt_interval}) should be ' \ - 'divisible by the interval of evaluation ' \ - f'({self.eval_interval}).' - - # Initialize evaluation table - if self.log_evaluation: - # Initialize data table - self._init_data_table() - # Add data to the data table - self._add_ground_truth(runner) - # Log ground truth data - self._log_data_table() - - @master_only - def after_train_epoch(self, runner): - super(MMDetWandbHook, self).after_train_epoch(runner) - - if not self.by_epoch: - return - - # Log checkpoint and metadata. - if (self.log_checkpoint - and self.every_n_epochs(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_epoch(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'epoch': runner.epoch + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'epoch_{runner.epoch + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'epoch_{runner.epoch + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.epoch + 1) - - @master_only - def after_train_iter(self, runner): - if self.get_mode(runner) == 'train': - # An ugly patch. The iter-based eval hook will call the - # `after_train_iter` method of all logger hooks before evaluation. - # Use this trick to skip that call. - # Don't call super method at first, it will clear the log_buffer - return super(MMDetWandbHook, self).after_train_iter(runner) - else: - super(MMDetWandbHook, self).after_train_iter(runner) - - if self.by_epoch: - return - - # Save checkpoint and metadata - if (self.log_checkpoint - and self.every_n_iters(runner, self.ckpt_interval) - or (self.ckpt_hook.save_last and self.is_last_iter(runner))): - if self.log_checkpoint_metadata and self.eval_hook: - metadata = { - 'iter': runner.iter + 1, - **self._get_eval_results() - } - else: - metadata = None - aliases = [f'iter_{runner.iter + 1}', 'latest'] - model_path = osp.join(self.ckpt_hook.out_dir, - f'iter_{runner.iter + 1}.pth') - self._log_ckpt_as_artifact(model_path, aliases, metadata) - - # Save prediction table - if self.log_evaluation and self.eval_hook._should_evaluate(runner): - results = self.eval_hook.latest_results - # Initialize evaluation table - self._init_pred_table() - # Log predictions - self._log_predictions(results) - # Log the table - self._log_eval_table(runner.iter + 1) - - @master_only - def after_run(self, runner): - self.wandb.finish() - - def _update_wandb_config(self, runner): - """Update wandb config.""" - # Import the config file. - sys.path.append(runner.work_dir) - config_filename = runner.meta['exp_name'][:-3] - configs = importlib.import_module(config_filename) - # Prepare a nested dict of config variables. - config_keys = [key for key in dir(configs) if not key.startswith('__')] - config_dict = {key: getattr(configs, key) for key in config_keys} - # Update the W&B config. - self.wandb.config.update(config_dict) - - def _log_ckpt_as_artifact(self, model_path, aliases, metadata=None): - """Log model checkpoint as W&B Artifact. - - Args: - model_path (str): Path of the checkpoint to log. - aliases (list): List of the aliases associated with this artifact. - metadata (dict, optional): Metadata associated with this artifact. - """ - model_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_model', type='model', metadata=metadata) - model_artifact.add_file(model_path) - self.wandb.log_artifact(model_artifact, aliases=aliases) - - def _get_eval_results(self): - """Get model evaluation results.""" - results = self.eval_hook.latest_results - eval_results = self.val_dataset.evaluate( - results, logger='silent', **self.eval_hook.eval_kwargs) - return eval_results - - def _init_data_table(self): - """Initialize the W&B Tables for validation data.""" - columns = ['image_name', 'image'] - self.data_table = self.wandb.Table(columns=columns) - - def _init_pred_table(self): - """Initialize the W&B Tables for model evaluation.""" - columns = ['image_name', 'ground_truth', 'prediction'] - self.eval_table = self.wandb.Table(columns=columns) - - def _add_ground_truth(self, runner): - # Get image loading pipeline - from mmdet.datasets.pipelines import LoadImageFromFile - img_loader = None - for t in self.val_dataset.pipeline.transforms: - if isinstance(t, LoadImageFromFile): - img_loader = t - - if img_loader is None: - self.log_evaluation = False - runner.logger.warning( - 'LoadImageFromFile is required to add images ' - 'to W&B Tables.') - return - - # Select the images to be logged. - self.eval_image_indexs = np.arange(len(self.val_dataset)) - # Set seed so that same validation set is logged each time. - np.random.seed(42) - np.random.shuffle(self.eval_image_indexs) - self.eval_image_indexs = self.eval_image_indexs[:self.num_eval_images] - - CLASSES = self.val_dataset.CLASSES - self.class_id_to_label = { - id + 1: name - for id, name in enumerate(CLASSES) - } - self.class_set = self.wandb.Classes([{ - 'id': id, - 'name': name - } for id, name in self.class_id_to_label.items()]) - - img_prefix = self.val_dataset.img_prefix - - for idx in self.eval_image_indexs: - img_info = self.val_dataset.data_infos[idx] - image_name = img_info.get('filename', f'img_{idx}') - img_height, img_width = img_info['height'], img_info['width'] - - img_meta = img_loader( - dict(img_info=img_info, img_prefix=img_prefix)) - - # Get image and convert from BGR to RGB - image = mmcv.bgr2rgb(img_meta['img']) - - data_ann = self.val_dataset.get_ann_info(idx) - bboxes = data_ann['bboxes'] - labels = data_ann['labels'] - masks = data_ann.get('masks', None) - - # Get dict of bounding boxes to be logged. - assert len(bboxes) == len(labels) - wandb_boxes = self._get_wandb_bboxes(bboxes, labels) - - # Get dict of masks to be logged. - if masks is not None: - wandb_masks = self._get_wandb_masks( - masks, - labels, - is_poly_mask=True, - height=img_height, - width=img_width) - else: - wandb_masks = None - # TODO: Panoramic segmentation visualization. - - # Log a row to the data table. - self.data_table.add_data( - image_name, - self.wandb.Image( - image, - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _log_predictions(self, results): - table_idxs = self.data_table_ref.get_index() - assert len(table_idxs) == len(self.eval_image_indexs) - - for ndx, eval_image_index in enumerate(self.eval_image_indexs): - # Get the result - result = results[eval_image_index] - if isinstance(result, tuple): - bbox_result, segm_result = result - if isinstance(segm_result, tuple): - segm_result = segm_result[0] # ms rcnn - else: - bbox_result, segm_result = result, None - assert len(bbox_result) == len(self.class_id_to_label) - - # Get labels - bboxes = np.vstack(bbox_result) - labels = [ - np.full(bbox.shape[0], i, dtype=np.int32) - for i, bbox in enumerate(bbox_result) - ] - labels = np.concatenate(labels) - - # Get segmentation mask if available. - segms = None - if segm_result is not None and len(labels) > 0: - segms = mmcv.concat_list(segm_result) - segms = mask_util.decode(segms) - segms = segms.transpose(2, 0, 1) - assert len(segms) == len(labels) - # TODO: Panoramic segmentation visualization. - - # Remove bounding boxes and masks with score lower than threshold. - if self.bbox_score_thr > 0: - assert bboxes is not None and bboxes.shape[1] == 5 - scores = bboxes[:, -1] - inds = scores > self.bbox_score_thr - bboxes = bboxes[inds, :] - labels = labels[inds] - if segms is not None: - segms = segms[inds, ...] - - # Get dict of bounding boxes to be logged. - wandb_boxes = self._get_wandb_bboxes(bboxes, labels, log_gt=False) - # Get dict of masks to be logged. - if segms is not None: - wandb_masks = self._get_wandb_masks(segms, labels) - else: - wandb_masks = None - - # Log a row to the eval table. - self.eval_table.add_data( - self.data_table_ref.data[ndx][0], - self.data_table_ref.data[ndx][1], - self.wandb.Image( - self.data_table_ref.data[ndx][1], - boxes=wandb_boxes, - masks=wandb_masks, - classes=self.class_set)) - - def _get_wandb_bboxes(self, bboxes, labels, log_gt=True): - """Get list of structured dict for logging bounding boxes to W&B. - - Args: - bboxes (list): List of bounding box coordinates in - (minX, minY, maxX, maxY) format. - labels (int): List of label ids. - log_gt (bool): Whether to log ground truth or prediction boxes. - - Returns: - Dictionary of bounding boxes to be logged. - """ - wandb_boxes = {} - - box_data = [] - for bbox, label in zip(bboxes, labels): - if not isinstance(label, int): - label = int(label) - label = label + 1 - - if len(bbox) == 5: - confidence = float(bbox[4]) - class_name = self.class_id_to_label[label] - box_caption = f'{class_name} {confidence:.2f}' - else: - box_caption = str(self.class_id_to_label[label]) - - position = dict( - minX=int(bbox[0]), - minY=int(bbox[1]), - maxX=int(bbox[2]), - maxY=int(bbox[3])) - - box_data.append({ - 'position': position, - 'class_id': label, - 'box_caption': box_caption, - 'domain': 'pixel' - }) - - wandb_bbox_dict = { - 'box_data': box_data, - 'class_labels': self.class_id_to_label - } - - if log_gt: - wandb_boxes['ground_truth'] = wandb_bbox_dict - else: - wandb_boxes['predictions'] = wandb_bbox_dict - - return wandb_boxes - - def _get_wandb_masks(self, - masks, - labels, - is_poly_mask=False, - height=None, - width=None): - """Get list of structured dict for logging masks to W&B. - - Args: - masks (list): List of masks. - labels (int): List of label ids. - is_poly_mask (bool): Whether the mask is polygonal or not. - This is true for CocoDataset. - height (int): Height of the image. - width (int): Width of the image. - - Returns: - Dictionary of masks to be logged. - """ - mask_label_dict = dict() - for mask, label in zip(masks, labels): - label = label + 1 - # Get bitmap mask from polygon. - if is_poly_mask: - if height is not None and width is not None: - mask = polygon_to_bitmap(mask, height, width) - # Create composite masks for each class. - if label not in mask_label_dict.keys(): - mask_label_dict[label] = mask - else: - mask_label_dict[label] = np.logical_or(mask_label_dict[label], - mask) - - wandb_masks = dict() - for key, value in mask_label_dict.items(): - # Create mask for that class. - value = value.astype(np.uint8) - value[value > 0] = key - - # Create dict of masks for logging. - class_name = self.class_id_to_label[key] - wandb_masks[class_name] = { - 'mask_data': value, - 'class_labels': self.class_id_to_label - } - - return wandb_masks - - def _log_data_table(self): - """Log the W&B Tables for validation data as artifact and calls - `use_artifact` on it so that the evaluation table can use the reference - of already uploaded images. - - This allows the data to be uploaded just once. - """ - data_artifact = self.wandb.Artifact('val', type='dataset') - data_artifact.add(self.data_table, 'val_data') - - self.wandb.run.use_artifact(data_artifact) - data_artifact.wait() - - self.data_table_ref = data_artifact.get('val_data') - - def _log_eval_table(self, idx): - """Log the W&B Tables for model evaluation. - - The table will be logged multiple times creating new version. Use this - to compare models at different intervals interactively. - """ - pred_artifact = self.wandb.Artifact( - f'run_{self.wandb.run.id}_pred', type='evaluation') - pred_artifact.add(self.eval_table, 'eval_data') - if self.by_epoch: - aliases = ['latest', f'epoch_{idx}'] - else: - aliases = ['latest', f'iter_{idx}'] - self.wandb.run.log_artifact(pred_artifact, aliases=aliases) diff --git a/mmdet/evaluation/functional/class_names.py b/mmdet/evaluation/functional/class_names.py index 737971182d3..ba86c5ddf44 100644 --- a/mmdet/evaluation/functional/class_names.py +++ b/mmdet/evaluation/functional/class_names.py @@ -1,5 +1,5 @@ # Copyright (c) OpenMMLab. All rights reserved. -import mmcv +from mmengine.utils import is_str def wider_face_classes(): @@ -322,7 +322,7 @@ def get_classes(dataset): for alias in aliases: alias2name[alias] = name - if mmcv.is_str(dataset): + if is_str(dataset): if dataset in alias2name: labels = eval(alias2name[dataset] + '_classes()') else: diff --git a/mmdet/evaluation/functional/mean_ap.py b/mmdet/evaluation/functional/mean_ap.py index e2ee7d4cb2b..4f049d47965 100644 --- a/mmdet/evaluation/functional/mean_ap.py +++ b/mmdet/evaluation/functional/mean_ap.py @@ -1,9 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. from multiprocessing import Pool -import mmcv import numpy as np -from mmcv.utils import print_log +from mmengine.logging import print_log +from mmengine.utils import is_str from terminaltables import AsciiTable from .bbox_overlaps import bbox_overlaps @@ -559,7 +559,7 @@ def eval_map(det_results, there are minor differences in metrics for different datasets, e.g. "voc", "imagenet_det", etc. Defaults to None. logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. + summary. See `mmengine.logging.print_log()` for details. Defaults to None. tpfp_fn (callable | None): The function used to determine true/ false positives. If None, :func:`tpfp_default` is used as default @@ -739,7 +739,7 @@ def print_map_summary(mean_ap, dataset (list[str] | str | None): Dataset name or dataset classes. scale_ranges (list[tuple] | None): Range of scales to be evaluated. logger (logging.Logger | str | None): The way to print the mAP - summary. See `mmcv.utils.print_log()` for details. + summary. See `mmengine.logging.print_log()` for details. Defaults to None. """ @@ -767,7 +767,7 @@ def print_map_summary(mean_ap, if dataset is None: label_names = [str(i) for i in range(num_classes)] - elif mmcv.is_str(dataset): + elif is_str(dataset): label_names = get_classes(dataset) else: label_names = dataset diff --git a/mmdet/evaluation/functional/panoptic_utils.py b/mmdet/evaluation/functional/panoptic_utils.py index 9f8bacf7c10..77c6cd22ec1 100644 --- a/mmdet/evaluation/functional/panoptic_utils.py +++ b/mmdet/evaluation/functional/panoptic_utils.py @@ -9,6 +9,7 @@ import mmcv import numpy as np +from mmengine.fileio import FileClient # A custom value to distinguish instance ID and category ID; need to # be greater than the number of categories. @@ -56,7 +57,7 @@ def pq_compute_single_core(proc_id, if file_client is None: file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) + file_client = FileClient(**file_client_args) pq_stat = PQStat() @@ -207,7 +208,7 @@ def pq_compute_multi_core(matched_annotations_list, if file_client is None: file_client_args = dict(backend='disk') - file_client = mmcv.FileClient(**file_client_args) + file_client = FileClient(**file_client_args) cpu_num = min(nproc, multiprocessing.cpu_count()) diff --git a/mmdet/evaluation/functional/recall.py b/mmdet/evaluation/functional/recall.py index 82b3c909b82..4bce2bf3614 100644 --- a/mmdet/evaluation/functional/recall.py +++ b/mmdet/evaluation/functional/recall.py @@ -2,7 +2,7 @@ from collections.abc import Sequence import numpy as np -from mmcv.utils import print_log +from mmengine.logging import print_log from terminaltables import AsciiTable from .bbox_overlaps import bbox_overlaps @@ -76,7 +76,8 @@ def eval_recalls(gts, proposal_nums (int | Sequence[int]): Top N proposals to be evaluated. iou_thrs (float | Sequence[float]): IoU thresholds. Default: 0.5. logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. + summary. See `mmengine.logging.print_log()` for details. + Default: None. use_legacy_coordinate (bool): Whether use coordinate system in mmdet v1.x. "1" was added to both height and width which means w, h should be @@ -129,7 +130,8 @@ def print_recall_summary(recalls, row_idxs (ndarray): which rows(proposal nums) to print col_idxs (ndarray): which cols(iou thresholds) to print logger (logging.Logger | str | None): The way to print the recall - summary. See `mmcv.utils.print_log()` for details. Default: None. + summary. See `mmengine.logging.print_log()` for details. + Default: None. """ proposal_nums = np.array(proposal_nums, dtype=np.int32) iou_thrs = np.array(iou_thrs) diff --git a/mmdet/evaluation/metrics/coco_panoptic_metric.py b/mmdet/evaluation/metrics/coco_panoptic_metric.py index 63848d29399..50a877d5290 100644 --- a/mmdet/evaluation/metrics/coco_panoptic_metric.py +++ b/mmdet/evaluation/metrics/coco_panoptic_metric.py @@ -8,7 +8,7 @@ import mmcv import numpy as np from mmengine.evaluator import BaseMetric -from mmengine.fileio import dump, load +from mmengine.fileio import FileClient, dump, load from mmengine.logging import MMLogger, print_log from terminaltables import AsciiTable @@ -57,7 +57,7 @@ class CocoPanopticMetric(BaseMetric): Defaults to 32. When ``nproc`` exceeds the number of cpu cores, the number of cpu cores is used. file_client_args (dict): Arguments to instantiate a FileClient. - See :class:`mmcv.fileio.FileClient` for details. + See :class:`mmengine.fileio.FileClient` for details. Defaults to ``dict(backend='disk')``. collect_device (str): Device name used for collecting results from different ranks during distributed training. Must be 'cpu' or @@ -114,7 +114,7 @@ def __init__(self, self._coco_api = None self.categories = None - self.file_client = mmcv.FileClient(**file_client_args) + self.file_client = FileClient(**file_client_args) def __del__(self) -> None: """Clean up.""" diff --git a/mmdet/models/backbones/detectors_resnet.py b/mmdet/models/backbones/detectors_resnet.py index 25f7dfb4365..4211435c00c 100644 --- a/mmdet/models/backbones/detectors_resnet.py +++ b/mmdet/models/backbones/detectors_resnet.py @@ -2,9 +2,9 @@ import torch.nn as nn import torch.utils.checkpoint as cp from mmcv.cnn import build_conv_layer, build_norm_layer -from mmcv.runner import load_checkpoint from mmengine.model import Sequential from mmengine.model.utils import constant_init, kaiming_init +from mmengine.runner.checkpoint import load_checkpoint from torch.nn.modules.batchnorm import _BatchNorm from mmdet.registry import MODELS diff --git a/mmdet/models/backbones/pvt.py b/mmdet/models/backbones/pvt.py index 1602462c7bd..c3133cd2140 100644 --- a/mmdet/models/backbones/pvt.py +++ b/mmdet/models/backbones/pvt.py @@ -10,10 +10,10 @@ from mmcv.cnn import Conv2d, build_activation_layer, build_norm_layer from mmcv.cnn.bricks.drop import build_dropout from mmcv.cnn.bricks.transformer import MultiheadAttention -from mmcv.runner import _load_checkpoint, load_state_dict from mmengine.model import BaseModule, ModuleList, Sequential from mmengine.model.utils import (constant_init, normal_init, trunc_normal_, trunc_normal_init) +from mmengine.runner.checkpoint import CheckpointLoader, load_state_dict from torch.nn.modules.utils import _pair as to_2tuple from mmdet.registry import MODELS @@ -41,7 +41,7 @@ class MixFFN(BaseModule): Default: None. use_conv (bool): If True, add 3x3 DWConv between two Linear layers. Defaults: False. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization. Default: None. """ @@ -123,7 +123,7 @@ class SpatialReductionAttention(MultiheadAttention): Default: dict(type='LN'). sr_ratio (int): The ratio of spatial reduction of Spatial Reduction Attention of PVT. Default: 1. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization. Default: None. """ @@ -544,7 +544,7 @@ def init_weights(self): f'specify `Pretrained` in ' \ f'`init_cfg` in ' \ f'{self.__class__.__name__} ' - checkpoint = _load_checkpoint( + checkpoint = CheckpointLoader.load_checkpoint( self.init_cfg.checkpoint, logger=logger, map_location='cpu') logger.warn(f'Load pre-trained model for ' f'{self.__class__.__name__} from original repo') diff --git a/mmdet/models/backbones/swin.py b/mmdet/models/backbones/swin.py index b3b8a039100..6e54a494787 100644 --- a/mmdet/models/backbones/swin.py +++ b/mmdet/models/backbones/swin.py @@ -9,10 +9,10 @@ import torch.utils.checkpoint as cp from mmcv.cnn import build_norm_layer from mmcv.cnn.bricks.transformer import FFN, build_dropout -from mmcv.runner import _load_checkpoint from mmengine.model import BaseModule, ModuleList from mmengine.model.utils import (constant_init, trunc_normal_, trunc_normal_init) +from mmengine.runner.checkpoint import CheckpointLoader from mmengine.utils import to_2tuple from mmdet.registry import MODELS @@ -686,7 +686,7 @@ def init_weights(self): f'specify `Pretrained` in ' \ f'`init_cfg` in ' \ f'{self.__class__.__name__} ' - ckpt = _load_checkpoint( + ckpt = CheckpointLoader.load_checkpoint( self.init_cfg.checkpoint, logger=logger, map_location='cpu') if 'state_dict' in ckpt: _state_dict = ckpt['state_dict'] diff --git a/mmdet/models/dense_heads/yolact_head.py b/mmdet/models/dense_heads/yolact_head.py index 8e34ab140b8..c9aa3e796a2 100644 --- a/mmdet/models/dense_heads/yolact_head.py +++ b/mmdet/models/dense_heads/yolact_head.py @@ -375,7 +375,7 @@ def predict_by_feat(self, coeff_preds (list[Tensor]): Mask coefficients for each scale level with shape (N, num_anchors * num_protos, H, W) batch_img_metas (list[dict]): Batch image meta info. - cfg (mmcv.Config | None): Test / postprocessing configuration, + cfg (:obj:`Config` | None): Test / postprocessing configuration, if None, test_cfg would be used rescale (bool): If True, return boxes in original image space. Defaults to True. diff --git a/mmdet/models/layers/se_layer.py b/mmdet/models/layers/se_layer.py index 49fbf87523c..89771e539f8 100644 --- a/mmdet/models/layers/se_layer.py +++ b/mmdet/models/layers/se_layer.py @@ -1,9 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. -import mmcv import torch import torch.nn as nn from mmcv.cnn import ConvModule from mmengine.model import BaseModule +from mmengine.utils import is_tuple_of class SELayer(BaseModule): @@ -35,7 +35,7 @@ def __init__(self, if isinstance(act_cfg, dict): act_cfg = (act_cfg, act_cfg) assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) + assert is_tuple_of(act_cfg, dict) self.global_avgpool = nn.AdaptiveAvgPool2d(1) self.conv1 = ConvModule( in_channels=channels, @@ -96,7 +96,7 @@ def __init__(self, if isinstance(act_cfg, dict): act_cfg = (act_cfg, act_cfg) assert len(act_cfg) == 2 - assert mmcv.is_tuple_of(act_cfg, dict) + assert is_tuple_of(act_cfg, dict) self.channels = channels self.expansion = 4 # for a1, b1, a2, b2 self.global_avgpool = nn.AdaptiveAvgPool2d(1) diff --git a/mmdet/models/layers/transformer.py b/mmdet/models/layers/transformer.py index df696724e1d..ad641b087da 100644 --- a/mmdet/models/layers/transformer.py +++ b/mmdet/models/layers/transformer.py @@ -153,8 +153,8 @@ class PatchEmbed(BaseModule): input_size (int | tuple | None): The size of input, which will be used to calculate the out size. Only work when `dynamic_size` is False. Default: None. - init_cfg (`mmcv.ConfigDict`, optional): The Config for initialization. - Default: None. + init_cfg (`mmengine.ConfigDict`, optional): The Config for + initialization. Default: None. """ def __init__( @@ -407,7 +407,7 @@ class DetrTransformerDecoderLayer(BaseTransformerLayer): """Implements decoder layer in DETR transformer. Args: - attn_cfgs (list[`mmcv.ConfigDict`] | list[dict] | dict )): + attn_cfgs (list[`mmengine.ConfigDict`] | list[dict] | dict )): Configs for self_attention or cross_attention, the order should be consistent with it in `operation_order`. If it is a dict, it would be expand to the number of attention in @@ -548,11 +548,11 @@ class Transformer(BaseModule): `_ for details. Args: - encoder (`mmcv.ConfigDict` | Dict): Config of + encoder (`mmengine.ConfigDict` | Dict): Config of TransformerEncoder. Defaults to None. - decoder ((`mmcv.ConfigDict` | Dict)): Config of + decoder ((`mmengine.ConfigDict` | Dict)): Config of TransformerDecoder. Defaults to None - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + init_cfg (obj:`mmegine.ConfigDict`): The Config for initialization. Defaults to None. """ @@ -1081,7 +1081,7 @@ class DynamicConv(BaseModule): act_cfg (dict): The activation config for DynamicConv. norm_cfg (dict): Config dict for normalization layer. Default layer normalization. - init_cfg (obj:`mmcv.ConfigDict`): The Config for initialization. + init_cfg (obj:`mmengine.ConfigDict`): The Config for initialization. Default: None. """ diff --git a/mmdet/models/necks/pafpn.py b/mmdet/models/necks/pafpn.py index 2c74ec4a590..3ed9dfd79de 100644 --- a/mmdet/models/necks/pafpn.py +++ b/mmdet/models/necks/pafpn.py @@ -2,7 +2,6 @@ import torch.nn as nn import torch.nn.functional as F from mmcv.cnn import ConvModule -from mmcv.runner import auto_fp16 from mmdet.registry import MODELS from .fpn import FPN @@ -96,7 +95,6 @@ def __init__(self, self.downsample_convs.append(d_conv) self.pafpn_convs.append(pafpn_conv) - @auto_fp16() def forward(self, inputs): """Forward function.""" assert len(inputs) == len(self.in_channels) diff --git a/mmdet/models/roi_heads/mask_heads/fcn_mask_head.py b/mmdet/models/roi_heads/mask_heads/fcn_mask_head.py index 9fc1974976b..55f238db2ea 100644 --- a/mmdet/models/roi_heads/mask_heads/fcn_mask_head.py +++ b/mmdet/models/roi_heads/mask_heads/fcn_mask_head.py @@ -303,7 +303,7 @@ def _predict_by_feat_single(self, Tensor: Encoded masks, has shape (n, img_w, img_h) Example: - >>> import mmcv + >>> from mmengine.config import Config >>> from mmdet.models.roi_heads.mask_heads.fcn_mask_head import * # NOQA >>> N = 7 # N = number of extracted ROIs >>> C, H, W = 11, 32, 32 @@ -314,7 +314,7 @@ def _predict_by_feat_single(self, >>> # Each input is associated with some bounding box >>> bboxes = torch.Tensor([[1, 1, 42, 42 ]] * N) >>> labels = torch.randint(0, C, size=(N,)) - >>> rcnn_test_cfg = mmcv.Config({'mask_thr_binary': 0, }) + >>> rcnn_test_cfg = Config({'mask_thr_binary': 0, }) >>> ori_shape = (H * 4, W * 4) >>> scale_factor = (1, 1) >>> rescale = False diff --git a/mmdet/models/task_modules/prior_generators/anchor_generator.py b/mmdet/models/task_modules/prior_generators/anchor_generator.py index ddb1683c5ff..a0b12855751 100644 --- a/mmdet/models/task_modules/prior_generators/anchor_generator.py +++ b/mmdet/models/task_modules/prior_generators/anchor_generator.py @@ -1,9 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. import warnings -import mmcv import numpy as np import torch +from mmengine.utils import is_tuple_of from torch.nn.modules.utils import _pair from mmdet.registry import TASK_UTILS @@ -507,7 +507,7 @@ def __init__(self, if min_sizes is None and max_sizes is None: # use hard code to generate SSD anchors self.input_size = input_size - assert mmcv.is_tuple_of(basesize_ratio_range, float) + assert is_tuple_of(basesize_ratio_range, float) self.basesize_ratio_range = basesize_ratio_range # calculate anchor ratios and sizes min_ratio, max_ratio = basesize_ratio_range diff --git a/mmdet/models/test_time_augs/merge_augs.py b/mmdet/models/test_time_augs/merge_augs.py index bf6e4b8965d..a2f3562ffcf 100644 --- a/mmdet/models/test_time_augs/merge_augs.py +++ b/mmdet/models/test_time_augs/merge_augs.py @@ -6,7 +6,7 @@ import numpy as np import torch from mmcv.ops import nms -from mmengine import ConfigDict +from mmengine.config import ConfigDict from torch import Tensor from mmdet.structures.bbox import bbox_mapping_back diff --git a/mmdet/structures/mask/mask_target.py b/mmdet/structures/mask/mask_target.py index aa3a9434f17..b2fc5f18783 100644 --- a/mmdet/structures/mask/mask_target.py +++ b/mmdet/structures/mask/mask_target.py @@ -21,12 +21,12 @@ def mask_target(pos_proposals_list, pos_assigned_gt_inds_list, gt_masks_list, Tensor: Mask target of each image, has shape (num_pos, w, h). Example: - >>> import mmcv + >>> from mmengine.config import Config >>> import mmdet >>> from mmdet.data_elements.mask import BitmapMasks >>> from mmdet.data_elements.mask.mask_target import * >>> H, W = 17, 18 - >>> cfg = mmcv.Config({'mask_size': (13, 14)}) + >>> cfg = Config({'mask_size': (13, 14)}) >>> rng = np.random.RandomState(0) >>> # Positive proposals (tl_x, tl_y, br_x, br_y) for each image >>> pos_proposals_list = [ @@ -78,12 +78,12 @@ def mask_target_single(pos_proposals, pos_assigned_gt_inds, gt_masks, cfg): Tensor: Mask target of each positive proposals in the image. Example: - >>> import mmcv + >>> from mmengine.config import Config >>> import mmdet >>> from mmdet.data_elements.mask import BitmapMasks >>> from mmdet.data_elements.mask.mask_target import * # NOQA >>> H, W = 32, 32 - >>> cfg = mmcv.Config({'mask_size': (7, 11)}) + >>> cfg = Config({'mask_size': (7, 11)}) >>> rng = np.random.RandomState(0) >>> # Masks for each ground truth box (relative to the image) >>> gt_masks_data = rng.rand(3, H, W) diff --git a/mmdet/structures/mask/utils.py b/mmdet/structures/mask/utils.py index c048e14d71a..6bd445e4fce 100644 --- a/mmdet/structures/mask/utils.py +++ b/mmdet/structures/mask/utils.py @@ -1,8 +1,8 @@ # Copyright (c) OpenMMLab. All rights reserved. -import mmcv import numpy as np import pycocotools.mask as mask_util import torch +from mmengine.utils import slice_list def split_combined_polys(polys, poly_lens, polys_per_mask): @@ -28,8 +28,8 @@ def split_combined_polys(polys, poly_lens, polys_per_mask): polys_lens_single = poly_lens[img_id].tolist() polys_per_mask_single = polys_per_mask[img_id].tolist() - split_polys = mmcv.slice_list(polys_single, polys_lens_single) - mask_polys = mmcv.slice_list(split_polys, polys_per_mask_single) + split_polys = slice_list(polys_single, polys_lens_single) + mask_polys = slice_list(split_polys, polys_per_mask_single) mask_polys_list.append(mask_polys) return mask_polys_list diff --git a/mmdet/testing/_utils.py b/mmdet/testing/_utils.py index 9a9ffe3c30c..43246322e63 100644 --- a/mmdet/testing/_utils.py +++ b/mmdet/testing/_utils.py @@ -4,6 +4,7 @@ import numpy as np import torch +from mmengine.config import Config from mmengine.data import InstanceData, PixelData from ..registry import TASK_UTILS @@ -28,7 +29,6 @@ def _get_config_directory(): def _get_config_module(fname): """Load a configuration as a python module.""" - from mmengine import Config config_dpath = _get_config_directory() config_fpath = join(config_dpath, fname) config_mod = Config.fromfile(config_fpath) diff --git a/mmdet/utils/__init__.py b/mmdet/utils/__init__.py index 2a65bf544e0..a41479821fd 100644 --- a/mmdet/utils/__init__.py +++ b/mmdet/utils/__init__.py @@ -1,28 +1,24 @@ # Copyright (c) OpenMMLab. All rights reserved. from .collect_env import collect_env from .compat_config import compat_cfg -from .dist_utils import (DistOptimizerHook, all_reduce_dict, allreduce_grads, - reduce_mean, sync_random_seed) +from .dist_utils import (all_reduce_dict, allreduce_grads, reduce_mean, + sync_random_seed) from .logger import get_caller_name, get_root_logger, log_img_scale from .memory import AvoidCUDAOOM, AvoidOOM from .misc import find_latest_checkpoint, update_data_root -from .parallel import MMDataParallel, MMDistributedDataParallel from .replace_cfg_vals import replace_cfg_vals from .setup_env import register_all_modules, setup_multi_processes from .split_batch import split_batch from .typing import (ConfigType, InstanceList, MultiConfig, OptConfigType, OptInstanceList, OptMultiConfig, OptPixelList, PixelList, RangeType) -from .util_distribution import build_ddp, build_dp, get_device __all__ = [ 'get_root_logger', 'collect_env', 'find_latest_checkpoint', 'update_data_root', 'setup_multi_processes', 'get_caller_name', - 'log_img_scale', 'compat_cfg', 'split_batch', 'build_ddp', 'build_dp', - 'get_device', 'MMDataParallel', 'MMDistributedDataParallel', - 'register_all_modules', 'replace_cfg_vals', 'AvoidOOM', 'AvoidCUDAOOM', - 'DistOptimizerHook', 'all_reduce_dict', 'allreduce_grads', 'reduce_mean', - 'sync_random_seed', 'ConfigType', 'InstanceList', 'MultiConfig', - 'OptConfigType', 'OptInstanceList', 'OptMultiConfig', 'OptPixelList', - 'PixelList', 'RangeType' + 'log_img_scale', 'compat_cfg', 'split_batch', 'register_all_modules', + 'replace_cfg_vals', 'AvoidOOM', 'AvoidCUDAOOM', 'all_reduce_dict', + 'allreduce_grads', 'reduce_mean', 'sync_random_seed', 'ConfigType', + 'InstanceList', 'MultiConfig', 'OptConfigType', 'OptInstanceList', + 'OptMultiConfig', 'OptPixelList', 'PixelList', 'RangeType' ] diff --git a/mmdet/utils/benchmark.py b/mmdet/utils/benchmark.py index dbaeb6f3759..85f48695692 100644 --- a/mmdet/utils/benchmark.py +++ b/mmdet/utils/benchmark.py @@ -8,7 +8,8 @@ import torch import torch.nn as nn from mmcv.cnn import fuse_conv_bn -from mmcv.runner import wrap_fp16_model +# TODO need update +# from mmcv.runner import wrap_fp16_model from mmengine import MMLogger from mmengine.config import Config from mmengine.device import get_max_cuda_memory @@ -178,9 +179,11 @@ def __init__(self, def _init_model(self, checkpoint: str, is_fuse_conv_bn: bool) -> nn.Module: """Initialize the model.""" model = build_detector(self.cfg.model) - fp16_cfg = self.cfg.get('fp16', None) - if fp16_cfg is not None: - wrap_fp16_model(model) + # TODO need update + # fp16_cfg = self.cfg.get('fp16', None) + # if fp16_cfg is not None: + # wrap_fp16_model(model) + load_checkpoint(model, checkpoint, map_location='cpu') if is_fuse_conv_bn: model = fuse_conv_bn(model) diff --git a/mmdet/utils/collect_env.py b/mmdet/utils/collect_env.py index 97e25c0e953..8b93d4e1325 100644 --- a/mmdet/utils/collect_env.py +++ b/mmdet/utils/collect_env.py @@ -1,6 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. -from mmcv.utils import collect_env as collect_base_env -from mmcv.utils import get_git_hash +from mmengine.utils import collect_env as collect_base_env +from mmengine.utils import get_git_hash import mmdet diff --git a/mmdet/utils/compat_config.py b/mmdet/utils/compat_config.py index 05aa37dcd6f..133adb65c22 100644 --- a/mmdet/utils/compat_config.py +++ b/mmdet/utils/compat_config.py @@ -2,7 +2,7 @@ import copy import warnings -from mmcv import ConfigDict +from mmengine.config import ConfigDict def compat_cfg(cfg): diff --git a/mmdet/utils/dist_utils.py b/mmdet/utils/dist_utils.py index 8760774fd90..2f2c8614a18 100644 --- a/mmdet/utils/dist_utils.py +++ b/mmdet/utils/dist_utils.py @@ -7,7 +7,7 @@ import numpy as np import torch import torch.distributed as dist -from mmcv.runner import OptimizerHook, get_dist_info +from mmengine.dist import get_dist_info from torch._utils import (_flatten_dense_tensors, _take_tensors, _unflatten_dense_tensors) @@ -56,15 +56,6 @@ def allreduce_grads(params, coalesce=True, bucket_size_mb=-1): dist.all_reduce(tensor.div_(world_size)) -class DistOptimizerHook(OptimizerHook): - """Deprecated optimizer hook for distributed training.""" - - def __init__(self, *args, **kwargs): - warnings.warn('"DistOptimizerHook" is deprecated, please switch to' - '"mmcv.runner.OptimizerHook".') - super().__init__(*args, **kwargs) - - def reduce_mean(tensor): """"Obtain the mean of tensor on different GPUs.""" if not (dist.is_available() and dist.is_initialized()): diff --git a/mmdet/utils/misc.py b/mmdet/utils/misc.py index 4113672acfb..512a4630fea 100644 --- a/mmdet/utils/misc.py +++ b/mmdet/utils/misc.py @@ -4,8 +4,8 @@ import os.path as osp import warnings -import mmcv -from mmcv.utils import print_log +from mmengine.config import Config, ConfigDict +from mmengine.logging import print_log def find_latest_checkpoint(path, suffix='pth'): @@ -49,11 +49,11 @@ def update_data_root(cfg, logger=None): MMDET_DATASETS. Otherwise, using cfg.data_root as default. Args: - cfg (mmcv.Config): The model config need to modify + cfg (:obj:`Config`): The model config need to modify logger (logging.Logger | str | None): the way to print msg """ - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' + assert isinstance(cfg, Config), \ + f'cfg got wrong type: {type(cfg)}, expected mmengine.Config' if 'MMDET_DATASETS' in os.environ: dst_root = os.environ['MMDET_DATASETS'] @@ -62,12 +62,12 @@ def update_data_root(cfg, logger=None): else: return - assert isinstance(cfg, mmcv.Config), \ - f'cfg got wrong type: {type(cfg)}, expected mmcv.Config' + assert isinstance(cfg, Config), \ + f'cfg got wrong type: {type(cfg)}, expected mmengine.Config' def update(cfg, src_str, dst_str): for k, v in cfg.items(): - if isinstance(v, mmcv.ConfigDict): + if isinstance(v, ConfigDict): update(cfg[k], src_str, dst_str) if isinstance(v, str) and src_str in v: cfg[k] = v.replace(src_str, dst_str) diff --git a/mmdet/utils/parallel.py b/mmdet/utils/parallel.py deleted file mode 100644 index a0c8c45dbcc..00000000000 --- a/mmdet/utils/parallel.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -from torch.nn.parallel import DataParallel -from torch.nn.parallel.distributed import DistributedDataParallel - - -# TODO: This is a transition plan and will be deleted in the future -class MMDistributedDataParallel(DistributedDataParallel): - - def train_step(self, *inputs, **kwargs): - return self.forward(*inputs, **kwargs) - - -class MMDataParallel(DataParallel): - - def train_step(self, *inputs, **kwargs): - return self.forward(*inputs, **kwargs) diff --git a/mmdet/utils/replace_cfg_vals.py b/mmdet/utils/replace_cfg_vals.py index 8223556e297..a3331a36ce5 100644 --- a/mmdet/utils/replace_cfg_vals.py +++ b/mmdet/utils/replace_cfg_vals.py @@ -13,11 +13,11 @@ def replace_cfg_vals(ori_cfg): < https://github.com/microsoft/SoftTeacher/blob/main/ssod/utils/vars.py>`_ # noqa: E501 Args: - ori_cfg (mmcv.utils.config.Config): + ori_cfg (mmengine.config.Config): The origin config with "${key}" generated from a file. Returns: - updated_cfg [mmcv.utils.config.Config]: + updated_cfg [mmengine.config.Config]: The config with "${key}" replaced by the corresponding value. """ @@ -60,7 +60,7 @@ def replace_value(cfg): # the pattern of string "${key}" pattern_key = re.compile(r'\$\{[a-zA-Z\d_.]*\}') - # the type of ori_cfg._cfg_dict is mmcv.utils.config.ConfigDict + # the type of ori_cfg._cfg_dict is mmengine.config.ConfigDict updated_cfg = Config( replace_value(ori_cfg._cfg_dict), filename=ori_cfg.filename) # replace the model with model_wrapper diff --git a/mmdet/utils/util_distribution.py b/mmdet/utils/util_distribution.py deleted file mode 100644 index a186bf6cb9d..00000000000 --- a/mmdet/utils/util_distribution.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel - -dp_factory = {'cuda': MMDataParallel, 'cpu': MMDataParallel} - -ddp_factory = {'cuda': MMDistributedDataParallel} - - -def build_dp(model, device='cuda', dim=0, *args, **kwargs): - """build DataParallel module by device type. - - if device is cuda, return a MMDataParallel model; if device is mlu, - return a MLUDataParallel model. - - Args: - model (:class:`nn.Module`): model to be parallelized. - device (str): device type, cuda, cpu or mlu. Defaults to cuda. - dim (int): Dimension used to scatter the data. Defaults to 0. - - Returns: - nn.Module: the model to be parallelized. - """ - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDataParallel - dp_factory['mlu'] = MLUDataParallel - model = model.mlu() - - return dp_factory[device](model, dim=dim, *args, **kwargs) - - -def build_ddp(model, device='cuda', *args, **kwargs): - """Build DistributedDataParallel module by device type. - - If device is cuda, return a MMDistributedDataParallel model; - if device is mlu, return a MLUDistributedDataParallel model. - - Args: - model (:class:`nn.Module`): module to be parallelized. - device (str): device type, mlu or cuda. - - Returns: - :class:`nn.Module`: the module to be parallelized - - References: - .. [1] https://pytorch.org/docs/stable/generated/torch.nn.parallel. - DistributedDataParallel.html - """ - assert device in ['cuda', 'mlu'], 'Only available for cuda or mlu devices.' - if device == 'cuda': - model = model.cuda() - elif device == 'mlu': - from mmcv.device.mlu import MLUDistributedDataParallel - ddp_factory['mlu'] = MLUDistributedDataParallel - model = model.mlu() - - return ddp_factory[device](model, *args, **kwargs) - - -def is_mlu_available(): - """Returns a bool indicating if MLU is currently available.""" - return hasattr(torch, 'is_mlu_available') and torch.is_mlu_available() - - -def get_device(): - """Returns an available device, cpu, cuda or mlu.""" - is_device_available = { - 'cuda': torch.cuda.is_available(), - 'mlu': is_mlu_available() - } - device_list = [k for k, v in is_device_available.items() if v] - return device_list[0] if len(device_list) == 1 else 'cpu' diff --git a/mmdet/visualization/palette.py b/mmdet/visualization/palette.py index 36c928ae9cf..bad44b354a4 100644 --- a/mmdet/visualization/palette.py +++ b/mmdet/visualization/palette.py @@ -3,6 +3,7 @@ import mmcv import numpy as np +from mmengine.utils import is_str def palette_val(palette: List[tuple]) -> List[tuple]: @@ -56,7 +57,7 @@ def get_palette(palette: Union[List[tuple], str, tuple], elif palette == 'voc': from mmdet.datasets import VOCDataset dataset_palette = VOCDataset.METAINFO['PALETTE'] - elif mmcv.is_str(palette): + elif is_str(palette): dataset_palette = [mmcv.color_val(palette)[::-1]] * num_classes else: raise TypeError(f'Invalid type for palette: {type(palette)}') diff --git a/tests/test_datasets/test_cityscapes.py b/tests/test_datasets/test_cityscapes.py index 2fff9c74746..7ea922f00fb 100644 --- a/tests/test_datasets/test_cityscapes.py +++ b/tests/test_datasets/test_cityscapes.py @@ -2,7 +2,7 @@ import os import unittest -from mmcv import dump +from mmengine.fileio import dump from mmdet.datasets import CityscapesDataset diff --git a/tests/test_datasets/test_coco_api_wrapper.py b/tests/test_datasets/test_coco_api_wrapper.py index fdb02ec474a..c3df8ac22cd 100644 --- a/tests/test_datasets/test_coco_api_wrapper.py +++ b/tests/test_datasets/test_coco_api_wrapper.py @@ -2,7 +2,7 @@ import tempfile import unittest -import mmcv +from mmengine.fileio import dump from mmdet.datasets.api_wrappers import COCOPanoptic @@ -18,7 +18,7 @@ def tearDown(self): def test_create_index(self): ann_json = {'test': ['test', 'createIndex']} annotation_file = osp.join(self.tmp_dir.name, 'createIndex.json') - mmcv.dump(ann_json, annotation_file) + dump(ann_json, annotation_file) COCOPanoptic(annotation_file) def test_load_anns(self): @@ -59,7 +59,7 @@ def test_load_anns(self): } annotation_file = osp.join(self.tmp_dir.name, 'load_anns.json') - mmcv.dump(ann_json, annotation_file) + dump(ann_json, annotation_file) api = COCOPanoptic(annotation_file) api.load_anns(1) diff --git a/tests/test_datasets/test_coco_panoptic.py b/tests/test_datasets/test_coco_panoptic.py index 7ea025128db..075fc46274f 100644 --- a/tests/test_datasets/test_coco_panoptic.py +++ b/tests/test_datasets/test_coco_panoptic.py @@ -2,7 +2,7 @@ import os import unittest -import mmcv +from mmengine.fileio import dump from mmdet.datasets import CocoPanopticDataset @@ -161,7 +161,7 @@ def setUp(self): 'categories': categories } self.json_name = 'coco_panoptic.json' - mmcv.dump(fake_json, self.json_name) + dump(fake_json, self.json_name) self.metainfo = dict( CLASSES=('person', 'car', 'wall'), diff --git a/tests/test_datasets/test_lvis.py b/tests/test_datasets/test_lvis.py index 26d3bfade53..235838eec4a 100644 --- a/tests/test_datasets/test_lvis.py +++ b/tests/test_datasets/test_lvis.py @@ -2,7 +2,7 @@ import os import unittest -from mmcv import dump +from mmengine.fileio import dump from mmdet.datasets import LVISV1Dataset, LVISV05Dataset diff --git a/tests/test_engine/test_hooks/test_mean_teacher_hook.py b/tests/test_engine/test_hooks/test_mean_teacher_hook.py index b1687afe9cb..f0f116fe30a 100644 --- a/tests/test_engine/test_hooks/test_mean_teacher_hook.py +++ b/tests/test_engine/test_hooks/test_mean_teacher_hook.py @@ -8,7 +8,7 @@ import torch.nn as nn from mmengine.model import BaseModel from mmengine.optim import OptimWrapper -from mmengine.registry import DATASETS, MODEL_WRAPPERS +from mmengine.registry import MODEL_WRAPPERS from mmengine.runner import Runner from torch.utils.data import Dataset @@ -56,7 +56,6 @@ def forward(self, *args, **kwargs): return self.student(*args, **kwargs) -@DATASETS.register_module() class DummyDataset(Dataset): METAINFO = dict() # type: ignore data = torch.randn(12, 2) diff --git a/tests/test_engine/test_runner/test_loops.py b/tests/test_engine/test_runner/test_loops.py index 80378391d65..3986a77d5fa 100644 --- a/tests/test_engine/test_runner/test_loops.py +++ b/tests/test_engine/test_runner/test_loops.py @@ -7,7 +7,6 @@ import torch.nn as nn from mmengine.model import BaseModel from mmengine.optim import OptimWrapper -from mmengine.registry import DATASETS from mmengine.runner import Runner from torch.utils.data import Dataset @@ -56,7 +55,6 @@ def forward(self, *args, **kwargs): return self.student(*args, **kwargs) -@DATASETS.register_module() class DummyDataset(Dataset): METAINFO = dict() # type: ignore data = torch.randn(12, 2) diff --git a/tests/test_evaluation/test_metrics/test_coco_panoptic_metric.py b/tests/test_evaluation/test_metrics/test_coco_panoptic_metric.py index 478a8506e24..625123d5917 100644 --- a/tests/test_evaluation/test_metrics/test_coco_panoptic_metric.py +++ b/tests/test_evaluation/test_metrics/test_coco_panoptic_metric.py @@ -7,6 +7,7 @@ import mmcv import numpy as np import torch +from mmengine.fileio import dump from mmdet.evaluation import INSTANCE_OFFSET, CocoPanopticMetric @@ -102,7 +103,7 @@ def _create_panoptic_gt_annotations(self, ann_file, seg_map_dir): img_path = osp.join(seg_map_dir, 'fake_name1.png') mmcv.imwrite(rgb_gt_seg_map[:, :, ::-1], img_path) - mmcv.dump(gt_json, ann_file) + dump(gt_json, ann_file) return gt_json diff --git a/tests/test_models/test_layers/test_transformer.py b/tests/test_models/test_layers/test_transformer.py index 4838939acfb..9151e308424 100644 --- a/tests/test_models/test_layers/test_transformer.py +++ b/tests/test_models/test_layers/test_transformer.py @@ -1,7 +1,7 @@ # Copyright (c) OpenMMLab. All rights reserved. import pytest import torch -from mmcv.utils import ConfigDict +from mmengine.config import ConfigDict from mmdet.models.layers.transformer import (AdaptivePadding, DetrTransformerDecoder, diff --git a/tests/test_models/test_losses/test_loss.py b/tests/test_models/test_losses/test_loss.py index 280f3f6ddec..39ab04d82a0 100644 --- a/tests/test_models/test_losses/test_loss.py +++ b/tests/test_models/test_losses/test_loss.py @@ -1,7 +1,7 @@ # Copyright (c) OpenMMLab. All rights reserved. import pytest import torch -from mmcv.utils import digit_version +from mmengine.utils import digit_version from mmdet.models.losses import (BalancedL1Loss, CrossEntropyLoss, DiceLoss, DistributionFocalLoss, FocalLoss, diff --git a/tests/test_utils/test_replace_cfg_vals.py b/tests/test_utils/test_replace_cfg_vals.py index 85d9d0e2fa0..de039f95eee 100644 --- a/tests/test_utils/test_replace_cfg_vals.py +++ b/tests/test_utils/test_replace_cfg_vals.py @@ -3,7 +3,7 @@ from copy import deepcopy import pytest -from mmcv.utils import Config +from mmengine.config import Config from mmdet.utils import replace_cfg_vals diff --git a/tools/analysis_tools/analyze_results.py b/tools/analysis_tools/analyze_results.py index 671f956bcb2..3b634727d47 100644 --- a/tools/analysis_tools/analyze_results.py +++ b/tools/analysis_tools/analyze_results.py @@ -3,9 +3,10 @@ import os.path as osp from multiprocessing import Pool -import mmcv import numpy as np -from mmcv import Config, DictAction +from mmengine.config import Config, DictAction +from mmengine.fileio import load +from mmengine.utils import ProgressBar, check_file_exist, mkdir_or_exist from mmdet.datasets import build_dataset, get_loading_pipeline from mmdet.evaluation import eval_map, pq_compute_single_core @@ -106,7 +107,7 @@ def _save_image_gts_results(self, out_dir (str, optional): The filename to write the image. Defaults: None. """ - mmcv.mkdir_or_exist(out_dir) + mkdir_or_exist(out_dir) for performance_info in performances: index, performance = performance_info @@ -201,7 +202,7 @@ def detection_evaluate(self, dataset, results, topk=20, eval_fn=None): else: assert callable(eval_fn) - prog_bar = mmcv.ProgressBar(len(results)) + prog_bar = ProgressBar(len(results)) _mAPs = {} for i, (result, ) in enumerate(zip(results)): # self.dataset[i] should not call directly @@ -240,12 +241,12 @@ def panoptic_evaluate(self, dataset, results, topk=20): gt_json = dataset.coco.img_ann_map result_files, tmp_dir = dataset.format_results(results) - pred_json = mmcv.load(result_files['panoptic'])['annotations'] + pred_json = load(result_files['panoptic'])['annotations'] pred_folder = osp.join(tmp_dir.name, 'panoptic') gt_folder = dataset.seg_prefix pqs = {} - prog_bar = mmcv.ProgressBar(len(results)) + prog_bar = ProgressBar(len(results)) for i in range(len(results)): data_info = dataset.prepare_train_img(i) image_id = data_info['img_info']['id'] @@ -328,7 +329,7 @@ def parse_args(): def main(): args = parse_args() - mmcv.check_file_exist(args.prediction_path) + check_file_exist(args.prediction_path) cfg = Config.fromfile(args.config) @@ -351,7 +352,7 @@ def main(): cfg.data.test.pipeline = get_loading_pipeline(cfg.data.train.pipeline) dataset = build_dataset(cfg.data.test) - outputs = mmcv.load(args.prediction_path) + outputs = load(args.prediction_path) result_visualizer = ResultVisualizer(args.show, args.wait_time, args.show_score_thr, diff --git a/tools/analysis_tools/benchmark.py b/tools/analysis_tools/benchmark.py index 745968a811d..59a58748326 100644 --- a/tools/analysis_tools/benchmark.py +++ b/tools/analysis_tools/benchmark.py @@ -2,10 +2,10 @@ import argparse import os -import mmcv -from mmcv import Config, DictAction from mmengine import MMLogger +from mmengine.config import Config, DictAction from mmengine.dist import init_dist +from mmengine.utils import mkdir_or_exist from mmdet.utils import register_all_modules from mmdet.utils.benchmark import (DataLoaderBenchmark, DatasetBenchmark, @@ -121,7 +121,7 @@ def main(): log_file = None if args.work_dir: log_file = os.path.join(args.work_dir, 'benchmark.log') - mmcv.mkdir_or_exist(args.work_dir) + mkdir_or_exist(args.work_dir) logger = MMLogger.get_instance( 'mmdet', log_file=log_file, log_level='INFO') diff --git a/tools/analysis_tools/browse_dataset.py b/tools/analysis_tools/browse_dataset.py index e6550108a98..74671ada486 100644 --- a/tools/analysis_tools/browse_dataset.py +++ b/tools/analysis_tools/browse_dataset.py @@ -2,9 +2,9 @@ import argparse import os.path as osp -import mmcv import numpy as np -from mmcv import Config, DictAction +from mmengine.config import Config, DictAction +from mmengine.utils import ProgressBar from mmdet.models.utils import mask2ndarray from mmdet.registry import DATASETS, VISUALIZERS @@ -53,7 +53,7 @@ def main(): visualizer = VISUALIZERS.build(cfg.visualizer) visualizer.dataset_meta = dataset.metainfo - progress_bar = mmcv.ProgressBar(len(dataset)) + progress_bar = ProgressBar(len(dataset)) for item in dataset: img = item['inputs'].permute(1, 2, 0).numpy() data_sample = item['data_sample'].numpy() diff --git a/tools/analysis_tools/confusion_matrix.py b/tools/analysis_tools/confusion_matrix.py index 46d95382b66..1883afc5896 100644 --- a/tools/analysis_tools/confusion_matrix.py +++ b/tools/analysis_tools/confusion_matrix.py @@ -2,12 +2,12 @@ import os import matplotlib.pyplot as plt -import mmcv -import mmengine import numpy as np from matplotlib.ticker import MultipleLocator from mmcv.ops import nms from mmengine import Config, DictAction +from mmengine.fileio import load +from mmengine.utils import ProgressBar from mmdet.evaluation import bbox_overlaps from mmdet.registry import DATASETS @@ -80,7 +80,7 @@ def calculate_confusion_matrix(dataset, num_classes = len(dataset.metainfo['CLASSES']) confusion_matrix = np.zeros(shape=[num_classes + 1, num_classes + 1]) assert len(dataset) == len(results) - prog_bar = mmcv.ProgressBar(len(results)) + prog_bar = ProgressBar(len(results)) for idx, per_img_res in enumerate(results): res_bboxes = per_img_res['pred_instances'] gts = dataset.get_data_info(idx)['instances'] @@ -249,7 +249,7 @@ def main(): if args.cfg_options is not None: cfg.merge_from_dict(args.cfg_options) - results = mmengine.fileio.load(args.prediction_path) + results = load(args.prediction_path) if not os.path.exists(args.save_dir): os.makedirs(args.save_dir) diff --git a/tools/analysis_tools/eval_metric.py b/tools/analysis_tools/eval_metric.py index 7caafe99df0..2e5f6f0f3a2 100644 --- a/tools/analysis_tools/eval_metric.py +++ b/tools/analysis_tools/eval_metric.py @@ -1,8 +1,8 @@ # Copyright (c) OpenMMLab. All rights reserved. import argparse -import mmcv -from mmcv import Config, DictAction +from mmengine.config import Config, DictAction +from mmengine.fileio import load from mmdet.datasets import build_dataset from mmdet.utils import replace_cfg_vals, update_data_root @@ -67,7 +67,7 @@ def main(): cfg.data.test.test_mode = True dataset = build_dataset(cfg.data.test) - outputs = mmcv.load(args.pkl_results) + outputs = load(args.pkl_results) kwargs = {} if args.eval_options is None else args.eval_options if args.format_only: diff --git a/tools/analysis_tools/get_flops.py b/tools/analysis_tools/get_flops.py index 4df87323b4a..ed59bcd93f7 100644 --- a/tools/analysis_tools/get_flops.py +++ b/tools/analysis_tools/get_flops.py @@ -3,7 +3,7 @@ import numpy as np import torch -from mmcv import Config, DictAction +from mmengine.config import Config, DictAction from mmdet.models import build_detector diff --git a/tools/analysis_tools/optimize_anchors.py b/tools/analysis_tools/optimize_anchors.py index 895bbd50e08..06bcce9eb27 100644 --- a/tools/analysis_tools/optimize_anchors.py +++ b/tools/analysis_tools/optimize_anchors.py @@ -21,10 +21,11 @@ import argparse import os.path as osp -import mmcv import numpy as np import torch -from mmcv import Config +from mmengine.config import Config +from mmengine.fileio import dump +from mmengine.utils import ProgressBar from scipy.optimize import differential_evolution from mmdet.datasets import build_dataset @@ -105,7 +106,7 @@ def get_whs_and_shapes(self): self.logger.info('Collecting bboxes from annotation...') bbox_whs = [] img_shapes = [] - prog_bar = mmcv.ProgressBar(len(self.dataset)) + prog_bar = ProgressBar(len(self.dataset)) for idx in range(len(self.dataset)): ann = self.dataset.get_ann_info(idx) data_info = self.dataset.data_infos[idx] @@ -145,7 +146,7 @@ def save_result(self, anchors, path=None): self.logger.info(f'Anchor optimize result:{anchor_results}') if path: json_path = osp.join(path, 'anchor_optimize_result.json') - mmcv.dump(anchor_results, json_path) + dump(anchor_results, json_path) self.logger.info(f'Result saved in {json_path}') @@ -184,7 +185,7 @@ def kmeans_anchors(self): anchors = sorted(anchors, key=lambda x: x[0] * x[1]) return anchors - prog_bar = mmcv.ProgressBar(self.iters) + prog_bar = ProgressBar(self.iters) for i in range(self.iters): converged, assignments = self.kmeans_expectation( bboxes, assignments, cluster_centers) diff --git a/tools/analysis_tools/robustness_eval.py b/tools/analysis_tools/robustness_eval.py index da5ec289243..26720fc6f27 100644 --- a/tools/analysis_tools/robustness_eval.py +++ b/tools/analysis_tools/robustness_eval.py @@ -2,8 +2,8 @@ import os.path as osp from argparse import ArgumentParser -import mmcv import numpy as np +from mmengine.fileio import load def print_coco_results(results): @@ -63,7 +63,7 @@ def get_coco_style_results(filename, 'ARs', 'ARm', 'ARl' ] - eval_output = mmcv.load(filename) + eval_output = load(filename) num_distortions = len(list(eval_output.keys())) results = np.zeros((num_distortions, 6, len(metrics)), dtype='float32') @@ -120,7 +120,7 @@ def get_voc_style_results(filename, prints='mPC', aggregate='benchmark'): for p in prints: assert p in ['P', 'mPC', 'rPC'] - eval_output = mmcv.load(filename) + eval_output = load(filename) num_distortions = len(list(eval_output.keys())) results = np.zeros((num_distortions, 6, 20), dtype='float32') @@ -183,7 +183,7 @@ def get_results(filename, def get_distortions_from_file(filename): - eval_output = mmcv.load(filename) + eval_output = load(filename) return get_distortions_from_results(eval_output) diff --git a/tools/analysis_tools/test_robustness.py b/tools/analysis_tools/test_robustness.py index a6c1d84b194..83e86f3c973 100644 --- a/tools/analysis_tools/test_robustness.py +++ b/tools/analysis_tools/test_robustness.py @@ -6,10 +6,14 @@ import mmcv import torch -from mmcv import DictAction -from mmcv.parallel import MMDataParallel, MMDistributedDataParallel -from mmcv.runner import (get_dist_info, init_dist, load_checkpoint, - wrap_fp16_model) +# TODO need refactor +from mmcv.runner import MMDataParallel, wrap_fp16_model +from mmengine.config import Config, DictAction +from mmengine.dist import get_dist_info, init_dist +from mmengine.fileio import dump, load +from mmengine.model import MMDistributedDataParallel +from mmengine.runner import load_checkpoint +from mmengine.utils import is_str from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval @@ -28,7 +32,7 @@ def coco_eval_with_return(result_files, for res_type in result_types: assert res_type in ['proposal', 'bbox', 'segm', 'keypoints'] - if mmcv.is_str(coco): + if is_str(coco): coco = COCO(coco) assert isinstance(coco, COCO) @@ -68,7 +72,7 @@ def voc_eval_with_return(result_file, iou_thr=0.5, logger='print', only_ap=True): - det_results = mmcv.load(result_file) + det_results = load(result_file) annotations = [dataset.get_ann_info(i) for i in range(len(dataset))] if hasattr(dataset, 'year') and dataset.year == 2007: dataset_name = 'voc07' @@ -188,7 +192,7 @@ def main(): if args.out is not None and not args.out.endswith(('.pkl', '.pickle')): raise ValueError('The output file must be a pkl file.') - cfg = mmcv.Config.fromfile(args.config) + cfg = Config.fromfile(args.config) if args.cfg_options is not None: cfg.merge_from_dict(args.cfg_options) # set cudnn_benchmark @@ -297,6 +301,7 @@ def main(): model.CLASSES = dataset.CLASSES if not distributed: + # TODO model = MMDataParallel(model, device_ids=[0]) show_dir = args.show_dir if show_dir is not None: @@ -317,12 +322,13 @@ def main(): eval_results_filename = ( osp.splitext(args.out)[0] + '_results' + osp.splitext(args.out)[1]) - mmcv.dump(outputs, args.out) + dump(outputs, args.out) eval_types = args.eval if cfg.dataset_type == 'VOCDataset': if eval_types: for eval_type in eval_types: if eval_type == 'bbox': + # TODO test_dataset = mmcv.runner.obj_from_dict( cfg.data.test, datasets) logger = 'print' if args.summaries else None @@ -361,7 +367,7 @@ def main(): '\nUse --eval to select a task') # save results after each evaluation - mmcv.dump(aggregated_results, eval_results_filename) + dump(aggregated_results, eval_results_filename) if rank == 0: # print final results diff --git a/tools/dataset_converters/cityscapes.py b/tools/dataset_converters/cityscapes.py index c8e44b96c4e..23ad431ce0b 100644 --- a/tools/dataset_converters/cityscapes.py +++ b/tools/dataset_converters/cityscapes.py @@ -7,6 +7,9 @@ import mmcv import numpy as np import pycocotools.mask as maskUtils +from mmengine.fileio import dump +from mmengine.utils import (Timer, mkdir_or_exist, track_parallel_progress, + track_progress) def collect_files(img_dir, gt_dir): @@ -29,10 +32,9 @@ def collect_files(img_dir, gt_dir): def collect_annotations(files, nproc=1): print('Loading annotation images') if nproc > 1: - images = mmcv.track_parallel_progress( - load_img_info, files, nproc=nproc) + images = track_parallel_progress(load_img_info, files, nproc=nproc) else: - images = mmcv.track_progress(load_img_info, files) + images = track_progress(load_img_info, files) return images @@ -107,7 +109,7 @@ def cvt_annotations(image_infos, out_json_name): if len(out_json['annotations']) == 0: out_json.pop('annotations') - mmcv.dump(out_json, out_json_name) + dump(out_json, out_json_name) return out_json @@ -128,7 +130,7 @@ def main(): args = parse_args() cityscapes_path = args.cityscapes_path out_dir = args.out_dir if args.out_dir else cityscapes_path - mmcv.mkdir_or_exist(out_dir) + mkdir_or_exist(out_dir) img_dir = osp.join(cityscapes_path, args.img_dir) gt_dir = osp.join(cityscapes_path, args.gt_dir) @@ -140,8 +142,7 @@ def main(): for split, json_name in set_name.items(): print(f'Converting {split} into {json_name}') - with mmcv.Timer( - print_tmpl='It took {}s to convert Cityscapes annotation'): + with Timer(print_tmpl='It took {}s to convert Cityscapes annotation'): files = collect_files( osp.join(img_dir, split), osp.join(gt_dir, split)) image_infos = collect_annotations(files, nproc=args.nproc) diff --git a/tools/dataset_converters/images2coco.py b/tools/dataset_converters/images2coco.py index 1c4e2f14a23..a893de8421c 100644 --- a/tools/dataset_converters/images2coco.py +++ b/tools/dataset_converters/images2coco.py @@ -2,7 +2,8 @@ import argparse import os -import mmcv +from mmengine.fileio import dump, list_from_file +from mmengine.utils import mkdir_or_exist, scandir, track_iter_progress from PIL import Image @@ -30,8 +31,8 @@ def parse_args(): def collect_image_infos(path, exclude_extensions=None): img_infos = [] - images_generator = mmcv.scandir(path, recursive=True) - for image_path in mmcv.track_iter_progress(list(images_generator)): + images_generator = scandir(path, recursive=True) + for image_path in track_iter_progress(list(images_generator)): if exclude_extensions is None or ( exclude_extensions is not None and not image_path.lower().endswith(exclude_extensions)): @@ -86,14 +87,14 @@ def main(): img_infos = collect_image_infos(args.img_path, args.exclude_extensions) # 2 convert to coco format data - classes = mmcv.list_from_file(args.classes) + classes = list_from_file(args.classes) coco_info = cvt_to_coco_json(img_infos, classes) # 3 dump save_dir = os.path.join(args.img_path, '..', 'annotations') - mmcv.mkdir_or_exist(save_dir) + mkdir_or_exist(save_dir) save_path = os.path.join(save_dir, args.out) - mmcv.dump(coco_info, save_path) + dump(coco_info, save_path) print(f'save json file: {save_path}') diff --git a/tools/dataset_converters/pascal_voc.py b/tools/dataset_converters/pascal_voc.py index 25b137c69dd..fd238bfcf28 100644 --- a/tools/dataset_converters/pascal_voc.py +++ b/tools/dataset_converters/pascal_voc.py @@ -3,8 +3,9 @@ import os.path as osp import xml.etree.ElementTree as ET -import mmcv import numpy as np +from mmengine.fileio import dump, list_from_file +from mmengine.utils import mkdir_or_exist, track_progress from mmdet.evaluation import voc_classes @@ -76,7 +77,7 @@ def cvt_annotations(devkit_path, years, split, out_file): print(f'filelist does not exist: {filelist}, ' f'skip voc{year} {split}') return - img_names = mmcv.list_from_file(filelist) + img_names = list_from_file(filelist) xml_paths = [ osp.join(devkit_path, f'VOC{year}/Annotations/{img_name}.xml') for img_name in img_names @@ -84,12 +85,12 @@ def cvt_annotations(devkit_path, years, split, out_file): img_paths = [ f'VOC{year}/JPEGImages/{img_name}.jpg' for img_name in img_names ] - part_annotations = mmcv.track_progress(parse_xml, - list(zip(xml_paths, img_paths))) + part_annotations = track_progress(parse_xml, + list(zip(xml_paths, img_paths))) annotations.extend(part_annotations) if out_file.endswith('json'): annotations = cvt_to_coco_json(annotations) - mmcv.dump(annotations, out_file) + dump(annotations, out_file) return annotations @@ -198,7 +199,7 @@ def main(): args = parse_args() devkit_path = args.devkit_path out_dir = args.out_dir if args.out_dir else devkit_path - mmcv.mkdir_or_exist(out_dir) + mkdir_or_exist(out_dir) years = [] if osp.isdir(osp.join(devkit_path, 'VOC2007')): diff --git a/tools/deployment/mmdet2torchserve.py b/tools/deployment/mmdet2torchserve.py index 70a081a2464..91d13287f22 100644 --- a/tools/deployment/mmdet2torchserve.py +++ b/tools/deployment/mmdet2torchserve.py @@ -3,7 +3,8 @@ from pathlib import Path from tempfile import TemporaryDirectory -import mmcv +from mmengine.config import Config +from mmengine.utils import mkdir_or_exist try: from model_archiver.model_packaging import package_model @@ -42,9 +43,9 @@ def mmdet2torchserve( If True, if there is an existing `{model_name}.mar` file under `output_folder` it will be overwritten. """ - mmcv.mkdir_or_exist(output_folder) + mkdir_or_exist(output_folder) - config = mmcv.Config.fromfile(config_file) + config = Config.fromfile(config_file) with TemporaryDirectory() as tmpdir: config.dump(f'{tmpdir}/config.py') diff --git a/tools/misc/gen_coco_panoptic_test_info.py b/tools/misc/gen_coco_panoptic_test_info.py index 5ad315dcbf6..dc928e66f0a 100644 --- a/tools/misc/gen_coco_panoptic_test_info.py +++ b/tools/misc/gen_coco_panoptic_test_info.py @@ -1,7 +1,7 @@ import argparse import os.path as osp -import mmcv +from mmengine.fileio import dump, load def parse_args(): @@ -17,17 +17,16 @@ def parse_args(): def main(): args = parse_args() data_root = args.data_root - val_info = mmcv.load(osp.join(data_root, 'panoptic_val2017.json')) - test_old_info = mmcv.load( - osp.join(data_root, 'image_info_test-dev2017.json')) + val_info = load(osp.join(data_root, 'panoptic_val2017.json')) + test_old_info = load(osp.join(data_root, 'image_info_test-dev2017.json')) # replace categories from image_info_test-dev2017.json # with categories from panoptic_val2017.json which # has attribute `isthing`. test_info = test_old_info test_info.update({'categories': val_info['categories']}) - mmcv.dump(test_info, - osp.join(data_root, 'panoptic_image_info_test-dev2017.json')) + dump(test_info, osp.join(data_root, + 'panoptic_image_info_test-dev2017.json')) if __name__ == '__main__': diff --git a/tools/misc/get_image_metas.py b/tools/misc/get_image_metas.py index 475d9664b1d..03b35cb2c0d 100644 --- a/tools/misc/get_image_metas.py +++ b/tools/misc/get_image_metas.py @@ -13,7 +13,8 @@ from multiprocessing import Pool import mmcv -from mmcv import Config +from mmengine.config import Config +from mmengine.fileio import FileClient, dump def parse_args(): @@ -68,7 +69,7 @@ def get_metas_from_txt_style_ann_file(ann_file): def get_image_metas(data_info, img_prefix): - file_client = mmcv.FileClient(backend='disk') + file_client = FileClient(backend='disk') filename = data_info.get('filename', None) if filename is not None: if img_prefix is not None: @@ -117,7 +118,7 @@ def main(): # save image metas root_path = dataloader_cfg.dataset.ann_file.rsplit('/', 1)[0] save_path = osp.join(root_path, args.out) - mmcv.dump(image_metas, save_path, protocol=4) + dump(image_metas, save_path, protocol=4) print(f'Image meta file save to: {save_path}') diff --git a/tools/misc/split_coco.py b/tools/misc/split_coco.py index 78cc655034d..e4c991ef3f4 100644 --- a/tools/misc/split_coco.py +++ b/tools/misc/split_coco.py @@ -2,8 +2,9 @@ import argparse import os.path as osp -import mmcv import numpy as np +from mmengine.fileio import dump, load +from mmengine.utils import mkdir_or_exist, track_parallel_progress prog_description = '''K-Fold coco split. @@ -58,13 +59,13 @@ def save_anns(name, images, annotations): sub_anns['categories'] = anns['categories'] sub_anns['info'] = anns['info'] - mmcv.mkdir_or_exist(out_dir) - mmcv.dump(sub_anns, f'{out_dir}/{name}.json') + mkdir_or_exist(out_dir) + dump(sub_anns, f'{out_dir}/{name}.json') # set random seed with the fold np.random.seed(fold) ann_file = osp.join(data_root, 'annotations/instances_train2017.json') - anns = mmcv.load(ann_file) + anns = load(ann_file) image_list = anns['images'] labeled_total = int(percent / 100. * len(image_list)) @@ -106,4 +107,4 @@ def multi_wrapper(args): arguments_list = [(args.data_root, args.out_dir, p, f) for f in range(1, args.fold + 1) for p in args.labeled_percent] - mmcv.track_parallel_progress(multi_wrapper, arguments_list, args.fold) + track_parallel_progress(multi_wrapper, arguments_list, args.fold) diff --git a/tools/model_converters/detectron2pytorch.py b/tools/model_converters/detectron2pytorch.py index b7264d53d24..fe0920ada19 100644 --- a/tools/model_converters/detectron2pytorch.py +++ b/tools/model_converters/detectron2pytorch.py @@ -2,8 +2,8 @@ import argparse from collections import OrderedDict -import mmcv import torch +from mmengine.fileio import load arch_settings = {50: (3, 4, 6, 3), 101: (3, 4, 23, 3)} @@ -39,7 +39,7 @@ def convert(src, dst, depth): raise ValueError('Only support ResNet-50 and ResNet-101 currently') block_nums = arch_settings[depth] # load caffe model - caffe_model = mmcv.load(src, encoding='latin1') + caffe_model = load(src, encoding='latin1') blobs = caffe_model['blobs'] if 'blobs' in caffe_model else caffe_model # convert to pytorch style state_dict = OrderedDict()