TLDR;私の質問は、TFRecordsから圧縮されたビデオフレームをロードする方法です。
大規模なビデオデータセット( Kinetics )でディープラーニングモデルをトレーニングするためのデータパイプラインを設定しています。このために、TensorFlow、より具体的には_tf.data.Dataset
_およびTFRecordDataset
構造を使用しています。データセットには10秒の約30万本の動画が含まれているため、処理するデータが大量にあります。トレーニング中に、ビデオから64の連続するフレームをランダムにサンプリングしたいので、高速のランダムサンプリングが重要です。これを実現するために、トレーニング中に可能なデータ読み込みシナリオがいくつかあります。
ffmpeg
またはOpenCV
とサンプルフレームを使用してビデオをロードします。ビデオの検索には注意が必要であり、ビデオストリームのデコードはJPGのデコードよりもはるかに遅いため、理想的ではありません。TFRecords
または_HDF5
_ファイルに前処理します。パイプラインを準備するためにより多くの作業が必要ですが、これらのオプションの中で最も速い可能性があります。オプション(3)を選択し、TFRecord
ファイルを使用して前処理されたバージョンのデータセットを保存することにしました。ただし、これも見た目ほど簡単ではありません。たとえば、次のようになります。
ビデオデータセットを前処理し、ビデオフレームを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())
_
これは正常に機能します。データセットは、圧縮されたJPGバイトとしてフレームを使用してTFRecordファイルとして適切に記述されています。私の質問は、トレーニング中にTFRecordファイルを読み取り、ビデオから64フレームをランダムにサンプリングし、JPG画像をデコードする方法に関するものです。
TensorFlowのドキュメント _tf.Data
_によると、次のようなことを行う必要があります。
_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ファイルからビデオサンプルを読み取るのに最適な設定方法についてのヘルプをいただければ幸いです。
tf.parse_example()
(およびtf.parse_single_example()
)の署名では、解析されたフィーチャ名のセットをグラフの作成時に固定する必要があるため、各フレームを個別のフィーチャとしてエンコードすると、フレームを動的に選択することが困難になります。 。ただし、JPEGでエンコードされた文字列のリストを含むsingle機能としてフレームをエンコードしてみることができます。
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
機能を動的にスライスできるようになります。
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
を使用するのに十分な適応性が必要です。
免責事項:私はパッケージの作成者の1人です。