三维卷积 (3D CNN) 简介
1 理论
1.1 三维卷积的提出
卷积神经网络(Convolutional neural networks, CNNs)是一种深度模型,可直接作用于原始输入,从而自动化特征构建过程。然而,这种模型目前仅限于处理2D输入。
在本文中,作者开发了一种新颖的3D CNN动作识别模型。该模型通过3D卷积从空间和时间维度提取特征,从而捕获编码在多个相邻帧中的运动信息。
该模型从输入帧中生成多个通道的信息,并将所有通道的信息进行组合得到最终的特征表示。
作者将所开发的模型应用于识别真实环境中的人类行为,并在不依赖于手工制作的功能的情况下实现了卓越的性能。
3D CNN 的优势
在这个方向上,一个简单的方法是将视频帧视为静止图像,并应用CNN 在单个帧级别识别动作 。然而,该方法 没有考虑编码在多个连续帧中的运动信息。为了在视频分析中有效地融合运动信息,作者提出在CNN的卷积层中进行三维卷积,从而捕获空间和时间维度上的鉴别特征。 通过在输入的相同位置上应用多个不同的卷积操作,可以提取多种类型的特征。基于提出的3D卷积,可以设计出各种3D CNN架构来分析视频数据。 对于3D CNN架构,可以从相邻的视频帧生成多个通道的信息,并在每个通道分别进行卷积和子采样。最后的特征表示由所有通道的信息组合而成。 基于CNN的模型的另一个优势是,由于其前馈特性,识别阶段非常有效。
1.2 三维卷积的结构
在介绍 3D CNN 之前,回顾 2D CNN。
2D卷积操作
在 子采样层(池化层) 中,特征映射的分辨率通过在前一层的特征映射上汇集局部邻域来降低,从而增加了输入失真的不变性。
可以通过交替叠加多层卷积和子采样来构建CNN结构。
CNN的参数,通常使用有监督或无监督的方法进行训练
3D卷积操作
在 2D CNNs 中,对二维特征图应用卷积,仅从空间维度计算特征。
当应用于视频分析问题时,希望捕获编码在多个连续帧中的运动信息。为此提出在 CNN 的卷积阶段进行 3D卷积,从空间和时间两个维度计算特征。
三维卷积是通过将一个三维核与多个连续帧叠加在一起形成的立方体进行卷积来实现的。
通过这种构造,卷积层中的特征映射被连接到多个连续帧中的上一层,从而捕捉运动信息。
3D CNN 的形式
2D 和 3D CNN的比较
2D 提取的特征仍为 2D; 3D 提取的特征仍为 3D,因为使用同一个3D核,产生的特征图存在重叠。
上图3D卷积核只能从框架立方体中提取一种类型的特征,因为核权值在整个立方体中被复制(权值共享)。
CNN 的一般设计原则是,通过从同一层生成多种类型的特征,在后期层中增加特征图的数量从一组较低级别的特征图提取多类型特征。
类似于二维卷积的情况,这可以通过在前一层的相同位置应用多个具有不同核的三维卷积来实现,如下图所示。
因为权值不共享,所以产生的特征图不同。
1.3 三维卷积的应用
基于上面描述的3D卷积,可以设计出各种CNN架构。
下图描述了一个3D CNN架构,它是在TRECVID数据集上为人类动作识别而开发的。
首先应用一组 hardweird核 来 从输入帧生成多个信息通道。这将导致在 5 个不同的通道中,第二层有 33 个特征图。hardweird 层 用来编码我们关于特征的先验知识,与随机初始化相比,这种方案通常会带来更好的性能。 为了增加feature map的数量,在每个位置上应用两组不同的卷积,从而在C2层中得到两组feature map,每组包含23个feature map。这一层包含1480个可训练参数。 在后续的子采样层S3中,对C2层的每个特征图都进行了2 × 2的子采样,这使得空间分辨率降低的特征图数量相同。该层中可训练参数的数量为92个。 对两组feature map中的 5 个channel分别进行核大小为7 × 6 × 3的3D卷积,得到下一层卷积C4。 为了增加特征图的数量,在每个位置使用3个不同核的卷积,从而在 C4 层中得到6组不同的特征图,每组包含13个特征图。这一层包含3810个可训练参数。 下一层 S5 是通过对 C4 层的每个feature map应用3 × 3 subsampling得到的,这使得空间分辨率降低的feature map数量相同。该层可训练参数个数为156个。 在这个阶段,时间维度的大小已经相对较小(gray, gradient-x, gradient-y为3,optflow-x, optflow-y为2),所以只在这一层的空间维度上进行卷积。所使用的卷积核的大小为7 × 4,因此输出特征图的大小减少到11。C6层由128个大小为1 × 1的feature map组成,每一个feature map都连接到S5层的全部78个feature map,得到289,536个可训练参数。
通过多层卷积和子采样,将7个输入帧转换为128D特征向量,捕捉输入帧中的运动信息。
输出层由与动作数量相同的单元组成,每个单元与C6层的128个单元中的每个单元完全连接。
在本次设计中,本质上是在128D特征向量上应用一个线性分类器进行动作分类。
对于有3类的动作识别问题,输出层可训练参数的个数为 384 个。该 3D CNN 模型中可训练的参数总数为295,458个,全部随机初始化,并采用在线误差反向传播算法进行训练。
我们已经设计和评估了其他3D CNN架构,在不同阶段结合了多个信息渠道,我们的结果表明,该架构具有最好的性能。
1.4 数据集
TRECVID 2008开发数据集由在伦敦盖特威克机场拍摄的49小时视频组成,使用5个不同的摄像机,分辨率为720×576,帧率为 25 fps。
3个动作类识别(CellToEar, ObjectPut, Pointing)。
每个动作都以一对一的方式进行分类,不属于这 3类 的动作会产生大量的负样本。该数据集采集时间为5天(20071101、20071106、20071107、20071108、20071112),实验数据统计结果见下表。
数据处理
由于视频是在真实环境中录制的,每一帧都有多个人,应用了一个人体探测器和一个检测驱动的跟踪器来定位人的头部。一些样本人体检测和跟踪结果如图所示。
Sample human detection and tracking results from camera
根据检测和跟踪的结果,计算出每个执行动作的人的边界框。3D CNN模型需要的多帧是通过从当前帧前后的连续帧中提取相同位置的包围框,得到包含动作的立方体。
在实验中,立方体的时间维度设置为7,因为已经证明5-7帧足够实现整个视频序列。提取帧的步长为2。也就是说,假设当前帧编号为0,从编号为 -6、-4、-2、0、2、4 和 6 的帧中提取相同位置的边界框。每帧包围框内的 patch 被缩放到 60 × 40 像素。
1.5 实验结果
从图中可以看出,总体上 3D CNN的效果更好,但在POINTING的分类中结果低于 2D CNN。
2 实践
这里没有写 model,简单写了一个视频读取的代码。
import cv2
import numpy as np
import torchvision.transforms as transforms
import torch.nn as nn
import torch
from PIL import Image
#------------------------------------------------------------------------
cap = cv2.VideoCapture(/content/v_ApplyEyeMakeup_g01_c01.avi) # 读取视频
all_frames = []
"""判断视频对象是否成功读取,成功读取视频对象返回True"""
while cap.isOpened():
"""按帧读取视频,返回值succ是布尔型,正确读取则返回True,读取失败或读取视频结尾则会返回False。
frame为每一帧的图像,这里图像是三维矩阵,即frame.shape = (240,320,3),读取的图像为BGR格式。"""
succ, frame = cap.read()
if not succ:
break
if frame.any():
all_frames.append(frame) # 164 // number_frame
# all_frames = np.stack(all_frames, axis=0)
cap.release()
#------------------------------------------------------------------------
transform = transforms.Compose([transforms.Resize((224, 224)),
transforms.ToTensor()])
all_frames = [torch.unsqueeze(transform(Image.fromarray(np.uint8(arr)).convert(RGB)),0)
for arr in all_frames]
all_frames = torch.cat(all_frames,dim=0)
# 164, 3, 224, 224 // number_frame, channel, height, weight
all_frames = all_frames.permute(1,0,2,3)
# 3, 164, 224, 224 // channel, number_frame, height, weight
#------------------------------------------------------------------------
batch_size = 1
num_frames = 164
channel_in= 3
channel_out = 5
m = nn.Conv3d(channel_in, channel_out, (3, 5, 2), stride=2)
output = m(all_frames.unsqueeze(0))
# 1, 5, 81, 110, 112 // batch_size, channel_out, num_frames, height, weight
后台回复 “c3d”,即可获取数据链接。
参考:
3D Convolutional Neural Networks for Human Action Recognition.