当前位置:   article > 正文

详解视频中动作识别模型与代码实践_ret, frame = cap.read() keyboardinterrupt

ret, frame = cap.read() keyboardinterrupt
摘要:本案例将为大家介绍视频动作识别领域的经典模型并进行代码实践。

本文分享自华为云社区《视频动作识别》,作者:HWCloudAI。实验目标

通过本案例的学习:

  • 掌握 C3D 模型训练和模型推理、I3D 模型推理的方法;

注意事项

  1. 本案例推荐使用TensorFlow-1.13.1,需使用 GPU 运行,请查看《ModelArts JupyterLab 硬件规格使用指南》了解切换硬件规格的方法;
  2. 如果您是第一次使用 JupyterLab,请查看《ModelArts JupyterLab使用指导》了解使用方法;
  3. 如果您在使用 JupyterLab 过程中碰到报错,请参考《ModelArts JupyterLab常见问题解决办法》尝试解决问题。

实验步骤

案例内容介绍

视频动作识别是指对一小段视频中的内容进行分析,判断视频中的人物做了哪种动作。视频动作识别与图像领域的图像识别,既有联系又有区别,图像识别是对一张静态图片进行识别,而视频动作识别不仅要考察每张图片的静态内容,还要考察不同图片静态内容之间的时空关系。比如一个人扶着一扇半开的门,仅凭这一张图片无法判断该动作是开门动作还是关门动作。

视频分析领域的研究相比较图像分析领域的研究,发展时间更短,也更有难度。视频分析模型完成的难点首先在于,需要强大的计算资源来完成视频的分析。视频要拆解成为图像进行分析,导致模型的数据量十分庞大。视频内容有很重要的考虑因素是动作的时间顺序,需要将视频转换成的图像通过时间关系联系起来,做出判断,所以模型需要考虑时序因素,加入时间维度之后参数也会大量增加。

得益于 PASCAL VOC、ImageNet、MS COCO 等数据集的公开,图像领域产生了很多的经典模型,那么在视频分析领域有没有什么经典的模型呢?答案是有的,本案例将为大家介绍视频动作识别领域的经典模型并进行代码实践。

1. 准备源代码和数据

这一步准备案例所需的源代码和数据,相关资源已经保存在 OBS 中,我们通过ModelArts SDK将资源下载到本地,并解压到当前目录下。解压后,当前目录包含 data、dataset_subset 和其他目录文件,分别是预训练参数文件、数据集和代码文件等。


  
  
  1. import os
  2. import moxing as mox
  3. if not os.path.exists( 'videos'):
  4. mox. file. copy( "obs://ai-course-common-26-bj4-v2/video/video.tar.gz", "./video.tar.gz")
  5. # 使用tar命令解压资源包
  6. os.system( "tar xf ./video.tar.gz")
  7. # 使用rm命令删除压缩包
  8. os.system( "rm ./video.tar.gz")
  9. INFO:root: Using MoXing-v 1.17.3-
  10. INFO:root: Using OBS-Python-SDK- 3.20.7
  • 1

上一节课我们已经介绍了视频动作识别有 HMDB51、UCF-101 和 Kinetics 三个常用的数据集,本案例选用了 UCF-101 数据集的部分子集作为演示用数据集,接下来,我们播放一段 UCF-101 中的视频:

video_name = "./data/v_TaiChi_g01_c01.avi"
  
  
  • 1


  
  
  1. from IPython. display import clear_ output, Image, display, HTML
  2. import time
  3. import cv 2
  4. import base 64
  5. import numpy as np
  6. def arrayShow(img):
  7. _,ret = cv 2.imencode( '.jpg', img)
  8. return Image( data =ret)
  9. cap = cv 2.VideoCapture(video_name)
  10. while True:
  11. try:
  12. clear_ output(wait = True)
  13. ret, frame = cap. read()
  14. if ret:
  15. tmp = cv 2.cvtColor(frame, cv 2.COLOR_BGR 2RGB)
  16. img = arrayShow(frame)
  17. display(img)
  18. time.sleep( 0.05)
  19. else:
  20. break
  21. except KeyboardInterrupt:
  22. cap. release()
  23. cap. release()
  • 1

2. 视频动作识别模型介绍

在图像领域中,ImageNet 作为一个大型图像识别数据集,自 2010 年开始,使用此数据集训练出的图像算法层出不穷,深度学习模型经历了从 AlexNet 到 VGG-16 再到更加复杂的结构,模型的表现也越来越好。在识别千种类别的图片时,错误率表现如下:

在图像识别中表现很好的模型,可以在图像领域的其他任务中继续使用,通过复用模型中部分层的参数,就可以提升模型的训练效果。有了基于 ImageNet 模型的图像模型,很多模型和任务都有了更好的训练基础,比如说物体检测、实例分割、人脸检测、人脸识别等。

那么训练效果显著的图像模型是否可以用于视频模型的训练呢?答案是 yes,有研究证明,在视频领域,如果能够复用图像模型结构,甚至参数,将对视频模型的训练有很大帮助。但是怎样才能复用上图像模型的结构呢?首先需要知道视频分类与图像分类的不同,如果将视频视作是图像的集合,每一个帧将作为一个图像,视频分类任务除了要考虑到图像中的表现,也要考虑图像间的时空关系,才可以对视频动作进行分类。

为了捕获图像间的时空关系,论文 I3D 介绍了三种旧的视频分类模型,并提出了一种更有效的 Two-Stream Inflated 3D ConvNets(简称 I3D)的模型,下面将逐一简介这四种模型,更多细节信息请查看原论文。

旧模型一:卷积网络 + LSTM

模型使用了训练成熟的图像模型,通过卷积网络,对每一帧图像进行特征提取、池化和预测,最后在模型的末端加一个 LSTM 层(长短期记忆网络),如下图所示,这样就可以使模型能够考虑时间性结构,将上下文特征联系起来,做出动作判断。这种模型的缺点是只能捕获较大的工作,对小动作的识别效果较差,而且由于视频中的每一帧图像都要经过网络的计算,所以训练时间很长。

旧模型二:3D 卷积网络

3D 卷积类似于 2D 卷积,将时序信息加入卷积操作。虽然这是一种看起来更加自然的视频处理方式,但是由于卷积核维度增加,参数的数量也增加了,模型的训练变得更加困难。这种模型没有对图像模型进行复用,而是直接将视频数据传入 3D 卷积网络进行训练。

旧模型三:Two-Stream 网络

Two-Stream 网络的两个流分别为 1 张 RGB 快照和 10 张计算之后的光流帧画面组成的栈。两个流都通过 ImageNet 预训练好的图像卷积网络,光流部分可以分为竖直和水平两个通道,所以是普通图片输入的 2 倍,模型在训练和测试中表现都十分出色。

光流视频 optical flow video

上面讲到了光流,在此对光流做一下介绍。光流是什么呢?名字很专业,感觉很陌生,但实际上这种视觉现象我们每天都在经历,我们坐高铁的时候,可以看到窗外的景物都在快速往后退,开得越快,就感受到外面的景物就是 “刷” 地一个残影,这种视觉上目标的运动方向和速度就是光流。光流从概念上讲,是对物体运动的观察,通过找到相邻帧之间的相关性来判断帧之间的对应关系,计算出相邻帧画面中物体的运动信息,获取像素运动的瞬时速度。在原始视频中,有运动部分和静止的背景部分,我们通常需要判断的只是视频中运动部分的状态,而光流就是通过计算得到了视频中运动部分的运动信息。

下面是一个经过计算后的原视频及光流视频。

原视频

光流视频

新模型:Two-Stream Inflated 3D ConvNets

新模型采取了以下几点结构改进:

  • 拓展 2D 卷积为 3D。直接利用成熟的图像分类模型,只不过将网络中二维 $ N × N 的 filters 和 pooling kernels 直接变成的 filters 和 poolingkernels 直接变成 N × N × N $;
  • 用 2D filter 的预训练参数来初始化 3D filter 的参数。上一步已经利用了图像分类模型的网络,这一步的目的是能利用上网络的预训练参数,直接将 2D filter 的参数直接沿着第三个时间维度进行复制 N 次,最后将所有参数值再除以 N;
  • 调整感受野的形状和大小。新模型改造了图像分类模型 Inception-v1 的结构,前两个 max-pooling 层改成使用 $ 1 × 3 × 3 kernels and stride 1 in time,其他所有 max-pooling 层都仍然使用对此的 kernel 和 stride,最后一个 average pooling 层使用 kernelsandstride1intime,其他所有 maxpooling 层都仍然使用对此的 kernel 和 stride,最后一个 averagepooling 层使用 2 × 7 × 7 $ 的 kernel。
  • 延续了 Two-Stream 的基本方法。用双流结构来捕获图片之间的时空关系仍然是有效的。

最后新模型的整体结构如下图所示:

好,到目前为止,我们已经讲解了视频动作识别的经典数据集和经典模型,下面我们通过代码来实践地跑一跑其中的两个模型:C3D 模型( 3D 卷积网络)以及 I3D 模型(Two-Stream Inflated 3D ConvNets)。

C3D 模型结构

我们已经在前面的 “旧模型二:3D 卷积网络” 中讲解到 3D 卷积网络是一种看起来比较自然的处理视频的网络,虽然它有效果不够好,计算量也大的特点,但它的结构很简单,可以构造一个很简单的网络就可以实现视频动作识别,如下图所示是 3D 卷积的示意图:

a) 中,一张图片进行了 2D 卷积, b) 中,对视频进行 2D 卷积,将多个帧视作多个通道, c) 中,对视频进行 3D 卷积,将时序信息加入输入信号中。

ab 中,output 都是一张二维特征图,所以无论是输入是否有时间信息,输出都是一张二维的特征图,2D 卷积失去了时序信息。只有 3D 卷积在输出时,保留了时序信息。2D 和 3D 池化操作同样有这样的问题。

如下图所示是一种 C3D 网络的变种:(如需阅读原文描述,请查看 I3D 论文 2.2 节)

C3D 结构,包括 8 个卷积层,5 个最大池化层以及 2 个全连接层,最后是 softmax 输出层。

所有的 3D 卷积核为 $ 3 × 3 × 3$ 步长为 1,使用 SGD,初始学习率为 0.003,每 150k 个迭代,除以 2。优化在 1.9M 个迭代的时候结束,大约 13epoch。

数据处理时,视频抽帧定义大小为:$ c × l × h × w,,c 为通道数量,为通道数量,l 为帧的数量,为帧的数量,h 为帧画面的高度,为帧画面的高度,w 为帧画面的宽度。3D 卷积核和池化核的大小为为帧画面的宽度。3D 卷积核和池化核的大小为 d × k × k,,d 是核的时间深度,是核的时间深度,k 是核的空间大小。网络的输入为视频的抽帧,预测出的是类别标签。所有的视频帧画面都调整大小为是核的空间大小。网络的输入为视频的抽帧,预测出的是类别标签。所有的视频帧画面都调整大小为 128 × 171 $,几乎将 UCF-101 数据集中的帧调整为一半大小。视频被分为不重复的 16 帧画面,这些画面将作为模型网络的输入。最后对帧画面的大小进行裁剪,输入的数据为 $16 × 112 × 112 $

3.C3D 模型训练

接下来,我们将对 C3D 模型进行训练,训练过程分为:数据预处理以及模型训练。在此次训练中,我们使用的数据集为 UCF-101,由于 C3D 模型的输入是视频的每帧图片,因此我们需要对数据集的视频进行抽帧,也就是将视频转换为图片,然后将图片数据传入模型之中,进行训练。

在本案例中,我们随机抽取了 UCF-101 数据集的一部分进行训练的演示,感兴趣的同学可以下载完整的 UCF-101 数据集进行训练。

UCF-101 下载

数据集存储在目录 dataset_subset 下

如下代码是使用 cv2 库进行视频文件到图片文件的转换


  
  
  1. import cv 2
  2. import os
  3. # 视频数据集存储位置
  4. video_path = './dataset_subset/'
  5. # 生成的图像数据集存储位置
  6. save_path = './dataset/'
  7. # 如果文件路径不存在则创建路径
  8. if not os.path.exists(save_path):
  9. os.mkdir(save_path)
  10. # 获取动作列表
  11. action_list = os.listdir(video_path)
  12. # 遍历所有动作
  13. for action in action_list:
  14. if action.startswith( ".") = = False:
  15. if not os.path.exists(save_path +action):
  16. os.mkdir(save_path +action)
  17. video_list = os.listdir(video_path +action)
  18. # 遍历所有视频
  19. for video in video_list:
  20. prefix = video.split( '.')[ 0]
  21. if not os.path.exists(os.path.join(save_path, action, prefix)):
  22. os.mkdir(os.path.join(save_path, action, prefix))
  23. save_name = os.path.join(save_path, action, prefix) + '/'
  24. video_name = video_path +action + '/' +video
  25. # 读取视频文件
  26. # cap为视频的帧
  27. cap = cv 2.VideoCapture(video_name)
  28. # fps为帧率
  29. fps = int(cap. get(cv 2.CAP_PROP_FRAME_ COUNT))
  30. fps_ count = 0
  31. for i in range(fps):
  32. ret, frame = cap. read()
  33. if ret:
  34. # 将帧画面写入图片文件中
  35. cv 2.imwrite(save_name +str( 10000 +fps_ count) + '.jpg',frame)
  36. fps_ count + = 1
  • 1

此时,视频逐帧转换成的图片数据已经存储起来,为模型训练做准备。

4. 模型训练

首先,我们构建模型结构。

C3D 模型结构我们之前已经介绍过,这里我们通过 keras 提供的 Conv3D,MaxPool3D,ZeroPadding3D 等函数进行模型的搭建。


  
  
  1. from keras.layers import Dense,Dropout,Conv 3D, Input,MaxPool 3D,Flatten,Activation, ZeroPadding 3D
  2. from keras.regularizers import l 2
  3. from keras.models import Model, Sequential
  4. # 输入数据为 112× 112 的图片, 16帧, 3通道
  5. input_shape = ( 112,112,16,3)
  6. # 权重衰减率
  7. weight_decay = 0.005
  8. # 类型数量,我们使用UCF- 101 为数据集,所以为 101
  9. nb_classes = 101
  10. # 构建模型结构
  11. inputs = Input( input_shape)
  12. x = Conv 3D( 64,( 3,3,3),strides =( 1,1,1),padding = 'same',
  13. activation = 'relu',kernel_regularizer =l 2(weight_decay))(inputs)
  14. x = MaxPool 3D(( 2,2,1),strides =( 2,2,1),padding = 'same')(x)
  15. x = Conv 3D( 128,( 3,3,3),strides =( 1,1,1),padding = 'same',
  16. activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  17. x = MaxPool 3D(( 2,2,2),strides =( 2,2,2),padding = 'same')(x)
  18. x = Conv 3D( 128,( 3,3,3),strides =( 1,1,1),padding = 'same',
  19. activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  20. x = MaxPool 3D(( 2,2,2),strides =( 2,2,2),padding = 'same')(x)
  21. x = Conv 3D( 256,( 3,3,3),strides =( 1,1,1),padding = 'same',
  22. activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  23. x = MaxPool 3D(( 2,2,2),strides =( 2,2,2),padding = 'same')(x)
  24. x = Conv 3D( 256, ( 3, 3, 3), strides =( 1, 1, 1), padding = 'same',
  25. activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  26. x = MaxPool 3D(( 2, 2, 2), strides =( 2, 2, 2), padding = 'same')(x)
  27. x = Flatten()(x)
  28. x = Dense( 2048,activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  29. x = Dropout( 0.5)(x)
  30. x = Dense( 2048,activation = 'relu',kernel_regularizer =l 2(weight_decay))(x)
  31. x = Dropout( 0.5)(x)
  32. x = Dense(nb_classes,kernel_regularizer =l 2(weight_decay))(x)
  33. x = Activation( 'softmax')(x)
  34. model = Model(inputs, x)
  35. Using TensorFlow backend.
  36. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 526: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  37. _np_qint 8 = np.dtype([( "qint8", np.int 8, 1)])
  38. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 527: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  39. _np_quint 8 = np.dtype([( "quint8", np.uint 8, 1)])
  40. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 528: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  41. _np_qint 16 = np.dtype([( "qint16", np.int 16, 1)])
  42. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 529: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  43. _np_quint 16 = np.dtype([( "quint16", np.uint 16, 1)])
  44. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 530: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  45. _np_qint 32 = np.dtype([( "qint32", np.int 32, 1)])
  46. /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /dtypes.py: 535: FutureWarning: Passing ( type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as ( type, ( 1,)) / '(1,)type'.
  47. np_resource = np.dtype([( "resource", np.ubyte, 1)])
  48. WARNING:tensorflow: From /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /framework /op_def_library.py: 263: colocate_ with ( from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
  49. Instructions for updating:
  50. Colocations handled automatically by placer.
  51. WARNING:tensorflow: From /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /keras /backend /tensorflow_backend.py: 3445: calling dropout ( from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.
  52. Instructions for updating:
  53. Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.
  • 1

通过 keras 提供的 summary () 方法,打印模型结构。可以看到模型的层构建以及各层的输入输出情况。

model.summary()
  
  
  • 1

此处输出较长,省略

通过 keras 的 input 方法可以查看模型的输入形状,shape 分别为 (batch size, width, height, frames, channels) 。


  
  
  1. model. input
  2. <tf.Tensor 'input_1:0' shape =(?, 112, 112, 16, 3) dtype =float 32 >
  • 1

可以看到模型的数据处理的维度与图像处理模型有一些差别,多了 frames 维度,体现出时序关系在视频分析中的影响。

接下来,我们开始将图片文件转为训练需要的数据形式。


  
  
  1. # 引用必要的库
  2. from keras.optimizers import SGD,Adam
  3. from keras.utils import np_utils
  4. import numpy as np
  5. import random
  6. import cv2
  7. import matplotlib.pyplot as plt
  8. # 自定义callbacks
  9. from schedules import onetenth_4_8_12
  10. INFO:matplotlib.font_manager:font search path ['/home/ma-user/anaconda3/envs/ TensorFlow- 1.13. 1/lib/python3. 6/site-packages/matplotlib/mpl- data/fonts/ttf', '/home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/afm', '/home/ma-user/anaconda3/envs/TensorFlow-1.13.1/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts']
  11. INFO:matplotlib.font_manager:generated new fontManager
  • 1

参数定义


  
  
  1. img_path = save_path # 图片文件存储位置
  2. results_path = './results' # 训练结果保存位置
  3. if not os. path.exists(results_path):
  4. os.mkdir(results_path)
  • 1

数据集划分,随机抽取 4/5 作为训练集,其余为验证集。将文件信息分别存储在 train_list 和 test_list 中,为训练做准备。


  
  
  1. cates = os.listdir(img_path)
  2. train_list = []
  3. test_list = []
  4. # 遍历所有的动作类型
  5. for cate in cates:
  6. videos = os.listdir(os.path.join(img_path, cate))
  7. length = len(videos) / / 5
  8. # 训练集大小,随机取视频文件加入训练集
  9. train = random.sample(videos, length * 4)
  10. train_list. extend(train)
  11. # 将余下的视频加入测试集
  12. for video in videos:
  13. if video not in train:
  14. test_list.append(video)
  15. print( "训练集为:")
  16. print( train_list)
  17. print( "共%d 个视频\n"%(len(train_list)))
  18. print( "验证集为:")
  19. print( test_list)
  20. print( "共%d 个视频"%(len( test_list)))
  • 1

此处输出较长,省略

接下来开始进行模型的训练。

首先定义数据读取方法。方法 process_data 中读取一个 batch 的数据,包含 16 帧的图片信息的数据,以及数据的标注信息。在读取图片数据时,对图片进行随机裁剪和翻转操作以完成数据增广。


  
  
  1. def process_ data(img_path, file_list,batch_ size = 16,train = True):
  2. batch = np. zeros((batch_ size, 16,112,112,3),dtype = 'float32')
  3. labels = np. zeros(batch_ size,dtype = 'int')
  4. cate_list = os.listdir(img_path)
  5. def read_classes():
  6. path = "./classInd.txt"
  7. with open(path, "r+") as f:
  8. lines = f.readlines()
  9. classes = {}
  10. for line in lines:
  11. c_id = line.split()[ 0]
  12. c_name = line.split()[ 1]
  13. classes[c_name] =c_id
  14. return classes
  15. classes_dict = read_classes()
  16. for file in file_list:
  17. cate = file.split( "_")[ 1]
  18. img_list = os.listdir(os.path.join(img_path, cate, file))
  19. img_list. sort()
  20. batch_img = []
  21. for i in range(batch_ size):
  22. path = os.path.join(img_path, cate, file)
  23. label = int(classes_dict[cate])- 1
  24. symbol = len(img_list) / / 16
  25. if train:
  26. # 随机进行裁剪
  27. crop_x = random.randint( 0, 15)
  28. crop_y = random.randint( 0, 58)
  29. # 随机进行翻转
  30. is_flip = random.randint( 0, 1)
  31. # 以 16 帧为单位
  32. for j in range( 16):
  33. img = img_list[symbol + j]
  34. image = cv 2.imread( path + '/' + img)
  35. image = cv 2.cvtColor(image, cv 2.COLOR_BGR 2RGB)
  36. image = cv 2.resize(image, ( 171, 128))
  37. if is_flip = = 1:
  38. image = cv 2.flip(image, 1)
  39. batch[i][j][:][:][:] = image[crop_x:crop_x + 112, crop_y:crop_y + 112, :]
  40. symbol- = 1
  41. if symbol < 0:
  42. break
  43. labels[i] = label
  44. else:
  45. for j in range( 16):
  46. img = img_list[symbol + j]
  47. image = cv 2.imread( path + '/' + img)
  48. image = cv 2.cvtColor(image, cv 2.COLOR_BGR 2RGB)
  49. image = cv 2.resize(image, ( 171, 128))
  50. batch[i][j][:][:][:] = image[ 8: 120, 30: 142, :]
  51. symbol- = 1
  52. if symbol < 0:
  53. break
  54. labels[i] = label
  55. return batch, labels
  56. batch, labels = process_ data(img_path, train_list)
  57. print( "每个batch的形状为:%s"%(str(batch.shape)))
  58. print( "每个label的形状为:%s"%(str(labels.shape)))
  59. 每个batch的形状为:( 16, 16, 112, 112, 3)
  60. 每个label的形状为:( 16,)
  • 1

定义 data generator, 将数据批次传入训练函数中。


  
  
  1. def generator_train_batch(train_list, batch_ size, num_classes, img_path):
  2. while True:
  3. # 读取一个batch的数据
  4. x_train, x_labels = process_ data(img_path, train_list, batch_ size = 16,train = True)
  5. x = preprocess(x_train)
  6. # 形成 input要求的数据格式
  7. y = np_utils. to_categorical(np.array(x_labels), num_classes)
  8. x = np.transpose(x, ( 0,2,3,1,4))
  9. yield x, y
  10. def generator_val_batch( test_list, batch_ size, num_classes, img_path):
  11. while True:
  12. # 读取一个batch的数据
  13. y_ test,y_labels = process_ data(img_path, train_list, batch_ size = 16,train = False)
  14. x = preprocess(y_ test)
  15. # 形成 input要求的数据格式
  16. x = np.transpose(x,( 0,2,3,1,4))
  17. y = np_utils. to_categorical(np.array(y_labels), num_classes)
  18. yield x, y
  • 1

定义方法 preprocess, 对函数的输入数据进行图像的标准化处理。


  
  
  1. def preprocess(inputs):
  2. inputs[..., 0] - = 99.9
  3. inputs[..., 1] - = 92.1
  4. inputs[..., 2] - = 82.6
  5. inputs[..., 0] / = 65.8
  6. inputs[..., 1] / = 62.3
  7. inputs[..., 2] / = 60.3
  8. return inputs
  9. # 训练一个epoch大约需 4分钟
  10. # 类别数量
  11. num_classes = 101
  12. # batch大小
  13. batch_ size = 4
  14. # epoch数量
  15. epochs = 1
  16. # 学习率大小
  17. lr = 0.005
  18. # 优化器定义
  19. sgd = SGD(lr =lr, momentum = 0.9, nesterov = True)
  20. model.compile(loss = 'categorical_crossentropy', optimizer =sgd, metrics =[ 'accuracy'])
  21. # 开始训练
  22. history = model.fit_generator(generator_train_batch(train_list, batch_ size, num_classes,img_path),
  23. steps_per_epoch = len(train_list) / / batch_ size,
  24. epochs =epochs,
  25. callbacks =[onetenth_ 4_ 8_ 12(lr)],
  26. validation_ data =generator_val_batch( test_list, batch_ size,num_classes,img_path),
  27. validation_steps = len( test_list) / / batch_ size,
  28. verbose = 1)
  29. # 对训练结果进行保存
  30. model.save_weights(os.path.join(results_path, 'weights_c3d.h5'))
  31. WARNING:tensorflow: From /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /ops /math_ops.py: 3066: to_int 32 ( from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
  32. Instructions for updating:
  33. Use tf.cast instead.
  34. Epoch 1 / 1
  35. 20 / 20 [ = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =] - 442s 22s /step - loss: 28.7099 - acc: 0.9344 - val_loss: 27.7600 - val_acc: 1.0000
  • 1

5. 模型测试

接下来我们将训练之后得到的模型进行测试。随机在 UCF-101 中选择一个视频文件作为测试数据,然后对视频进行取帧,每 16 帧画面传入模型进行一次动作预测,并且将动作预测以及预测百分比打印在画面中并进行视频播放。

首先,引入相关的库。


  
  
  1. from IPython.display import clear_output, Image, display, HTML
  2. import time
  3. import cv2
  4. import base64
  5. import numpy as np
  • 1

构建模型结构并且加载权重。


  
  
  1. from models import c 3d_model
  2. model = c 3d_model()
  3. model.load_weights(os.path.join(results_path, 'weights_c3d.h5'), by_name = True) # 加载刚训练的模型
  • 1

定义函数 arrayshow,进行图片变量的编码格式转换。


  
  
  1. def arrayShow(img):
  2. _,ret = cv 2.imencode( '.jpg', img)
  3. return Image( data =ret)
  • 1

进行视频的预处理以及预测,将预测结果打印到画面中,最后进行播放。


  
  
  1. # 加载所有的类别和编号
  2. with open( './ucfTrainTestlist/classInd.txt', 'r') as f:
  3. class_names = f.readlines()
  4. f. close()
  5. # 读取视频文件
  6. video = './videos/v_Punch_g03_c01.avi'
  7. cap = cv 2.VideoCapture(video)
  8. clip = []
  9. # 将视频画面传入模型
  10. while True:
  11. try:
  12. clear_ output(wait = True)
  13. ret, frame = cap. read()
  14. if ret:
  15. tmp = cv 2.cvtColor(frame, cv 2.COLOR_BGR 2RGB)
  16. clip.append(cv 2.resize(tmp, ( 171, 128)))
  17. # 每 16帧进行一次预测
  18. if len(clip) = = 16:
  19. inputs = np.array(clip).astype(np.float 32)
  20. inputs = np.expand_dims(inputs, axis = 0)
  21. inputs[..., 0] - = 99.9
  22. inputs[..., 1] - = 92.1
  23. inputs[..., 2] - = 82.6
  24. inputs[..., 0] / = 65.8
  25. inputs[..., 1] / = 62.3
  26. inputs[..., 2] / = 60.3
  27. inputs = inputs[:,:, 8: 120,30: 142,:]
  28. inputs = np.transpose(inputs, ( 0, 2, 3, 1, 4))
  29. # 获得预测结果
  30. pred = model.predict(inputs)
  31. label = np.argmax(pred[ 0])
  32. # 将预测结果绘制到画面中
  33. cv 2.putText(frame, class_names[label].split( ' ')[- 1].strip(), ( 20, 20),
  34. cv 2.FONT_HERSHEY_SIMPLEX, 0.6,
  35. ( 0, 0, 255), 1)
  36. cv 2.putText(frame, "prob: %.4f" % pred[ 0][label], ( 20, 40),
  37. cv 2.FONT_HERSHEY_SIMPLEX, 0.6,
  38. ( 0, 0, 255), 1)
  39. clip.pop( 0)
  40. # 播放预测后的视频
  41. lines, columns, _ = frame.shape
  42. frame = cv 2.resize(frame, (int( columns), int( lines)))
  43. img = arrayShow(frame)
  44. display(img)
  45. time.sleep( 0.02)
  46. else:
  47. break
  48. except:
  49. print( 0)
  50. cap. release()
  • 1

6.I3D 模型

在之前我们简单介绍了 I3D 模型,I3D 官方 github 库提供了在 Kinetics 上预训练的模型和预测代码,接下来我们将体验 I3D 模型如何对视频进行预测。

首先,引入相关的包


  
  
  1. import numpy as np
  2. import tensorflow as tf
  3. import i3d
  4. WARNING: The TensorFlow contrib module will not be included in TensorFlow 2.0.
  5. For more information, please see:
  6. * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md
  7. * https://github.com/tensorflow/addons
  8. If you depend on functionality not listed there, please file an issue.
  • 1

进行参数的定义


  
  
  1. # 输入图片大小
  2. _IMAGE_ SIZE = 224
  3. # 视频的帧数
  4. _SAMPLE_VIDEO_FRAMES = 79
  5. # 输入数据包括两部分:RGB和光流
  6. # RGB和光流数据已经经过提前计算
  7. _SAMPLE_PATHS = {
  8. 'rgb': 'data/v_CricketShot_g04_c01_rgb.npy',
  9. 'flow': 'data/v_CricketShot_g04_c01_flow.npy',
  10. }
  11. # 提供了多种可以选择的预训练权重
  12. # 其中,imagenet系列模型从ImageNet的 2D权重中拓展而来,其余为视频数据下的预训练权重
  13. _CHECKPOINT_PATHS = {
  14. 'rgb': 'data/checkpoints/rgb_scratch/model.ckpt',
  15. 'flow': 'data/checkpoints/flow_scratch/model.ckpt',
  16. 'rgb_imagenet': 'data/checkpoints/rgb_imagenet/model.ckpt',
  17. 'flow_imagenet': 'data/checkpoints/flow_imagenet/model.ckpt',
  18. }
  19. # 记录类别文件
  20. _LABEL_MAP_PATH = 'data/label_map.txt'
  21. # 类别数量为 400
  22. NUM_CLASSES = 400
  • 1

定义参数:

  • imagenet_pretrained :如果为 True,则调用预训练权重,如果为 False,则调用 ImageNet 转成的权重

  
  
  1. imagenet_pretrained = True
  2. # 加载动作类型
  3. kinetics_classes = [x.strip() for x in open(_LABEL_MAP_PATH)]
  4. tf.logging. set_verbosity(tf.logging.INFO)
  • 1

构建 RGB 部分模型


  
  
  1. rgb_ input = tf.placeholder(tf.float 32, shape =( 1, _SAMPLE_VIDEO_FRAMES, _IMAGE_ SIZE, _IMAGE_ SIZE, 3))
  2. with tf.variable_scope( 'RGB', reuse =tf.AUTO_REUSE):
  3. rgb_model = i 3d.InceptionI 3d(NUM_CLASSES, spatial_squeeze = True, final_endpoint = 'Logits')
  4. rgb_logits, _ = rgb_model(rgb_ input, is_training = False, dropout_keep_prob = 1.0)
  5. rgb_variable_map = {}
  6. for variable in tf. global_variables():
  7. if variable.name.split( '/')[ 0] = = 'RGB':
  8. rgb_variable_map[variable.name. replace( ':0', '')] = variable
  9. rgb_saver = tf.train.Saver(var_list =rgb_variable_map, reshape = True)
  • 1

构建光流部分模型


  
  
  1. flow_ input = tf.placeholder(tf.float 32,shape =( 1, _SAMPLE_VIDEO_FRAMES, _IMAGE_ SIZE, _IMAGE_ SIZE, 2))
  2. with tf.variable_scope( 'Flow', reuse =tf.AUTO_REUSE):
  3. flow_model = i 3d.InceptionI 3d(NUM_CLASSES, spatial_squeeze = True, final_endpoint = 'Logits')
  4. flow_logits, _ = flow_model(flow_ input, is_training = False, dropout_keep_prob = 1.0)
  5. flow_variable_map = {}
  6. for variable in tf. global_variables():
  7. if variable.name.split( '/')[ 0] = = 'Flow':
  8. flow_variable_map[variable.name. replace( ':0', '')] = variable
  9. flow_saver = tf.train.Saver(var_list =flow_variable_map, reshape = True)
  • 1

将模型联合,成为完整的 I3D 模型


  
  
  1. model_logits = rgb_logits + flow_logits
  2. model_predictions = tf.nn.softmax(model_logits)
  • 1

开始模型预测,获得视频动作预测结果。

预测数据为开篇提供的 RGB 和光流数据:


  
  
  1. with tf.Session() as sess:
  2. feed_dict = {}
  3. if imagenet_pretrained:
  4. rgb_saver.restore(sess, _CHECKPOINT_PATHS[ 'rgb_imagenet']) # 加载rgb流的模型
  5. else:
  6. rgb_saver.restore(sess, _CHECKPOINT_PATHS[ 'rgb'])
  7. tf.logging.info( 'RGB checkpoint restored')
  8. if imagenet_pretrained:
  9. flow_saver.restore(sess, _CHECKPOINT_PATHS[ 'flow_imagenet']) # 加载flow流的模型
  10. else:
  11. flow_saver.restore(sess, _CHECKPOINT_PATHS[ 'flow'])
  12. tf.logging.info( 'Flow checkpoint restored')
  13. start_ time = time. time()
  14. rgb_sample = np.load(_SAMPLE_PATHS[ 'rgb']) # 加载rgb流的输入数据
  15. tf.logging.info( 'RGB data loaded, shape=%s', str(rgb_sample.shape))
  16. feed_dict[rgb_ input] = rgb_sample
  17. flow_sample = np.load(_SAMPLE_PATHS[ 'flow']) # 加载flow流的输入数据
  18. tf.logging.info( 'Flow data loaded, shape=%s', str(flow_sample.shape))
  19. feed_dict[flow_ input] = flow_sample
  20. out_logits, out_predictions = sess. run(
  21. [model_logits, model_predictions],
  22. feed_dict =feed_dict)
  23. out_logits = out_logits[ 0]
  24. out_predictions = out_predictions[ 0]
  25. sorted_indices = np.argsort(out_predictions)[ ::- 1]
  26. print( 'Inference time in sec: %.3f' % float( time. time() - start_ time))
  27. print( 'Norm of logits: %f' % np.linalg.norm(out_logits))
  28. print( '\nTop classes and probabilities')
  29. for index in sorted_indices[: 20]:
  30. print(out_predictions[ index], out_logits[ index], kinetics_classes[ index])
  31. WARNING:tensorflow: From /home /ma-user /anaconda 3 /envs /TensorFlow- 1.13.1 /lib /python 3.6 /site-packages /tensorflow /python /training /saver.py: 1266: checkpoint_exists ( from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version.
  32. Instructions for updating:
  33. Use standard file APIs to check for files with this prefix.
  34. INFO:tensorflow:Restoring parameters from data /checkpoints /rgb_imagenet /model.ckpt
  35. INFO:tensorflow:RGB checkpoint restored
  36. INFO:tensorflow:Restoring parameters from data /checkpoints /flow_imagenet /model.ckpt
  37. INFO:tensorflow:Flow checkpoint restored
  38. INFO:tensorflow:RGB data loaded, shape =( 1, 79, 224, 224, 3)
  39. INFO:tensorflow:Flow data loaded, shape =( 1, 79, 224, 224, 2)
  40. Inference time in sec: 1.511
  41. Norm of logits: 138.468643
  42. Top classes and probabilities
  43. 1.0 41.813675 playing cricket
  44. 1.497162e- 09 21.49398 hurling (sport)
  45. 3.8431236e- 10 20.13411 catching or throwing baseball
  46. 1.549242e- 10 19.22559 catching or throwing softball
  47. 1.1360187e- 10 18.915354 hitting baseball
  48. 8.801105e- 11 18.660116 playing tennis
  49. 2.4415466e- 11 17.37787 playing kickball
  50. 1.153184e- 11 16.627766 playing squash or racquetball
  51. 6.1318893e- 12 15.996157 shooting goal (soccer)
  52. 4.391727e- 12 15.662376 hammer throw
  53. 2.2134352e- 12 14.9772005 golf putting
  54. 1.6307096e- 12 14.67167 throwing discus
  55. 1.5456218e- 12 14.618079 javelin throw
  56. 7.6690325e- 13 13.917259 pumping fist
  57. 5.1929587e- 13 13.527372 shot put
  58. 4.2681337e- 13 13.331245 celebrating
  59. 2.7205462e- 13 12.880901 applauding
  60. 1.8357015e- 13 12.487494 throwing ball
  61. 1.6134511e- 13 12.358444 dodgeball
  62. 1.1388395e- 13 12.010078 tap dancing
  • 1

点击关注,第一时间了解华为云新鲜技术~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/280312
推荐阅读
相关标签
  

闽ICP备14008679号