(七)深度学习实战 | MMDetection笔记(4)



1. 简介

是一个用于计算机视觉任务的基础 P y t h o n {\rm Python} Python库。它支持许多开源的深度学习库,如MMDetectionMMDetection3DMMSegmentationMMEditingMMPoseMMAction2MMClassification。本文主要介绍 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 颜色空间的转换

支持的颜色空间转化操作有bgr2graygray2bgrbgr2rgbrgb2bgrbgr2hsvhsv2bgr

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.pyconfig_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_initxavier_initnormal_inituniform_initkaiming_initcaffe2_xavier_initbias_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的整体架构。


参考

  1. https://github.com/open-mmlab/mmdetection.


  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值