文章目录
1. 简介
是一个用于计算机视觉任务的基础 P y t h o n {\rm Python} Python库。它支持许多开源的深度学习库,如MMDetection、MMDetection3D、MMSegmentation、MMEditing、MMPose、MMAction2和MMClassification。本文主要介绍 m m c v {\rm mmcv} mmcv在目标检测中的应用,即 M M D e t e c t i o n {\rm MMDetection} MMDetection。它主要提供了通用输入输出接口、图像/视频预处理、图像及其标注的可视化、实用工具(如进度条、计时器等)、基于PyTorch的运行器、各种CNN架构和相关CUDA操作算子。关于 m m c v {\rm mmcv} mmcv的安装,我们在系列文章的第一篇已经详细说明。
2. 文件操作
2.1 加载和解析文件
前面提到, M M D e t e c t i o n {\rm MMDetection} MMDetection的核心部分由配置文件组成,而我们也会经常使用相关格式的配置文件来保存参数。在 m m c v {\rm mmcv} mmcv中提供了相关的操作用于快速、便捷地解析各种格式的文件。使用以下语句就可以完成对各种格式文件的解析:
import mmcv
# 解析json文件,返回结果是可以直接调用的字典格式
data = mmcv.load('test.json')
# 解析yaml文件,返回是可以直接调用的字典格式
data = mmcv.load('test.yaml')
# pkl是python中用于保存数据的一种格式,返回结果是字典格式
data = mmcv.load('test.pkl')
# 或者采用以下方式打开并解析文件
with open('test.json', 'r') as f:
data = mmcv.load(f)
# 将解析结果转化成字符串格式
json_str = mmcv.dump(data, file_format='json')
# 或者采用以下形式
with opn('test.yaml', 'w') as f:
data = mmcv.dump(data, f, file_format='yaml')
我们也可以在
m
m
c
v
{\rm mmcv}
mmcv中自定义处理的文件格式,自定义类并继承自BaseFileHandler
并至少实现三个关键的方法(具体的实现方法可参考
m
m
c
v
{\rm mmcv}
mmcv中的实现方式):
import mmcv
@mmcv.register_handler('txt')
class TxtHandler(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)
2.2 加载文本文件并将结果转化成列表或字典
如测试文件test.txt
的内容如下:
a
b
c
d
e
import mmcv
# 加载文本文件并将结果转化成列表
data = mmcv.list_from_file('test.txt')
# ['a', 'b', 'c', 'd', 'e']
# 设置'offset=value'使读取位置向后偏移value个位置
data = mmcv.list_from_file('test.txt', offset=2)
# ['c', 'd', 'e']
# 设置'max_num=value'使最大量为value
data = mmcv.list_from_file('test.txt', max_num=2)
# ['a', 'b']
# 设置'prefix=value'将结果转化成列表并给每个结果元素加上前缀value
data = mmcv.list_from_file('test.txt', prefix='/mnt/')
# ['/mnt/a', '/mnt/b', '/mnt/c', '/mnt/d', '/mnt/e']
如测试文件test.txt
的内容如下:
1 a
2 b
3 c
4 d
5 e
import mmcv
# 加载文本文件并将结果转化成字典
data = mmcv.dict_from_file('test.txt')
# ['1': 'a', '2': 'b', '3': 'c', '4': 'd', '5': 'e']
# 设置'key_type=int'将键的类型转化成整型
data = mmcv.dict_from_file('test.txt', key_type=int)
# [1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e']
其中list_from_file
函数和dict_from_file
函数位于/root_path/mmcv/mmcv/parse.py
。
3. 图像操作
3.1 图像的读/写/显示
import mmcv
# 读取图像,返回OpenCV对象
img = mmcv.imread('test.jpg')
# 以灰度模式读取图像
img = mmcv.imread('test.jpg', flag='grayscale')
# 写入图像,保存在'out.jpg'
mmcv.imwrite(img, 'out.jpg')
# 基于字节流读取图像
with open('test.jpg', 'rb') as f:
data = f.read()
img = mmcv.imfrombytes(data)
# 显示图像
mmcv.imshow('test.jpg')
3.2 颜色空间的转换
支持的颜色空间转化操作有bgr2gray
,gray2bgr
,bgr2rgb
,rgb2bgr
,bgr2hsv
,hsv2bgr
。
import mmcv
# 读取图像
img = mmcv.imread('test.jpg')
# 转换颜色空间
img_ = mmcv.bgr2rgb(img)
3.3 图像其他操作
调整尺寸 在
m
m
c
v
{\rm mmcv}
mmcv中有关调整尺寸操作的函数均包含参数return_scale
,如果指定该参数的值是False
,则仅返回调整后的图像;否则返回包含调整后的图像以及尺寸的元组。
# 将图像尺寸调整为指定尺寸
mmcv.imresize(img, (1000, 600), return_scale=True)
# 将图像尺寸调整为同其他图像一致
mmcv.imresize_like(img, dst_img, return_scale=False)
# 将图像按比例调整尺寸
mmcv.imrescale(img, 0.5)
# 调整图像尺寸,较长边不超过1000、较短边不超过800,且不改变图像比例
mmcv.imrescale(img, (1000, 800))
旋转 在 m m c v {\rm mmcv} mmcv中旋转图像时可以指定旋转中心,默认为图像的几何中心。
# 读取图像
img_ = mmcv.imread('test.jpg')
# 顺时针旋转图像30度
img_ = mmcv.imrotate(img, 30)
# 逆时针旋转图像90度
img_ = mmcv.imrotate(img, -90)
# 顺时针旋转图像30度,并调整尺寸
img_ = mmcv.imrotate(img, 30, scale=1.5)
# 顺时针旋转图像30度,并设置旋转中心为(100, 100)
img_ = mmcv.imrotate(img, 30, center(100, 100))
# 顺时针旋转图像30度,并扩展图像
img_ = mmcv.imrotate(img, 30, auto_bound=True)
翻转
# 读取图像
img = mmcv.imread('test.jpg')
# 水平翻转
mmcv.imflip(img)
# 垂直翻转
mmcv.imflip(img, direction='vertival')
裁剪 在
m
m
c
v
{\rm mmcv}
mmcv中提供了在图像中裁剪单个或多个区域的操作,指定区域为(x1, y1, x2, y2)
。
# 读取图像
img = mmcv.imread('test.jpg')
# 裁剪图像,裁剪区域为(10, 10, 100, 120)
bboxes = np.array([10, 10, 100, 120])
patch = mmcv.imcrop(img, boxes)
# 裁剪图像,裁剪区域为(10, 10, 100, 120)和(0, 0, 50, 50)
bboxes = np.array([[10, 10, 100, 120], [0, 0, 50, 50]])
patches = mmcv.imcrop(img, bboxes)
# 裁剪图像,并调整裁剪后的图像的大小
patches = mmcv.imcrop(img, bboxes, scale_ratio=1.2)
填充 在
m
m
c
v
{\rm mmcv}
mmcv中提供图像填充的操作,并且可以通过pad_val
指定填充值大小。
# 读取图像
img = mmcv.imread('test.jpg')
# 填充图像尺寸至(1000, 1200)
img_ = mmcv.impad(img, shape=(1000, 1200), pad_val=0)
# 填充图像尺寸至(1000, 1200),并设置不同通道的填充值
img_ = mmcv.impad(img, shape=(1000, 1200), pad_val=[100, 50, 200])
# 填充图像,并在不同方向指定填充尺寸
img_ = mmcv.impad(img, padding=(10, 20, 30, 40), pad_val=0)
# 填充图像,并在不同方向指定填充尺寸且指定填充值大小
img_ = mmcv.impad(img, padding=(10, 20, 30, 40), pad_val=[100, 50, 200])
# 填充图像,使得每一边的长度是给定值的倍数
img_ = mmcv.impad_to_multiple(img, 32)
图像操作部分对应源码位于/root_path/mmcv/mmcv/image
。
4. 视频操作
4.1 视频读取器
# 读取视频
video = mmcv.VideoReader('test.jpg')
# 获取视频的基本信息
print(len(video), video.width, video.height, video.resolution, video.fps)
# 遍历所有视频帧
for frame in video:
print(frame.shape)
# 读取视频流的下一帧
img = video.read()
# 获取视频流中的某一帧
img = video[100]
# 获取视频流中的某一段图像
img = video[5: 10]
# 将视频转化成单独的帧并保存到指定文件夹
video = mmcv.VideoReader('test.mp4')
video.cvt2frames('out_dir')
# 基于单独的帧组成视频
mmcv.frames2video('out_dir', 'test.avi')
4.2 视频编辑工具
# 截取视频的第3至10帧,并保存至'clip1.mp4'中
mmcv.cut_video('test.mp4', 'clip1.mp4', start=3, end=10, vcodec='h264')
# 合并图像,保存至'joined.mp4'中
mmcv.concat_video(['clip1.mp4', 'clip2.mp4'], 'joined.mp4', log_level='quiet')
# 调整图像中帧尺寸至指定值,并保存至'resized1.mp4'
mmcv.resize_video('test.mp4', 'resized1.mp4', (360, 240))
# 放大图像中帧尺寸,并保存至'resized2.mp4'
mmcv.resize_video('test.mp4', 'resized2.mp4', ratio=2)
4.3 光流
在 m m c v {\rm mmcv} mmcv中提供了针对光流的输入/输出、可视化和光流其他操作等。
# 生成流
flow = np.random.rand(800, 600, 2).astype(np.float32)
# 保存至.flo文件
mmcv.flowwrite(flow, 'uncompressed.flo')
# 保存至图像
mmcv.flowwrite(flow, 'compressed.jpg', quantize=True, concat_axis=1)
# 从.flo文件中读取流
flow = mmcv.flowread('uncompressed.flo')
# 从图像中读取流
flow = mmcv.flowread('compressed.jpg', quantize=True, concat_axis=1)
# 显示
mmcv.flowshow(flow)
视频操作部分对应源码位于/root_path/mmcv/mmcv/video
。
5. 使用工具
5.1 配置文件
Config
类用于处理配置文件,支持.py
、.json
、.yaml
等格式。如test.py
的格式如下:
a = 1
b = dict(b1=[0, 1, 2], b2=None)
c = (1, 2)
d = 'string'
from mmcv import Config
# 以字典的形式给出'test.py'里的内容
cfg = Config.fromfile('test.py')
# {'a': 1, 'b': {'b1': [0, 1, 2], 'b2': None}, 'c': (1, 2), 'd': 'string'}
对于配置文件的处理,支持一些预定义的值,在程序运行时会将对应的变量替换成真实值。当前在 m m c v {\rm mmcv} mmcv中预定义了四个变量:
{{ fileDirname }}
当前文件所在文件夹的名称;{{ fileBasename }}
当前文件名;{{ fileBasenameNoExtension }}
当前文件名(不包含扩展名);{{ fileExtname }}
当前文件的扩展名。
如测试文件config_a.py
的内容及输出为:
a = 1
b = './work_dir/{{ fileBasenameNoExtension }}'
c = '{{ fileExtname }}'
# {'a': 1, 'b': './work_dir/config_a', 'c': '.py'}
配置文件的继承 定义父配置文件config_a.py
的内容如下:
a = 1
b = dict(b1=[0, 1, 2], b2=None)
首先定义子配置文件config_b.py
并继承自config_a.py
,且不会覆盖父文件的值。
_base_ = './config_a.py'
c = (1, 2)
d = 'string'
# {'a': 1, 'b': {'b1': [0, 1, 2], 'b2': None}, 'c': (1, 2), 'd': 'string}
然后定义子配置文件config_c.py
并继承自config_a.py
,且会覆盖父文件的值。
_base_ = './config_a.py'
b = dict(_delete=True, b2=None, b3=0.1)
c = (1, 2)
# {'a': 1, 'b': {'b2': None, 'b3': 0.1}, 'c': (1, 2)}
然后定义子配置文件config_f.py
并继承自config_a.py
和config_e.py
。首先config_e.py
的内容如下:
c = (1, 2)
d = 'string'
config_f.py
的内容如下:
_base_ = ['./config_a.py', './config_e.py']
# {'a': 1, 'b': {'b1': [0, 1, 2], 'b2': None}, 'c': (1, 2)}
5.2 进度条
import mmcv
import time
# 每隔0.5秒返回当前值加1后的值
def plus_one(n):
time.sleep(0.5)
return n + 1
# 定义任务
tasks = list(range(10))
# 以进度条的方式显示任务执行过程
mmcv.track_progress(plus_one, tasks)
# 以进度条的方式显示任务执行过程,8个进程
mmcv.track_progress(plus_one, tasks, 8)
如果需要实时显示任务列表的执行进度,
m
m
c
v
{\rm mmcv}
mmcv提供了track_iter_progress
函数。
import mmcv
# 任务列表
tasks = [item_1, item_2,...,item_n]
# 执行任务
for task in mmcv.track_iter_progress(tasks):
...
print(task)
# 遍历任务
for i, task in enumerate(mmcv.track_iter_progress(tasks)):
...
print(i)
print(task)
5.3 计时器
import mmcv
import time
# 使用计时器
with mmcv.Timer():
time.sleep(1)
同时,
m
m
c
v
{\rm mmcv}
mmcv提供了提供了since_start
函数和since_last_check
函数用于显示具体时间。
timer = mmcv.Timer()
# 开始时间
print(timer.since_start())
# 结束时间
print(timer.since_last_check())
该部分对应源码位于图像操作部分对应源码位于/root_path/mmcv/mmcv/utils
。
6. PyTorch运行器
官方还没有给出关于这部分的说明文档,可以参考相应的源代码部分。该部分对应源码位于/root_path/mmcv/mmcv/runner
。
7. CNN
7.1 构建层
m m c v {\rm mmcv} mmcv中建立深度学习网络中层的函数包括:
build_conv_layer
建立卷积层,包括 C o n v 1 d {\rm Conv1d} Conv1d、 C o n v 2 d {\rm Conv2d} Conv2d、 C o n v 3 d {\rm Conv3d} Conv3d等;build_norm_layer
建立归一化层,包括 B N 1 d {\rm BN1d} BN1d、 B N 2 d {\rm BN2d} BN2d、 B N 3 d {\rm BN3d} BN3d、 G N {\rm GN} GN、 L N {\rm LN} LN、 I N {\rm IN} IN等;build_activation_layer
建立激活层,包括 R e L U {\rm ReLU} ReLU、 L e a k y R e L U {\rm LeakyReLU} LeakyReLU、 P R e L U {\rm PReLU} PReLU、 S i g m o i d {\rm Sigmoid} Sigmoid等;build_upsample_layer
建立上采样层,包括 n e a r e s t {\rm nearest} nearest、 b i l i n e a r {\rm bilinear} bilinear、 d e c o n v {\rm deconv} deconv、 p i x e l s h u f f l e {\rm pixel_shuffle} pixelshuffle等;build_padding_layer
建立填充层,包括 z e r o {\rm zero} zero、 r e f l e c t {\rm reflect} reflect、 r e p l i c a t e {\rm replicate} replicate等。如:
cfg = dict(type='Conv3d')
layer = build_conv_layer(cfg, in_channels=3, out_channels=8, kernel_size=3)
除了 m m c v {\rm mmcv} mmcv中包含的函数,我们可以自定义层:
# 建立
from mmcv.cnn import UPSAMPLE_LAYERS
@UPSAMPLE_LAYERS.register_module()
class MyUpsample:
def __init__(self, scale_factor):
pass
def forward(self, x):
pass
# 使用
cfg = dict(type='MyUpsample', scale_factor=2)
layer = build_upsample_layer(cfg)
7.2 绑定模块
在定义深度网络时,有时我们需要对某个模块组合重复若干次,在 m m c v {\rm mmcv} mmcv中我们可以定义模块的组合。
# 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'))
7.3 权重初始化
在
m
m
c
v
{\rm mmcv}
mmcv中包含了常用的初始化方法,包括constant_init
、xavier_init
、normal_init
、uniform_init
、kaiming_init
、caffe2_xavier_init
和bias_init_with_prob
。如:
conv1 = nn.Conv(3, 3, 1)
normal_init(conv1, std=0.01, bias=0)
xavier_init(conv1, distribution='uniform')
7.4 预训练模型
在
m
m
c
v
{\rm mmcv}
mmcv中存放预训练模型的地址。
该部分对应源码位于/root_path/mmcv/mmcv/cnn
。
8. CUDA
官方还没有给出关于这部分的说明文档,可以参考相应的源代码部分,具体支持操作算子可见。该部分对应源码位于/root_path/mmcv/mmcv/ops
。
9. 总结
本文简要介绍了
m
m
c
v
{\rm mmcv}
mmcv的相关内容,结合以前介绍的配置文件和
m
m
d
e
t
{\rm mmdet}
mmdet构成了整个
M
M
D
e
t
e
c
t
i
o
n
{\rm MMDetection}
MMDetection项目的核心。同时,由于
m
m
d
e
t
{\rm mmdet}
mmdet中基于
O
p
e
n
C
V
{\rm OpenCV}
OpenCV等提供了大量高级
A
P
I
{\rm API}
API,我们也可以将其应用于日常任务中。下文将就训练文件train.py
开始介绍
M
M
D
e
t
e
c
t
i
o
n
{\rm MMDetection}
MMDetection的整体架构。
参考
- https://github.com/open-mmlab/mmdetection.