赞
踩
行为识别,时序检测,时空检测三种任务的联系
对于视频的理解
视频 = 空间 + 时间:图像为二维空间,视频是三维,视频相对于图像多出来的维度就是时间维度。
视频理解的重点
重点1:如何描述视频中的动作?
动作 = 外观 + 运动。外观是静态的,是图像帧。运动是动态的,也叫帧间运动,就是时序上的变化。
思路1:独立提取图像特征,再进行时序建模
把静态的外观特征与动态的时序上的变化分成两个层次建模。
第一层次:提取每一个图像的外观特征。
第二层次:在外观特征所形成的序列基础之上进行一次时序建模。
提取动态信息,根据动态信息获取动作特征。
思路2:外观特征与运动特征并行计算,最后融合
首先根据单帧图像提取外观特征
通过相邻帧的变化,提取瞬时变化的信号,进而提取运动特征
以上两步并行计算,最后融合
思路3:利用更加强大的模型,从多帧图像直接计算运动特征
重点2:如何高效的处理视频数据?
视频的数据量远大于图像,一秒钟的视频就包含20~30张图像,对计算量,内存的占用都会带来巨大挑战
重点3:如何利用无标注的视频数据训练模型?
标注视频的工作量比起图像标注要打百倍千倍,传统的对每一张图片进行精细标注,不太现实。
光流的估计
这里有些Latex符号在这里会报错,所以直接贴图了。(Typora中打的)
光流的可视化
光流的两种类型
颜色表示方向,亮度表示大小
深度学习时代的视频理解
DeepVideo(2014)
用图像分类网络AlexNet在每一帧图片上提取特征并融合在一起,但是没有性能上的提升。因为它只关注每一帧图像的外观特征,没有捕捉运动特征。
Two Stream Networks(2014)
双流神经网络
Temporal Segment Networks(2016)
时序分段网络TSN
(1)与2D卷积网络的区别
3D卷积:2D卷积用于图像特征提取。而3D卷积用于视频特征提取,就是多增加了一个时间维度。
(2)3D卷积网络——I3D的提出
I3D(2017)
关键点:3D网络由图像分类的2D网络“膨胀”得来,因此可以充分利用已有的图像分类模型。
从此,基于膨胀的三维卷积核的三维卷积网络I3D逐渐称为动作识别这个领域的主流方法。
关于膨胀2D卷积
3D卷积相比于2D卷积多了一个在时间维度的堆叠
对图像分类的2D网络Inception进行膨胀
卷积膨胀:从二维的一个卷积核复制几份叠到一起,形成一个三维卷积核
相关视频理解方法的对比
(a)DeepVideo模型(2D卷积+LSTM):2D卷积只能处理单帧数据,而对于视频数据来说,则需要将2D卷积处理的多张单帧数据做融合,LSTM就是融合的方法。(即直接作用在图像上,通过二维卷积方法提取每一帧图像的外观特征,然后将特征送入LSTM来捕捉时间特征。)
(b)C3D模型(3D卷积):将2维卷积核变为3维卷积核。(单纯基于空间流,但没有使用膨胀卷积核,参数多,训练难度加大)
(c) 双流神经网络(2D卷积):还是基于图像帧去提取外观特征与运动特征。
(d)I3D模型——基于3D卷积的双流模型(3D卷积):3D卷积模型没有像2D卷积一样有成熟的预训练参数,所以借鉴了成熟的图像分类网络(2D卷积网络)Inception,将网络中的2D卷积核变为3D卷积核。H,W对应的参数直接从Inception获取,D参数自己训练。(I3D的训练方式是先通过Kinetics数据集进行预训练,再训练HMD51和UCF101并验证效果)
总而言之,I3D就是将C3D与双流网络进行融合。
(3)更高效的3D卷积网络(解决重点2)
解决办法1:分解3D卷积核
原因:全部使用3D卷积参数量最大
目的:降低参数量
思路:先过一遍空间卷积,进行空间信息的融合,再过一遍时间卷积,进行时间维度的融合。
t
d
2
>
d
2
+
t
td^2>d^2+t
td2>d2+t
相关网络:S3D(2018) & R2 + 1D(2018)
解决办法2:减少通道关联
输入通道是c,输出通道是c的时候,则需要c²个卷积核。可以选择分组卷积&逐层卷积的方法。
SlowFast(2019)
关键点:外观和运动速度的变化不同,外观变化慢,运动变化快。
思路:用低帧率对外观进行采样,用高帧率对运动进行采样。并用相对轻量级的网络结构来平衡计算量。
IG-65M(2019)
背景:Facebook2019年提出。使用Kinetics数据集。Kinetics是一个标准的学术数据集,里面提供大量视频。
主要内容:利用Kinetics数据里的关键标签,再从Instagram(大型的图像和视频分享网站)收集6500万个视频,对Kinetics上经过良好标注的视频进行补充。从而形成一个整体的弱监督的数据集,去预训练一个大模型。预训练完成后,再在Kinetics数据集上进行微调训练。
实验结果
(1)经过预训练的模型的性能优于直接在目标数据集上训练的模型
(2)预训练使用的数据越多,性能越好,不准确的标注由数据量弥补
OmniSource(2020)
背景:2020年,港中文提出的,使用多种来源的数据(长视频,短视频,图像)联合训练模型,对数据的利用更高效
主要内容
(1)数据爬取:获取原始网络的不同形态的图像视频
(2)数据过滤:使用与训练好的模型进行数据筛选,形成筛选后的数据集
(3)标准化处理:格式化视频与图像,形成标准化数据集
(4)混合数据训练模型
实验结果:分类精度进一步提升
(1)视频理解的3个基本任务:行为识别,时序动作检测,时空动作检测
(2)重点1:如何获得更好的动作特征?
(3)重点2:如何高效地处理视频数据,提高3D卷积模型地计算效率?
(4)重点3:如何控制标注成本?
action recognition实际上是个分类问题。主要由两类模型,一个是基于2D卷积神经网络的,另一种是基于3D卷积神经网络的。
2D,3D两类模型在构成上没有太大区别,区别在于处理输入方面。
model = dict( type = 'Recognizer2D', # 2D ResNet-50作为主干网络 backbone = dict( type = 'ResNet', pretrained = 'torchvision://resnet50', # 从torchvision中拿取ResNet-50的预训练参数 depth = 50, norm_eval = False), # TSN的分类头 cls_head = dict( type = 'TSNHead', # TSN的头会接收ResNet产生的特征 num_classes = 400, in_channels = 2048, spatial_type = 'avg', consensus = dict(type = 'AvgConsensus', dim = 1), # 通过平均共识函数,把多个特征平均到一起,再产生400类的分类(Kinetics400) dropout_ratio = 0.4, init_std = 0.01), )
I3D是一个3D模型,主干网络使用ResNet-50层的结构
原始论文中I3D是基于Inception,这里基于ResNet-50
关于I3D的分类头:3D卷积网络要接受的是一个5维的输入,这里通常会使用average pooling把THW3个维度压缩成1个维度。然后只剩下batch维和通道维,通道维的维度是2048,经过average pooling后,针对每个数据可以得到2048个特征,然后再用一个全连接层产生一个400维的分类概率。模型最终输出400维的分类概率。
model = dict( type = 'Recognizer3D', # 膨胀的3D ResNet-50作为主干网络 backbone = dict( type = 'ResNet3d', pretrained2d = True, # 使用 2D ResNet-50的预训练参数 pretrained = 'torchvision://resnet50', # 从torchvision中拿取ResNet-50的预训练参数 depth = 50, conv_cfg = dict(type = 'Conv3d'), norm_eval = False, ''' infalte = 1,表示在对应的层使用膨胀策略,将2D卷积变为3D卷积,指定为0就不使用膨胀 ResNet-50有4组残差模块每组残差模块中分别有3,4,6,3个残差模块,1和0就表示指定的残差模块是否膨胀。 ''' inflate = ((1, 1, 1), (1, 0, 1, 0), (1, 0, 1, 0, 1, 0), (0, 1, 0)), zero_init_residual = False), # 分类时设置为True # I3D的分类头 cls_head = dict( type = 'I3DHead', num_classes = 400, in_channels = 2048, # 通道维 spatial_type = 'avg', dropout_ratio = 0.5, init_std = 0.01), )
数据集类型:
RawframeDataset(读图像帧)——将MP4或者其他编码格式的视频先在线下解码成一帧一帧的图像,然后将所有的图像帧存到对应的目录里,这个目录就代表一个视频。目的是为了减少在训练时的解码时间。所以预先把所有视频解码好,放到一个文件夹里训练的时候按照帧的序号去读对应的图像文件。如下图所示。
依次为:图像帧目录,图像帧数量,动作分类的序号。其中,每一个目录代表一个视频。
VideoDataset(读视频)
data = dict( # batchsize(每个视频加载的进程数) videos_per_gpu = 8, # 视频读取进程数 workers_per_gpu = 4, # 指定数据子集 train/val/test = dict( # 数据集类型 type = 'RawframeDataset'/'VideoDataset'/..., # 类别标注文件(RawframeDataset读图像帧的文件夹) ann_file = 'annotation.txt', # 数据集根目录 data_prefix = 'data/kinetics400/rawframes_train', # 数据是图像还是光流 modality = 'RGB'/'Flow', # 指定数据处理的工作流,通常做数据读取或者数据增强之类的任务 pipeline = train_pipeline ) )
train_pipeline = [
dict(type = 'SampleFrames', clip_len = 32, frame_interval = 2, num_clips = 1), # 32帧,每隔两帧抽取一帧,覆盖64帧,抽取1个片段
dict(type = 'RawFrameDecode'), # 解码,成为32个h×W数组
dict(type = 'Resize', scale = (-1, 256)), # 裁剪
dict(type = 'RandomResizedCrop'),
dict(type = 'Resize', scale = (224, 224), keep_ratio = False),
dict(type = 'Flip', flip_ratio = 0.5), # 翻转
dict(type = 'Normalize', **img_norm_cfg), # 像素归一化
dict(type = 'FormatShape', input_format = 'NCTHW'), # 维度排序
dict(type = 'Collect', keys = ['imgs', 'label'], meta_keys = []),
dict(type = 'ToTensor', keys = ['imgs', 'label']) # 转化为totensor格式
]
# optimizer
optimizer = dict(
type = 'SGD',
lr = 0.01, # 8 gpus
momentum = 0.9,
weight_dacay = 0.0001)
optimizer_config = dict(grad_clip = dict(max_norm = 40, norm_type = 2))
# learning policy
lr_config = dict(policy = 'step', step = [20, 40])
total_epochs = 50
主要任务:
这里是我安装的版本,要注意相关版本的对应。具体的安装文档见最后的地址链接。
# 检查 nvcc,gcc 版本
!nvcc -V
!gcc --version
# 检查torch的版本>1.5,GPU是否可用
import torch, torchvision
print(torch.__version__, torch.cuda.is_available(), torchvision.__version__)
# 检查mmcv版本
from mmcv.ops import get_compiling_cuda_version, get_compiler_version
print(get_compiler_version())
print(get_compiling_cuda_version())
# 检查MMAction2的版本
import mmaction
print(mmaction.__version__)
# 创建checkpoints文件夹,并下载TSN模型
!mkdir checkpoints
from mmaction.apis import inference_recognizer, init_recognizer
# 选择tsn对应的配置文件
config = 'configs/recognition/tsn/tsn_r50_video_inference_1x1x3_100e_kinetics400_rgb.py'
# 加载上面下载的checkpoint文件
checkpoint = 'checkpoints/tsn_r50_1x1x3_100e_kinetics400_rgb_20200614-e508be42.pth'
# 在GPU上初始化该模型
model = init_recognizer(config, checkpoint, device='cuda:0')
# 选择视频进行推理
video = 'demo/demo.mp4'
label = 'tools/data/kinetics/label_map_k400.txt' # 400行的文件,每行就是数据集的一个类别
results = inference_recognizer(model, video)
labels = open(label).readlines()
labels = [x.strip() for x in labels]
results = [(labels[k[0]], k[1]) for k in results]
# 查看视频,传入已定义的video
from IPython.display import HTML
from base64 import b64encode
mp4 = open(video,'rb').read() # rb以二进制格式打开一个文件用于只读。
data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
HTML("""
<video width=400 controls>
<source src="%s" type="video/mp4">
</video>
""" % data_url)
demo视频为arm wrestling,地址:demo.mp4
# 查看推理Top-5结果
for result in results:
print(f'{result[0]}: ', result[1])
最终用这个预训练的TSN模型得到以下分类结果
训练新模型的三个步骤:
下载并且解压数据集kinetics400_tiny,下载地址:kinetics400_tiny
# 查看标注文件格式(linux命令-cat,正斜杠/)
!type kinetics400_tiny\kinetics_tiny_train_video.txt
# 获得tsn对应的配置文件cfg(分了8个clip)
from mmcv import Config
cfg = Config.fromfile('./configs/recognition/tsn/tsn_r50_video_1x1x8_100e_kinetics400_rgb.py')
from mmcv.runner import set_random_seed # 修改数据集类型和各个文件路径 cfg.dataset_type = 'VideoDataset' cfg.data_root = 'kinetics400_tiny/train/' cfg.data_root_val = 'kinetics400_tiny/val/' cfg.ann_file_train = 'kinetics400_tiny/kinetics_tiny_train_video.txt' cfg.ann_file_val = 'kinetics400_tiny/kinetics_tiny_val_video.txt' cfg.ann_file_test = 'kinetics400_tiny/kinetics_tiny_val_video.txt' cfg.data.test.type = 'VideoDataset' cfg.data.test.ann_file = 'kinetics400_tiny/kinetics_tiny_val_video.txt' cfg.data.test.data_prefix = 'kinetics400_tiny/val/' cfg.data.train.type = 'VideoDataset' cfg.data.train.ann_file = 'kinetics400_tiny/kinetics_tiny_train_video.txt' cfg.data.train.data_prefix = 'kinetics400_tiny/train/' cfg.data.val.type = 'VideoDataset' cfg.data.val.ann_file = 'kinetics400_tiny/kinetics_tiny_val_video.txt' cfg.data.val.data_prefix = 'kinetics400_tiny/val/' # 这里用于确认是否使用到omnisource训练 cfg.setdefault('omnisource', False) # 修改cls_head中类别数为2 cfg.model.cls_head.num_classes = 2 # 使用预训练好的tsn模型 cfg.load_from = './checkpoints/tsn_r50_1x1x3_100e_kinetics400_rgb_20200614-e508be42.pth' # 设置工作目录 cfg.work_dir = './tutorial_exps' # 由于是单卡训练,修改对应的lr cfg.data.videos_per_gpu = cfg.data.videos_per_gpu // 16 # 为了加速运行,把batchsize改为原来的1/16 cfg.optimizer.lr = cfg.optimizer.lr / 8 / 16 # 原始配置文件中使用8卡训练,根据线性扩展策略,把lr降到原来的1/128 cfg.total_epochs = 30 # 设置存档点间隔减少存储空间的消耗 cfg.checkpoint_config.interval = 10 # 设置日志打印间隔减少打印时间 cfg.log_config.interval = 5 # 固定随机种子使得结果可复现 cfg.seed = 0 set_random_seed(0, deterministic=False) cfg.gpu_ids = range(1) # 打印所有的配置参数 print(f'Config:\n{cfg.pretty_text}')
import os.path as osp from mmaction.datasets import build_dataset # 调用build_dataset构建数据集 from mmaction.models import build_model # 调用build_model构建模型 from mmaction.apis import train_model # 调用train_model训练模型,传入配置文件,数据,模型 import mmcv # 构建数据集 datasets = [build_dataset(cfg.data.train)] # 构建动作识别模型(基于预训练模型,把分类数改为2) model = build_model(cfg.model, train_cfg=cfg.get('train_cfg'), test_cfg=cfg.get('test_cfg')) # 创建工作目录并训练模型 mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir)) train_model(model, datasets, cfg, distributed=False, validate=True)
30个epoch后,最终精度如下图。
from mmaction.apis import single_gpu_test from mmaction.datasets import build_dataloader from mmcv.parallel import MMDataParallel # 构建测试数据集 dataset = build_dataset(cfg.data.test, dict(test_mode=True)) data_loader = build_dataloader( dataset, videos_per_gpu=1, # batchsize设置为1 workers_per_gpu=cfg.data.workers_per_gpu, dist=False, shuffle=False) model = MMDataParallel(model, device_ids=[0]) # 初始化模型 outputs = single_gpu_test(model, data_loader) # 得到所有模型的分类输出 # 在测试集上评价训练完成的识别模型 eval_config = cfg.evaluation eval_config.pop('interval') eval_res = dataset.evaluate(outputs, **eval_config) # 比较输出值与真实值,计算准确率 for name, val in eval_res.items(): print(f'{name}: {val:.04f}')
结果如下图
# 克隆mmdetection项目
%cd ..
!git clone https://github.com/open-mmlab/mmdetection.git
%cd mmdetection
# 以可编辑的模式安装mmdet
!pip install -e .
%cd ../mmaction2
# 上传视频至目录mmaction2下
!wget https://download.openmmlab.com/mmaction/dataset/sample/1j20qq1JyX4.mp4 -O demo/1j20qq1JyX4.mp4
# 完成时空检测
!python demo/demo_spatiotemporal_det.py --video demo/1j20qq1JyX4.mp4
(1)在时空检测的过程中,首先用下载的Fast-RCNN模型做一个人的检测(每帧都检测)
Fast-RCNN配置文件地址:Fast-RCNN
(2)然后下载行为识别模型slowfast(仅有slow),用OmniSource的方法预训练,完成时空检测。
SlowOnly(SlowFast)配置文件地址:SlowOnly
(3)最后检测完之后,下载ffmpeg把对应的图像编码成一个视频,做成可视化的demo。
# 查看视频
from IPython.display import HTML
from base64 import b64encode
mp4 = open('demo/stdet_demo.mp4','rb').read()
data_url = "data:video/mp4;base64," + b64encode(mp4).decode()
HTML("""
<video width=400 controls>
<source src="%s" type="video/mp4">
</video>
""" % data_url)
时空动作识别原视频地址:时空动作识别原视频
时空动作识别后:
时空动作识别——stdet_demo
1.相关课程 视频理解
2. MMAction安装步骤官方文档 MMAction2安装
3. 预训练模型TSN下载地址:TSN下载
4.kinetics400_tiny 下载地址:kinetics400_tiny数据集下载
5. MMAction2的demo.mp4地址 demo.mp4
6. Fast-RCNN配置文件地址:Fast-RCNN
7. SlowOnly(SlowFast)配置文件地址:SlowOnly
8. MMAction2的demo地址:demo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。