import mmcv #从文件中加载数据 data = mmcv.load('test.json') data = mmcv.load('test.yaml') data = mmcv.load('test.pkl') #从类文件对象加载数据 with open('test.json', 'r') as f: data = mmcv.load(f) #复制数据到字符串文件 json_str = mmcv.dump(data, file_formate='json') mmcv.dump(data, 'out.pkl') #使用类文件对象将数据转储到文件中 with open('test.yaml', 'w') as f: data = mmcv.dump(data, f, file_format='yaml')扩展api以支持更多的文件格式也非常方便。您所需要做的就是编写一个继承自BaseFileHandler的文件处理程序,并用一种或几种文件格式注册它。 您需要实现至少3个方法。
import mmcv # To register multiple file formats, a list can be used as the argument. # @mmcv.register_handler(['txt', 'log']) @mmcv.register_handler('txt') class TxtHandler1(mmcv.BaseFileHandler): def load_from_fileobj(self, file): return file.read() def dump_to_fileobj(self, obj, file): file.write(str(obj)) def dump_to_str(self, obj, **kwargs): return str(obj)下面是PickleHandler的一个示例。
import pickle class PickleHandler(mmcv.BaseFileHandler): def load_from_fileobj(self, file, **kwargs): return pickle.load(file, **kwargs) def load_from_path(self, filepath, **kwargs): return super(PickleHandler, self).load_from_path( filepath, mode='rb', **kwargs) def dump_to_str(self, obj, **kwargs): kwargs.setdefault('protocol', 2) return pickle.dumps(obj, **kwargs) def dump_to_fileobj(self, obj, file, **kwargs): kwargs.setdefault('protocol', 2) pickle.dump(obj, file, **kwargs) def dump_to_path(self, obj, filepath, **kwargs): super(PickleHandler, self).dump_to_path( obj, filepath, mode='wb', **kwargs)(2)以列表或字典的形式加载文本文件 例如,a.txt是一个5行文本文件。 a b c d e 然后使用list_from_file从a.txt加载列表。
>>> mmcv.list_from_file('a.txt') ['a', 'b', 'c', 'd', 'e'] >>> mmcv.list_from_file('a.txt', offset=2) ['c', 'd', 'e'] >>> mmcv.list_from_file('a.txt', max_num=2) ['a', 'b'] >>> mmcv.list_from_file('a.txt', prefix='/mnt/') ['/mnt/a', '/mnt/b', '/mnt/c', '/mnt/d', '/mnt/e']例如,b.txt是一个有3行文本文件。 1 cat 2 dog cow 3 panda 然后使用dict_from_file从b.txt加载列表。
>>> mmcv.dict_from_file('b.txt') {'1': 'cat', '2': ['dog', 'cow'], '3': 'panda'} >>> mmcv.dict_from_file('b.txt', key_type=int) {1: 'cat', 2: ['dog', 'cow'], 3: 'panda'}二、Imgae 这个模块提供了一些图像处理方法,需要安装opencv。 (1)读/写/显示图像 要读取或写入图像文件,请使用imread或imwrite。
import mmcv img = mmcv.imread('test.jpg') img = mmcv.imread('test.jpg', flag='grayscale') img_ = mmcv.imread(img) # nothing will happen, img_ = img mmcv.imwrite(img, 'out.jpg')从字节中读取图像
with open('test.jpg', 'rb') as f: data = f.read() img = mmcv.imfrombytes(data)显示一个图像文件或加载的图像
mmcv.imshow('tests/data/color.jpg') # this is equivalent to for i in range(10): img = np.random.randint(256, size=(100, 100, 3), dtype=np.uint8) mmcv.imshow(img, win_name='test image', wait_time=200)(2)颜色空间变换 提供下列的方法
bgr2gray gray2bgr bgr2rgb rgb2bgr bgr2hsv hsv2bgr
img = mmcv.imread('tests/data/color.jpg') img1 = mmcv.bgr2rgb(img) img2 = mmcv.rgb2gray(img1) img3 = mmcv.bgr2hsv(img)(3)缩放 有三个调整大小的方法。所有的imresize_*方法都有一个参数return_scale,如果这个参数是假的,那么返回值仅仅是调整大小的图像,否则是一个元组(resized_img, scale)。
# resize to a given size mmcv.imresize(img, (1000, 600), return_scale=True) # resize to the same size of another image mmcv.imresize_like(img, dst_img, return_scale=False) # resize by a ratio mmcv.imrescale(img, 0.5) # resize so that the max edge no longer than 1000, short edge no longer than 800 # without changing the aspect ratio mmcv.imrescale(img, (1000, 800))(4)旋转 使用imrotate旋转图像以一定角度旋转。可以指定中心,默认为原始图像的中心。旋转有两种方式,一种是保持图像大小不变,这样旋转后图像的某些部分会被裁剪,另一种是扩展图像大小以适应旋转后的图像。
img = mmcv.imread('tests/data/color.jpg') # rotate the image clockwise by 30 degrees. img_ = mmcv.imrotate(img, 30) # rotate the image counterclockwise by 90 degrees. img_ = mmcv.imrotate(img, -90) # rotate the image clockwise by 30 degrees, and rescale it by 1.5x at the same time. img_ = mmcv.imrotate(img, 30, scale=1.5) # rotate the image clockwise by 30 degrees, with (100, 100) as the center. img_ = mmcv.imrotate(img, 30, center=(100, 100)) # rotate the image clockwise by 30 degrees, and extend the image size. img_ = mmcv.imrotate(img, 30, auto_bound=True)(5)翻转 要翻转图像,请使用imflip。
img = mmcv.imread('tests/data/color.jpg') # flip the image horizontally mmcv.imflip(img) # flip the image vertically mmcv.imflip(img, direction='vertical')(6)剪裁 imcrop可以用一个或一些区域来裁剪图像,表示为(x1, y1, x2, y2)。
import mmcv import numpy as np img = mmcv.imread('tests/data/color.jpg') # crop the region (10, 10, 100, 120) bboxes = np.array([10, 10, 100, 120]) patch = mmcv.imcrop(img, bboxes) # crop two regions (10, 10, 100, 120) and (0, 0, 50, 50) bboxes = np.array([[10, 10, 100, 120], [0, 0, 50, 50]]) patches = mmcv.imcrop(img, bboxes) # crop two regions, and rescale the patches by 1.2x patches = mmcv.imcrop(img, bboxes, scale_ratio=1.2)(7)填充 有两个方法impad和impad_to_multiple可以用给定的值将图像填充到特定大小。
img = mmcv.imread('tests/data/color.jpg') # pad the image to (1000, 1200) with all zeros img_ = mmcv.impad(img, shape=(1000, 1200), pad_val=0) # pad the image to (1000, 1200) with different values for three channels. img_ = mmcv.impad(img, shape=(1000, 1200), pad_val=[100, 50, 200]) # pad the image on left, right, top, bottom borders with all zeros img_ = mmcv.impad(img, padding=(10, 20, 30, 40), pad_val=0) # pad the image on left, right, top, bottom borders with different values # for three channels. img_ = mmcv.impad(img, padding=(10, 20, 30, 40), pad_val=[100, 50, 200]) # pad an image so that each edge is a multiple of some value. img_ = mmcv.impad_to_multiple(img, 32)三、Video 此模块提供以下功能。 1、一个VideoReader类,具有友好的api来读取和转换视频。 2、剪辑(剪切,concat,调整大小)视频的一些方法。 3、光流读/写/变形。 (1)视频读取 VideoReader类提供了类似api的序列来访问视频帧。它将在内部缓存已访问的帧
video = mmcv.VideoReader('test.mp4') # obtain basic information print(len(video)) print(video.width, video.height, video.resolution, video.fps) # iterate over all frames for frame in video: print(frame.shape) # read the next frame img = video.read() # read a frame by index img = video[100] # read some frames img = video[5:10]将视频转换为图像或从图像目录生成视频。
# split a video into frames and save to a folder video = mmcv.VideoReader('test.mp4') video.cvt2frames('out_dir') # generate video from frames mmcv.frames2video('out_dir', 'test.avi')(2)操作视频 还有一些用于编辑视频的方法,它们包装了ffmpeg的命令。
# cut a video clip mmcv.cut_video('test.mp4', 'clip1.mp4', start=3, end=10, vcodec='h264') # join a list of video clips mmcv.concat_video(['clip1.mp4', 'clip2.mp4'], 'joined.mp4', log_level='quiet') # resize a video with the specified size mmcv.resize_video('test.mp4', 'resized1.mp4', (360, 240)) # resize a video with a scaling ratio of 2 mmcv.resize_video('test.mp4', 'resized2.mp4', ratio=2)
(3)光流操作 我们提供了两个选项来转储光流文件:未压缩和压缩。未压缩的方法只是将浮点数转储到二进制文件中。它是无损的,但转储文件有一个更大的大小。这种压缩方法将光流量化到0-255,并将其转储为jpeg图像。x-dim和y-dim的流程将被连接成一个单独的图像。
flow = np.random.rand(800, 600, 2).astype(np.float32) # dump the flow to a flo file (~3.7M) mmcv.flowwrite(flow, 'uncompressed.flo') # dump the flow to a jpeg file (~230K) # the shape of the dumped image is (800, 1200) mmcv.flowwrite(flow, 'compressed.jpg', quantize=True, concat_axis=1) # read the flow file, the shape of loaded flow is (800, 600, 2) for both ways flow = mmcv.flowread('uncompressed.flo') flow = mmcv.flowread('compressed.jpg', quantize=True, concat_axis=1)使用mmcv.flowshow()可以可视化光流。
mmcv.flowshow(flow)四、可视化 mmcv可以显示图像和注释(当前支持的类型包括边框)。
# show an image file mmcv.imshow('a.jpg') # show a loaded image img = np.random.rand(100, 100, 3) mmcv.imshow(img) # show image with bounding boxes img = np.random.rand(100, 100, 3) bboxes = np.array([[0, 0, 50, 50], [20, 20, 60, 60]]) mmcv.imshow_bboxes(img, bboxes)mmcv还可以可视化特殊的图像,如光流。
flow = mmcv.flowread('test.flo') mmcv.flowshow(flow)
五、Utils (1)Config配置这个类很经常拿来使用配置网络 Config类用于操作配置和配置文件。它支持从多种文件格式加载config,包括python、json和yaml。它提供了类似dict的api来获取和设置值。 下面是配置文件test.py的示例。
a = 1 b = dict(b1=[0, 1, 2], b2=None) c = (1, 2) d = 'string'
>>> cfg = Config.fromfile('test.py') >>> print(cfg) >>> dict(a=1, ... b=dict(b1=[0, 1, 2], b2=None), ... c=(1, 2), ... d='string')对于所有的配置格式,都支持一些预定义的变量。它将{{var}}中的变量与它的实际值进行转换。 目前,它支持四个预定义变量: {{fileDirname}} -当前打开文件的dirname,例如/home/your-username/your-project/文件夹 {{fileBasename}} -当前打开文件的basename,例如file.ext {{fileBasenameNoExtension}} -当前打开文件的basename,不带文件扩展名,例如file {{fileExtname}} -当前打开文件的扩展名,例如.ext 这些变量名是从VS代码中引用的。 下面是一个带有预定义变量的配置示例。
config_a.py a = 1 b = './work_dir/{{ fileBasenameNoExtension }}' c = '{{ fileExtname }}'
>>> cfg = Config.fromfile('./config_a.py') >>> print(cfg) >>> dict(a=1, ... b='./work_dir/config_a', ... c='.py')对于所有的配置格式,都支持继承。要在其他配置文件中重用字段,请指定_base_='./config_a。或一个configs _base_=['./config_a的列表。py”、“。/ config_b.py ']。下面是4个配置继承的例子。 第一类:从基本配置继承,没有重叠的键
config_a.py a = 1 b = dict(b1=[0, 1, 2], b2=None) config_b.py _base_ = './config_a.py' c = (1, 2) d = 'string'
>>> cfg = Config.fromfile('./config_b.py') >>> print(cfg) >>> dict(a=1, ... b=dict(b1=[0, 1, 2], b2=None), ... c=(1, 2), ... d='string')config_b.py中的新字段与config_a.py中的旧字段结合在一起 第二类:从基础配置继承重叠的键
config_c.py _base_ = './config_a.py' b = dict(b2=1) c = (1, 2)
>>> cfg = Config.fromfile('./config_c.py') >>> print(cfg) >>> dict(a=1, ... b=dict(b1=[0, 1, 2], b2=1), ... c=(1, 2))config_c.py中的b.b b2=1替换config_a中的b.b b2=1。 第三类:从忽略字段的基本配置继承
config_d.py _base_ = './config_a.py' b = dict(_delete_=True, b2=None, b3=0.1) c = (1, 2)
>>> cfg = Config.fromfile('./config_d.py') >>> print(cfg) >>> dict(a=1, ... b=dict(b2=None, b3=0.1), ... c=(1, 2))还可以设置_delete_=True来忽略基配置中的一些字段。b中的所有旧键b1, b2, b3都被替换为新键b2, b3。 第四类:继承多个基配置(基配置不应该包含相同的键)
config_e.py c = (1, 2) d = 'string' config_f.py _base_ = ['./config_a.py', './config_e.py']
>>> cfg = Config.fromfile('./config_f.py') >>> print(cfg) >>> dict(a=1, ... b=dict(b1=[0, 1, 2], b2=None), ... c=(1, 2), ... d='string')(2)进度条 如果您想对项目列表应用一个方法并跟踪进度,track_progress是一个不错的选择。它将显示一个进度条来告知进度和ETA。
import mmcv def func(item): # do something pass tasks = [item_1, item_2, ..., item_n] mmcv.track_progress(func, tasks)还有另一个方法track_parallel_progress,它包装了多处理和进程可视化。
mmcv.track_parallel_progress(func, tasks, 8) # 8 workers如果您想迭代或枚举一列项目并跟踪进度,track_iter_progress是一个不错的选择。它将显示一个进度条来告知进度和ETA。
import mmcv tasks = [item_1, item_2, ..., item_n] for task in mmcv.track_iter_progress(tasks): # do something like print print(task) for i, task in enumerate(mmcv.track_iter_progress(tasks)): # do something like print print(i) print(task)(3)计时器 用计时器计算代码块的运行时间是方便的。
import time with mmcv.Timer(): # simulate some code block time.sleep(1)或者尝试使用since_start()和since_last_check()。前者可以返回自计时器启动以来的运行时,后者将返回自上次检查以来的时间。
timer = mmcv.Timer() # code block 1 here print(timer.since_start()) # code block 2 here print(timer.since_last_check()) print(timer.since_start())
六、Runner runner模块旨在帮助用户用更少的代码开始训练,同时保持灵活性和可配置性。 文档和示例仍在更新中。 七、Register MMCV实现了registry来管理在检测器*享类似功能的不同模块,如backbone、head和neck。OpenMMLab中的大多数项目都使用注册表来管理数据集和模型模块,如MMDetection、MMDetection3D、MMClassification、MMEditing等。 什么是register? 在MMCV中,registry可以看作是类到字符串的映射。单个注册表包含的这些类通常具有类似的api,但实现不同的算法或支持不同的数据集。使用注册表,用户可以通过相应的字符串查找和实例化类,并根据需要使用实例化的模块。一个典型的例子是大多数OpenMMLab项目中的配置系统,它们使用注册表通过配置创建钩子(hook)、运行器(runner)、模型(model)和数据集(datasets)。 要通过注册表管理代码基中的模块,有如下三个步骤。 (1)创建一个注册表 (2)创建一个构建方法 (3)使用这个注册表来管理模块 一个简单的例子:这里我们展示了一个使用registry管理包中的模块的简单示例。您可以在OpenMMLab项目中找到更多实际的示例。 假设我们希望实现一系列数据集转换器,用于将不同格式的数据转换为预期的数据格式。我们将目录创建为一个名为converters的包。在包中,我们首先创建一个文件来实现构建器,名为converters/builder.py。如下所示。
from mmcv.utils import Registry # create a registry for converters CONVERTERS = Registry('converter') # create a build function def build_converter(cfg, *args, **kwargs): cfg_ = cfg.copy() converter_type = cfg_.pop('type') if converter_type not in CONVERTERS: raise KeyError(f'Unrecognized task type {converter_type}') else: converter_cls = CONVERTERS.get(converter_type) converter = converter_cls(*args, **kwargs, **cfg_) return converter然后我们可以在包中实现不同的转换器。例如,在converters/ Converter1 .py中实现Converter1
from .builder import CONVERTERS # use the registry to namge the module @CONVERTERS.register_module() class Converter1(object): def __init__(self, a, b): self.a = a self.b = b使用registry管理模块的关键步骤是在创建模块时通过@CONVERTERS.register_module()将实现的模块注册到注册表转换器中。如果模块注册成功,您可以通过configs as使用这个转换器
converter_cfg = dict(type='Converter1', a=a_value, b=b_value) converter = build_converter(converter_cfg)八、CNN 我们提供了一些CNNs的构建块,包括层构建、模块捆绑和权重初始化。 (1)构建层 在运行实验时,我们可能需要尝试相同类型的不同层,但不希望不时地修改代码。这里我们提供了一些从dict构建层的方法,dict可以用configs编写,也可以通过命令行参数指定
cfg = dict(type='Conv3d') layer = build_norm_layer(cfg, in_channels=3, out_channels=8, kernel_size=3)
build_conv_layer: Supported types are Conv1d, Conv2d, Conv3d, Conv (alias for Conv2d). build_norm_layer: Supported types are BN1d, BN2d, BN3d, BN (alias for BN2d), SyncBN, GN, LN, IN1d, IN2d, IN3d, IN (alias for IN2d). build_activation_layer: Supported types are ReLU, LeakyReLU, PReLU, RReLU, ReLU6, ELU, Sigmoid, Tanh. build_upsample_layer: Supported types are nearest, bilinear, deconv, pixel_shuffle. build_padding_layer: Supported types are zero, reflect, replicate.我们还允许使用定制的层和操作符扩展构建方法。
编写和注册您自己的模块。 from mmcv.cnn import UPSAMPLE_LAYERS @UPSAMPLE_LAYERS.register_module() class MyUpsample: def __init__(self, scale_factor): pass def forward(self, x): pass
将MyUpsample导入到某个地方(例如,在__init__.py中),然后使用它。 cfg = dict(type='MyUpsample', scale_factor=2) layer = build_upsample_layer(cfg)(2)模块捆绑 我们还提供了通用模块包,方便网络建设。ConvModule是卷积层、归一化层和激活层的捆绑,详细请参考api。
# conv + bn + relu conv = ConvModule(3, 8, 2, norm_cfg=dict(type='BN')) # conv + gn + relu conv = ConvModule(3, 8, 2, norm_cfg=dict(type='GN', num_groups=2)) # conv + relu conv = ConvModule(3, 8, 2) # conv conv = ConvModule(3, 8, 2, act_cfg=None) # conv + leaky relu conv = ConvModule(3, 8, 3, padding=1, act_cfg=dict(type='LeakyReLU')) # bn + conv + relu conv = ConvModule( 3, 8, 2, norm_cfg=dict(type='BN'), order=('norm', 'conv', 'act'))(3)权重初始化
constant_init xavier_init normal_init uniform_init kaiming_init caffe2_xavier_init bias_init_with_prob conv1 = nn.Conv2d(3, 3, 1) normal_init(conv1, std=0.01, bias=0) xavier_init(conv1, distribution='uniform')(4)除了torchvision的预训练模型,我们还提供以下CNN的预训练模型:
VGG Caffe ResNet Caffe ResNeXt ResNet with Group Normalization ResNet with Group Normalization and Weight Standardization HRNetV2 Res2Net RegNetMMCV中的模型zoo链接由JSON文件管理。json文件由模型名称及其url或路径的键-值对组成。一个json文件:
{ "model_a": "https://example.com/models/model_a_9e5bac.pth", "model_b": "pretrain/model_b_ab3ef2c.pth" }OpenMMLab AWS上托管的预训练模型的默认链接可以在这里找到。 你可以通过打开mmlab来覆盖默认链接。json MMCV_HOME之下。如果在环境中找不到MMCV_HOME,则为~/。默认使用cache/mmcv。您可以导出MMCV_HOME=/your/path来使用自己的路径。外部json文件将合并到默认文件中。如果在外部json和默认json中都有相同的键,那么将使用外部键。 (5)加载权重 mmcv.load_checkpoint()的文件名参数支持以下类型。 (1)文件路径:checkpoint的文件路径。 (2)http://xxx和https://xxx:下载checkpoint的链接。SHA256后缀应该包含在文件名中。 (3)torchvision//xxx:模型链接在torchvision.models中。详情请参阅torchvision。 (4)open-mmlab://xxx:默认和其他json文件中提供的模型链接或文件路径。 九、我们实现了常用CUDA ops在检测、分割等方面的应用。
BBoxOverlaps CARAFE CrissCrossAttention ContextBlock CornerPool Deformable Convolution v1/v2 Deformable RoIPool GeneralizedAttention MaskedConv NMS PSAMask RoIPool RoIAlign SimpleRoIAlign SigmoidFocalLoss SoftmaxFocalLoss SoftNMS Synchronized BatchNorm Weight standardization