文章目录
0. 前言
- 要基于AVA数据集做一些工作,网上的文章都不能满足我的要求,所以自己写一篇记录一下。
- 参考资料:
- 感想:
- 行为识别数据集下载真的困难,国内搞过的都懂……
- 作为组里第一个吃螃蟹的,到现在还在跟 Kinetics-700、ActivityNet 等数据集死磕,辛酸泪。
- AVA 已经是最容易的了,毕竟可以用迅雷下载。
- 本文主要内容:
- 第一章:介绍AVA,如何获取、标注过程、annotations解析。
- 第二章:SlowFast中解析AVA的过程。
- 第三章:mmaction中解析AVA的过程。(尚未完成)
1. AVA简介
1.1. 基本情况
- 数据集类别:Spatio-Temporal Action Detection,即时空行为检测。
- 举个例子,就是检测出视频中所有人的位置以及对应的行为类别。
- 数据集形式(这里是简单介绍,后面会有更详细的说明):
- 要标记的内容包括人物bbox,以及每个人的行为类别,同一时间同一人可能有多个行为。
- 标记的内容还有还有每个实体编号,即相邻关键帧中的人物如果是同一个人,则拥有相同的实体编号。换句话说,“实体编号”其实就是目标跟踪的标签。
- 并不是对视频中的每一帧进行标记,而只是对关键帧进行标记。
- 所谓关键帧,按我的理解就是每秒取1帧作为关键帧,对该帧进行标记。
- 要标记的内容包括人物bbox,以及每个人的行为类别,同一时间同一人可能有多个行为。
- 行为类别:标签一共有80类,(但验证时只用到其中的60类)。80类标签分为三类(person movement, object manipulation, person interaction),具体信息如下。
- person movement
- bend/bow (at the waist)
- crawl
- crouch/kneel
- dance
- fall down
- get up
- jump/leap
- lie/sleep
- martial art
- run/jog
- sit
- stand
- swim
- walk
- object manipulation
- answer phone
- brush teeth
- carry/hold (an object)
- catch (an object)
- chop
- climb (e.g., a mountain)
- clink glass
- close (e.g., a door, a box)
- cook
- cut
- dig
- dress/put on clothing
- drink
- drive (e.g., a car, a truck)
- eat
- enter
- exit
- extract
- fishing
- hit (an object)
- kick (an object)
- lift/pick up
- listen (e.g., to music)
- open (e.g., a window, a car door)
- paint
- play board game
- play musical instrument
- play with pets
- point to (an object)
- press
- pull (an object)
- push (an object)
- put down
- read
- ride (e.g., a bike, a car, a horse)
- row boat
- sail boat
- shoot
- shovel
- smoke
- stir
- take a photo
- text on/look at a cellphone
- throw
- touch (an object)
- turn (e.g., a screwdriver)
- watch (e.g., TV)
- work on a computer
- write
- person interaction
- fight/hit (a person)
- give/serve (an object) to (a person)
- grab (a person)
- hand clap
- hand shake
- hand wave
- hug (a person)
- kick (a person)
- kiss (a person)
- lift (a person)
- listen to (a person)
- play with kids
- push (another person)
- sing to (e.g., self, a person, a group)
- take (an object) from (a person)
- talk to (e.g., self, a person, a group)
- watch (a person)
- person movement
1.2. 如何获取
- 两种获取方式
- SlowFast/MMAction 中都提供了 AVA 数据预处理相关脚本,具体在后续章节会介绍,可以参考。
1.3. 数据集构建过程
- 这部分内容主要参考了 数据集论文
- 第一步:Action vocabulary generation
- 任务:确定要标注的行为类别以及继承关系。
- 选择/设置“行为类别”时有三个准则:
- generality,即通用性,而不是特定环境下的特定动作(如“在篮球场打篮球”)。
- atomicity,即原子性。每个动作都有其特点,且与交互的物体无关(如行为 hold 且不要指定hold的物体)。
- exhaustivity,即类别尽可能丰富。
- 第二步:Movie and segment selection
- 任务:构建原始视频数据集,为后续标注工作做准备。
- AVA的数据源来自电影片段。
- 每个电影只标注第15-30分钟内的视频
- 每个长度为15分钟的视频都转换为897个长度为3s的视频片段(clip)。
- 15分钟共900秒,窗口长度为3秒,stride为1秒,滑动897次得到897个clip。
- 每个clip对对应一个keyframe(关键帧),关键帧是1.5秒位置。
- 搞了一个各个国家顶尖演员列表,然后在Youtube中对每个演员进行搜索,寻找符合条件的电影。
- 条件包括:有
file/television
标签,时长超过30分钟,发布时间超过1年,观看人数超过1000,且不包含黑白、低清晰度、卡通等类别的电影。
- 条件包括:有
- 第三步:Person bounding box annotation
- 任务:针对每个keyframe标注人物bbox。
- 使用了混合标注法:
- 先用Faster-RCNN标注,(说是
set operating point
从而保证高精度,不知道啥意思,猜测就是提高了阈值吧)。 - 之后在人工标注遗漏的bbox。
- 先用Faster-RCNN标注,(说是
- bbox对最终结果影响很大,所以这一步会比较注意。
- 第四步:Person link annotation
- 任务:对相邻keyframe中的任务bbox进行关联。
- 方法:
- 先机器标注一波:通过计算相邻两帧不同bbox之间的相似度,然后根据匈牙利算法进行匹配。
- 再手动处理一波:手工删除FP样本。
- 第五步:Action annotation
- 任务:标注行为类别。
- 通过众包实现。不可避免的,标注人会少标行为(因为行为太多了)。
- 参考界面如下:
1.4. annotations 解析
- 以 v2.2 为例,解压
ava_v2.1.zip
得到的结果如下。- V2.1 和 V2.2 的区别:
- 标签内容没细看,可能v2.2细化了吧。
- 但视频源没有任何变化,即V2.1与V2.2的 train/val/test 的视频是完全相同的。
- V2.1 和 V2.2 的区别:
- 行为类别文件:
ava_action_list_v2.1_for_activitynet_2018.pbtxt
:60类行为,Evaluate时使用ava_action_list_v2.1.pbtxt
:80类行为
- 行为标签文件:
ava_train_v2.1.csv
、ava_val_v2.1.csv
、ava_test_v2.1.txt
- 其中,train/val有标签,test只是视频名称列表。
- train/val 每行代表一个样本,共有5个部分
video_id
:视频名称,不包括文件后缀,即Youtube对应urlmiddle_Frame_timestamp
:关键帧所在位置(第几秒)person_box
:包括了四列,(x1, y1, x2, y2)
,分别代表左上、右下点的位置。action_id
:即ava_action_list_v2.1.pbtxt
中对应的id。person_id
:bbox中人物的编号,即 person link 时产生的标签,每个人的id不同。
ava_included_timestamps_v2.2.txt
:每个视频要检测的位置,即第902到1798秒。- 不需要进行检测的timestamp
ava_train_excluded_timestamps_v2.1.csv
ava_val_excluded_timestamps_v2.1.csv
ava_test_excluded_timestamps_v2.1.csv
- 即 train/val/test 数据集中每个视频不需要进行检测的timestamp。
2. SlowFast
- 相关源码:
- 数据集解析源码主要模块
- 构建
Ava
对象,解析标签文件,设置数据预处理参数,以 clip 为单位保存相关信息。 - 读取某个 clip 的相关信息。
- 构建
- 流水账,没兴趣的跳过。
2.1. 构建 Ava
对象
- 第一步:为每个视频进行编号,并保存对应的帧绝对路径的列表。
- 从代码角度看保存了两个列表
_video_idx_to_name
- 每个视频原来有个
video_name
,即youtube中对应url后缀,如1j20qq1JyX4
。 - 在代码中,
video_name
用起来不方便,所以对视频进行编号,即每个video_name
对应一个video_id
(从0开始编号)。 - 本对象是 list,index就是
video_id
,value就是video_name
。
- 每个视频原来有个
_image_paths
- 保存每个视频对应帧的绝对路径。
- 本对象是list,index是
video_id
,value是一个list(中每个元素是帧绝对路径,注意,这个list中帧文件的顺序必须是从小到大,不然后面代码有问题)。
- 源码细节:主要输入数据就是
frame_lists
文件- 该文件不是AVA官方提供的,而是FAIR提供的,可以自己生成。
- 该文件中保存有
video_name
以及对应所有帧的相对路径,且帧文件的顺序就是从小到大。
- 主要就是
ava_helper.load_image_lists
实现。
- 从代码角度看保存了两个列表
- 第二步:解析行为标签文件。
- 从代码角度看,就是构建了一个list
boxes_and_labels
- 该数据类型是:
boxes_and_labels[video_id][frame_sec_int] = list([box_i, box_i_labels])
boxes_and_labels[video_id].keys()
就是所有可用的时间点,即range(902, 1799)
len(boxes_and_labels[video_id][frame_sec_int])
就是这个时间点 box 的数量。
boxes_and_labels
整体是一个列表,index是video_id
,value是一个字典。- 该字典的 key 是
frame_sec_int
,即[902, 1798]
,表示视频中的第几帧。 - 该字典的 value 是列表,取名为
value_list
。 value_list
通过列表形式保存 bbox(x1, y1, x2, y2
形式) 以及对应的- labels(同一个box可能有多个标签)两部分信息。
- 其中,
box_i
的形式是x1, y1, x2, y2
。
- 该数据类型是:
- 源码细节:
- 这一步的输入数据主要包括GT与Predict两部分。
- GT指的就是AVA官方提供的标签文件,如
ava_train_v2.1.csv
。 - Predict指的是验证/测试时用的数据,只包括每个视频每一帧的人物bbox以及对应的score,不包括行为类别。包括
video_name, frame_sec, bbox_x1, bbox_y1, bbox_x2, bbox_y2, category, score
。在使用Predict数据时会根据 score 筛选一部分数据。
- 主要通过
ava_helper.load_boxes_and_labels
实现。
- 从代码角度看,就是构建了一个list
- 第三步:构建关键帧数据。
- 从源码上看,就是构建了
_keyframe_indices
和_keyframe_boxes_and_labels
两个列表。- 这两个对象是配合使用的。两个列表中相同index的元素就是后续构建 clip 样本的输入数据。
- 保存了所有可用关键帧相关信息。
_keyframe_indices
- 是个list对象,index的作用就是与
_keyframe_boxes_and_labels
对应。 - 主要保存四个数据
video_idx, sec_idx, sec, frame_idx
sec_idx
指的是当前关键帧在这个视频中所有关键帧的idx,从0开始取值。sec
指的是当前关键帧在这个视频中的位置,从902开始取值。frame_idx
指的是当前关键帧的具体编号,计算方法(sec-900)*FPS
,其实就是每个clip的中心frame编号。
- 是个list对象,index的作用就是与
_keyframe_boxes_and_labels
- 是个list对象,index的作用就是与
_keyframe_indices
对应。 - 其实就是将上一步中的
boxes_and_labels[video_id][frame_sec_int]
直接保存下来。
- 是个list对象,index的作用就是与
- 主要通过
ava_helper.get_keyframe_data
实现。
- 从源码上看,就是构建了
2.3. 读取某个keyframe信息
- 这里,每个keyframe的信息就是对应一个clip数据,主要就是通过
__getitem__
实现。- 输入的idx其实就是
2.2.
中第三步_keyframe_indices
中的下标。
- 输入的idx其实就是
- 第一步:获取
_keyframe_indices
对应 idx 下标信息。video_idx, sec_idx, sec, center_idx
,sec_idx 从0开始取值,sec从902开始取值。
- 第二步:根据输入数据,进行数据采样。
- 以
center_idx
为中心,根据输入数据_seq_len
和_sample_rate
进行采样。 - 采样细节:
- 从
center_idx - _seq_len // 2
开始,在[center_idx - _seq_len // 2, center_idx + _seq_len // 2)
范围内,根据_sample_rate
进行采样。 _seq_len
的取值其实是_sample_rate * _sample_frames_length
得到的。- 刚开始在想,为什么这个采样刚好就能用。列了不等式算了算,刚刚好,这个样子采样其实刚好能得到
_sample_frames_length
个帧下标。
- 从
- 以
- 第三步:获取
_keyframe_boxes_and_labels
获取该关键帧的所有boxes与对应label信息。 - 第四步:根据采样结果以及
_image_paths
读取帧文件绝对路径,并读取对应图片。_image_paths[video_idx][frame] for frame in seq
,其中seq
就是上面采样得到的结果。
- 第五步:图片数据预处理。
- 有pytorch与cv2两种模式,预处理的过程是一样的,只是调用的库不同。
- 得到的结果都是
C, T, H, W
结构。 - 预处理过程包括:
- 数据类型/范围转换:
[0, 255] -> [0, 1]
- resize/crop操作:训练集 随机短边resize -> random crop -> random flip;验证集 短边resize -> center crop;测试集 短边resize。
- 随机短边resize通过
transform.random_short_side_scale_jitter
实现,根据输入参数TRAIN_JITTER_SCALES
指定短边范围,随机获取其中数值作为短边的size,然后进行resize。
- 随机短边resize通过
- 随机色彩变换,主要通过
transform.color_jitter
与transform.lighting_jitter
实现。 - 图像标准化,减去平均数除以标准差。
- 数据类型/范围转换:
- 第六步:构建行为识别 one-hot 形式label。
- label的shape为
[num_boxes, num_classes]
。 - 注意,每行可能不止一个类别为1。
- label的shape为
- 第七步:分别为不同分支构建输入数据。
- 对于I3D模型,这一步其实也没做什么。
- 对于SlowFast模型,这一步会分别对 Slow 分支与 Fast 分支构建对应的输入图片。
- Fast分支就是之前输入的。
- Slow分支就是在T纬度上进行sample rate为
SLOWFAST.ALPHA
的采样。
- 第八步:构建输出数据。
- 输出数据包括四部分
imgs, labels, idx, extra_data
。 imgs
是第七步图像预处理的结果,是个list,分别对应每个分支的结果,每个分支的shape为[C, T, H, W]
labels
就是第六步的结果,shape为[num_boxes, num_classes]
idx
即__getitem__
的输入数据。extra_data
是个字典,包括三个数据:boxes
经过数据预处理后的bbox。ori_boxes
原始 boxes,即在resize/crop等操作前的bbox。metadata
元数据列表,列表长度与boxes
相同,每个元素都是[video_id, sec]
。sec从902开始取值。
- 输出数据包括四部分
3. MMAction
-
相关源码:
-
等待补充