web-dev-qa-db-ja.com

TensorFlow-TFRecordsファイルからビデオフレームを読み取ります

TLDR;私の質問は、TFRecordsから圧縮されたビデオフレームをロードする方法です。

大規模なビデオデータセット( Kinetics )でディープラーニングモデルをトレーニングするためのデータパイプラインを設定しています。このために、TensorFlow、より具体的には_tf.data.Dataset_およびTFRecordDataset構造を使用しています。データセットには10​​秒の約30万本の動画が含まれているため、処理するデータが大量にあります。トレーニング中に、ビデオから64の連続するフレームをランダムにサンプリングしたいので、高速のランダムサンプリングが重要です。これを実現するために、トレーニング中に可能なデータ読み込みシナリオがいくつかあります。

  1. ビデオからのサンプル。ffmpegまたはOpenCVとサンプルフレームを使用してビデオをロードします。ビデオの検索には注意が必要であり、ビデオストリームのデコードはJPGのデコードよりもはるかに遅いため、理想的ではありません。
  2. JPG画像。すべてのビデオフレームをJPGとして抽出することにより、データセットを前処理します。これにより大量のファイルが生成されますが、ランダムアクセスのために高速ではない可能性があります。
  3. データコンテナ。データセットをTFRecordsまたは_HDF5_ファイルに前処理します。パイプラインを準備するためにより多くの作業が必要ですが、これらのオプションの中で最も速い可能性があります。

オプション(3)を選択し、TFRecordファイルを使用して前処理されたバージョンのデータセットを保存することにしました。ただし、これも見た目ほど簡単ではありません。たとえば、次のようになります。

  1. 圧縮。ビデオフレームを非圧縮バイトデータとしてTFRecordsに保存するには、大量のディスク領域が必要になります。したがって、すべてのビデオフレームを抽出し、JPG圧縮を適用して、圧縮されたバイトをTFRecordsとして保存します。
  2. ビデオデータ。ビデオを扱っているため、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())
_

これは正常に機能します。データセットは、圧縮された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ファイルからビデオサンプルを読み取るのに最適な設定方法についてのヘルプをいただければ幸いです。

9
verified.human

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

(私はあなたのコードを実行できなかったので、いくつかの小さなエラーがあるかもしれませんが、うまくいけば、あなたが始めるのに十分であることに注意してください。)

6
mrry

非常によく似た依存関係を使用しているので、次のPythonパッケージを見て、正確な問題設定に対処することをお勧めします。

pip install video2tfrecord

または https://github.com/ferreirafabio/video2tfrecord を参照してください。また、tf.data.Datasetを使用するのに十分な適応性が必要です。

免責事項:私はパッケージの作成者の1人です。

4
Fábio