TensorFlow - 从 TFRecords 文件中读取视频帧
TensorFlow - Read video frames from TFRecords file
TLDR; 我的问题是如何从 TFRecords 加载压缩视频帧。
我正在建立一个数据管道,用于在大型视频数据集 (Kinetics) 上训练深度学习模型。为此,我使用了 TensorFlow,更具体地说是 tf.data.Dataset
和 TFRecordDataset
结构。由于数据集包含约 30 万个 10 秒的视频,因此需要处理大量数据。在训练期间,我想从视频中随机抽取 64 个连续帧,因此快速随机抽样很重要。为实现这一点,在训练期间有许多可能的数据加载场景:
- 来自视频的样本。 使用
ffmpeg
或 OpenCV
和样本帧加载视频。不太理想,因为在视频中搜索很棘手,解码视频流比解码 JPG 慢得多。
- JPG 图像。 通过将所有视频帧提取为 JPG 来预处理数据集。这会生成大量文件,由于随机访问,这可能不会很快。
- 数据容器。 将数据集预处理为
TFRecords
或 HDF5
文件。需要更多工作来准备管道,但最有可能是这些选项中最快的。
我决定采用选项 (3) 并使用 TFRecord
文件来存储数据集的预处理版本。然而,这也并不像看起来那么简单,例如:
- 压缩。将视频帧作为未压缩的字节数据存储在 TFRecords 中需要大量磁盘space。因此,我提取所有视频帧,应用 JPG 压缩并将压缩字节存储为 TFRecords。
- 视频数据。我们正在处理视频,因此 TFRecords 文件中的每个示例都非常大并且包含多个视频帧(对于 10 秒的视频通常为 250-300 ,取决于帧速率)。
我编写了以下代码来预处理视频数据集并将视频帧写入 TFRecord 文件(每个文件大小约为 5GB):
def _int64_feature(value):
"""Wrapper for inserting int64 features into Example proto."""
if not isinstance(value, list):
value = [value]
return tf.train.Feature(int64_list=tf.train.Int64List(value=value))
def _bytes_feature(value):
"""Wrapper for inserting bytes features into Example proto."""
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
with tf.python_io.TFRecordWriter(output_file) as writer:
# Read and resize all video frames, np.uint8 of size [N,H,W,3]
frames = ...
features = {}
features['num_frames'] = _int64_feature(frames.shape[0])
features['height'] = _int64_feature(frames.shape[1])
features['width'] = _int64_feature(frames.shape[2])
features['channels'] = _int64_feature(frames.shape[3])
features['class_label'] = _int64_feature(example['class_id'])
features['class_text'] = _bytes_feature(tf.compat.as_bytes(example['class_label']))
features['filename'] = _bytes_feature(tf.compat.as_bytes(example['video_id']))
# Compress the frames using JPG and store in as bytes in:
# 'frames/000001', 'frames/000002', ...
for i in range(len(frames)):
ret, buffer = cv2.imencode(".jpg", frames[i])
features["frames/{:04d}".format(i)] = _bytes_feature(tf.compat.as_bytes(buffer.tobytes()))
tfrecord_example = tf.train.Example(features=tf.train.Features(feature=features))
writer.write(tfrecord_example.SerializeToString())
这很好用;数据集很好地写为 TFRecord 文件,帧为压缩的 JPG 字节。我的问题是,如何在训练期间读取 TFRecord 文件,从视频中随机采样 64 帧并解码 JPG 图像。
根据 tf.Data
上的 TensorFlow's documentation,我们需要做如下事情:
filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})
有很多关于如何使用图像执行此操作的示例,而且非常简单。但是,对于视频和帧的随机采样,我被卡住了。 tf.train.Features
对象将帧存储为 frame/00001
、frame/000002
等。我的第一个问题是如何在 dataset.map()
函数中随机抽取一组连续的帧?考虑因素是由于 JPG 压缩,每个帧的字节数可变,需要使用 tf.image.decode_jpeg
.
进行解码
任何有关如何最好地设置从 TFRecord 文件读取视频样本的帮助,我们将不胜感激!
将每个帧编码为单独的特征使得动态 select 帧变得困难,因为 tf.parse_example()
(和 tf.parse_single_example()
)的签名要求解析的特征名称集是固定在图形构建时间。但是,您可以尝试将帧编码为包含 JPEG 编码字符串列表的 单个 特征:
def _bytes_list_feature(values):
"""Wrapper for inserting bytes features into Example proto."""
return tf.train.Feature(bytes_list=tf.train.BytesList(value=values))
with tf.python_io.TFRecordWriter(output_file) as writer:
# Read and resize all video frames, np.uint8 of size [N,H,W,3]
frames = ...
features = {}
features['num_frames'] = _int64_feature(frames.shape[0])
features['height'] = _int64_feature(frames.shape[1])
features['width'] = _int64_feature(frames.shape[2])
features['channels'] = _int64_feature(frames.shape[3])
features['class_label'] = _int64_feature(example['class_id'])
features['class_text'] = _bytes_feature(tf.compat.as_bytes(example['class_label']))
features['filename'] = _bytes_feature(tf.compat.as_bytes(example['video_id']))
# Compress the frames using JPG and store in as a list of strings in 'frames'
encoded_frames = [tf.compat.as_bytes(cv2.imencode(".jpg", frame)[1].tobytes())
for frame in frames]
features['frames'] = _bytes_list_feature(encoded_frames)
tfrecord_example = tf.train.Example(features=tf.train.Features(feature=features))
writer.write(tfrecord_example.SerializeToString())
完成此操作后,就可以动态切片 frames
特征,使用 your parsing code 的修改版本:
def decode(serialized_example, sess):
# Prepare feature list; read encoded JPG images as bytes
features = dict()
features["class_label"] = tf.FixedLenFeature((), tf.int64)
features["frames"] = tf.VarLenFeature(tf.string)
features["num_frames"] = tf.FixedLenFeature((), tf.int64)
# Parse into tensors
parsed_features = tf.parse_single_example(serialized_example, features)
# Randomly sample offset from the valid range.
random_offset = tf.random_uniform(
shape=(), minval=0,
maxval=parsed_features["num_frames"] - SEQ_NUM_FRAMES, dtype=tf.int64)
offsets = tf.range(random_offset, random_offset + SEQ_NUM_FRAMES)
# Decode the encoded JPG images
images = tf.map_fn(lambda i: tf.image.decode_jpeg(parsed_features["frames"].values[i]),
offsets)
label = tf.cast(parsed_features["class_label"], tf.int64)
return images, label
(请注意,我无法 运行 您的代码,因此可能存在一些小错误,但希望这足以让您入门。)
由于您使用的依赖项非常相似,我建议您查看以下 Python 包,因为它可以解决您的确切问题设置:
pip install video2tfrecord
或参考https://github.com/ferreirafabio/video2tfrecord。
它还应该具有足够的适应性以使用 tf.data.Dataset
.
免责声明:我是该软件包的作者之一。
TLDR; 我的问题是如何从 TFRecords 加载压缩视频帧。
我正在建立一个数据管道,用于在大型视频数据集 (Kinetics) 上训练深度学习模型。为此,我使用了 TensorFlow,更具体地说是 tf.data.Dataset
和 TFRecordDataset
结构。由于数据集包含约 30 万个 10 秒的视频,因此需要处理大量数据。在训练期间,我想从视频中随机抽取 64 个连续帧,因此快速随机抽样很重要。为实现这一点,在训练期间有许多可能的数据加载场景:
- 来自视频的样本。 使用
ffmpeg
或OpenCV
和样本帧加载视频。不太理想,因为在视频中搜索很棘手,解码视频流比解码 JPG 慢得多。 - JPG 图像。 通过将所有视频帧提取为 JPG 来预处理数据集。这会生成大量文件,由于随机访问,这可能不会很快。
- 数据容器。 将数据集预处理为
TFRecords
或HDF5
文件。需要更多工作来准备管道,但最有可能是这些选项中最快的。
我决定采用选项 (3) 并使用 TFRecord
文件来存储数据集的预处理版本。然而,这也并不像看起来那么简单,例如:
- 压缩。将视频帧作为未压缩的字节数据存储在 TFRecords 中需要大量磁盘space。因此,我提取所有视频帧,应用 JPG 压缩并将压缩字节存储为 TFRecords。
- 视频数据。我们正在处理视频,因此 TFRecords 文件中的每个示例都非常大并且包含多个视频帧(对于 10 秒的视频通常为 250-300 ,取决于帧速率)。
我编写了以下代码来预处理视频数据集并将视频帧写入 TFRecord 文件(每个文件大小约为 5GB):
def _int64_feature(value):
"""Wrapper for inserting int64 features into Example proto."""
if not isinstance(value, list):
value = [value]
return tf.train.Feature(int64_list=tf.train.Int64List(value=value))
def _bytes_feature(value):
"""Wrapper for inserting bytes features into Example proto."""
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))
with tf.python_io.TFRecordWriter(output_file) as writer:
# Read and resize all video frames, np.uint8 of size [N,H,W,3]
frames = ...
features = {}
features['num_frames'] = _int64_feature(frames.shape[0])
features['height'] = _int64_feature(frames.shape[1])
features['width'] = _int64_feature(frames.shape[2])
features['channels'] = _int64_feature(frames.shape[3])
features['class_label'] = _int64_feature(example['class_id'])
features['class_text'] = _bytes_feature(tf.compat.as_bytes(example['class_label']))
features['filename'] = _bytes_feature(tf.compat.as_bytes(example['video_id']))
# Compress the frames using JPG and store in as bytes in:
# 'frames/000001', 'frames/000002', ...
for i in range(len(frames)):
ret, buffer = cv2.imencode(".jpg", frames[i])
features["frames/{:04d}".format(i)] = _bytes_feature(tf.compat.as_bytes(buffer.tobytes()))
tfrecord_example = tf.train.Example(features=tf.train.Features(feature=features))
writer.write(tfrecord_example.SerializeToString())
这很好用;数据集很好地写为 TFRecord 文件,帧为压缩的 JPG 字节。我的问题是,如何在训练期间读取 TFRecord 文件,从视频中随机采样 64 帧并解码 JPG 图像。
根据 tf.Data
上的 TensorFlow's documentation,我们需要做如下事情:
filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})
有很多关于如何使用图像执行此操作的示例,而且非常简单。但是,对于视频和帧的随机采样,我被卡住了。 tf.train.Features
对象将帧存储为 frame/00001
、frame/000002
等。我的第一个问题是如何在 dataset.map()
函数中随机抽取一组连续的帧?考虑因素是由于 JPG 压缩,每个帧的字节数可变,需要使用 tf.image.decode_jpeg
.
任何有关如何最好地设置从 TFRecord 文件读取视频样本的帮助,我们将不胜感激!
将每个帧编码为单独的特征使得动态 select 帧变得困难,因为 tf.parse_example()
(和 tf.parse_single_example()
)的签名要求解析的特征名称集是固定在图形构建时间。但是,您可以尝试将帧编码为包含 JPEG 编码字符串列表的 单个 特征:
def _bytes_list_feature(values):
"""Wrapper for inserting bytes features into Example proto."""
return tf.train.Feature(bytes_list=tf.train.BytesList(value=values))
with tf.python_io.TFRecordWriter(output_file) as writer:
# Read and resize all video frames, np.uint8 of size [N,H,W,3]
frames = ...
features = {}
features['num_frames'] = _int64_feature(frames.shape[0])
features['height'] = _int64_feature(frames.shape[1])
features['width'] = _int64_feature(frames.shape[2])
features['channels'] = _int64_feature(frames.shape[3])
features['class_label'] = _int64_feature(example['class_id'])
features['class_text'] = _bytes_feature(tf.compat.as_bytes(example['class_label']))
features['filename'] = _bytes_feature(tf.compat.as_bytes(example['video_id']))
# Compress the frames using JPG and store in as a list of strings in 'frames'
encoded_frames = [tf.compat.as_bytes(cv2.imencode(".jpg", frame)[1].tobytes())
for frame in frames]
features['frames'] = _bytes_list_feature(encoded_frames)
tfrecord_example = tf.train.Example(features=tf.train.Features(feature=features))
writer.write(tfrecord_example.SerializeToString())
完成此操作后,就可以动态切片 frames
特征,使用 your parsing code 的修改版本:
def decode(serialized_example, sess):
# Prepare feature list; read encoded JPG images as bytes
features = dict()
features["class_label"] = tf.FixedLenFeature((), tf.int64)
features["frames"] = tf.VarLenFeature(tf.string)
features["num_frames"] = tf.FixedLenFeature((), tf.int64)
# Parse into tensors
parsed_features = tf.parse_single_example(serialized_example, features)
# Randomly sample offset from the valid range.
random_offset = tf.random_uniform(
shape=(), minval=0,
maxval=parsed_features["num_frames"] - SEQ_NUM_FRAMES, dtype=tf.int64)
offsets = tf.range(random_offset, random_offset + SEQ_NUM_FRAMES)
# Decode the encoded JPG images
images = tf.map_fn(lambda i: tf.image.decode_jpeg(parsed_features["frames"].values[i]),
offsets)
label = tf.cast(parsed_features["class_label"], tf.int64)
return images, label
(请注意,我无法 运行 您的代码,因此可能存在一些小错误,但希望这足以让您入门。)
由于您使用的依赖项非常相似,我建议您查看以下 Python 包,因为它可以解决您的确切问题设置:
pip install video2tfrecord
或参考https://github.com/ferreirafabio/video2tfrecord。
它还应该具有足够的适应性以使用 tf.data.Dataset
.
免责声明:我是该软件包的作者之一。