動画フレームのラベル生成とデータセット構築

UCF101の10クラスを例に、動画フレームからアノテーションファイルを生成する手順を説明します。

1. フレーム抽出

フレーム抽出には主に2つのディレクトリ構成があります。

方法1:クラスごとに整理された構造(rawframes)

rawframes/
├── ApplyEyemakeup/
│   ├── v_ApplyEyemakeup_g01_c01/
│   │   ├── img_0001.jpg
│   │   └── ...
│   └── v_ApplyEyemakeup_g01_c02/
└── Basketball/
    └── ...

この形式は mmaction2/tools/data/build_rawframes.py を使用して生成できます:

python build_rawframes.py ucf101/videos ucf101/rawframes \
  --task rgb --num-gpu 1 --level 2 --ext avi \
  --use-opencv --new-width 320 --new-height 240

ただし、このスクリプトは img_00001.jpg のようなゼロ埋め形式で出力するため、後述の処理のために 1.jpg のようなシンプルな連番形式に変更する必要があります。

方法2:フラットな構造(frames)

frames/
├── v_ApplyEyemakeup_g01_c01/
│   ├── img_00001.jpg
│   └── ...
├── v_BenchPress_g25_c07/
└── ...

この形式は外部ツール(例:vidtools)で実現可能です:

git clone https://github.com/liu-zhy/vidtools.git
cd vidtools
python extract_frames.py ../ucf101/ -o ../datasets/frames/ -j 16 --out_ext png

同様に、ファイル名を 1.jpg 形式に修正する必要があります。

2. クラスごとの再整理(three_f.py)

フレーム抽出後に、動画ファイル名(例:v_ApplyEyemakeup_g01_c01)からクラス名(ApplyEyemakeup)を抽出し、対応するクラスフォルダ下に移動します。


import os
import shutil

source = 'datasets/frames'
folders = [d for d in os.listdir(source) if os.path.isdir(os.path.join(source, d))]

mapping = {}
for name in folders:
    if '_' in name:
        cls = name.split('_')[1]
        mapping.setdefault(cls, []).append(name)

for cls, items in mapping.items():
    cls_dir = os.path.join(source, cls)
    os.makedirs(cls_dir, exist_ok=True)
    for item in items:
        src = os.path.join(source, item)
        dst = os.path.join(cls_dir, item)
        shutil.move(src, dst)

3. アノテーションファイル生成(four_f.py)

各フレームに対し、パス・フレーム番号・クラスIDを含むリストを生成します。ここでは UCF101 の classInd.txt を読み込み、クラス名とIDのマッピングを作成します。


import os

frames_root = 'datasets/frames'
label_map_path = 'ucfTrainTestlist/classInd.txt'

# クラスIDマッピング作成
cls_to_id = {}
with open(label_map_path) as f:
    for line in f:
        idx, name = line.strip().split()
        cls_to_id[name] = int(idx)

# 出力ファイル準備
train_f = open('train.txt', 'w')
val_f = open('val.txt', 'w')
test_f = open('test.txt', 'w')

for cls_name, cls_id in cls_to_id.items():
    cls_path = os.path.join(frames_root, cls_name)
    if not os.path.isdir(cls_path):
        continue
    videos = os.listdir(cls_path)
    for vid in videos:
        vid_path = os.path.join(cls_path, vid)
        frames = sorted([f for f in os.listdir(vid_path) if f.endswith('.jpg')])
        total = len(frames)
        n_train = int(0.6 * total)
        n_val = int(0.2 * total)

        for i, frame in enumerate(frames, 1):
            num = int(frame.split('.')[0])  # 1.jpg → 1
            rel_path = os.path.join(cls_name, vid, frame).replace('\\', '/')
            entry = f"{rel_path} {num} {cls_id}\n"

            if i <= n_train:
                train_f.write(entry)
            elif i <= n_train + n_val:
                val_f.write(entry)
            else:
                test_f.write(entry)

train_f.close()
val_f.close()
test_f.close()

4. パス整形(five_f.py)

上記で生成されたパス(例:/ApplyEyemakeup/v_ApplyEyemakeup_g01_c01/56.jpg)を、ApplyEyemakeup/v_ApplyEyemakeup_g01_c01 56 1 の形式に変換します。


with open('val.txt') as fin, open('val_final.txt', 'w') as fout:
    for line in fin:
        parts = line.strip().split()
        img_path = parts[0]
        frame_num = parts[1]
        cls_id = parts[2]

        # パスから先頭のスラッシュを除去し、最後のファイル名を削除
        tokens = img_path.split('/')
        new_path = '/'.join(tokens[1:3])  # クラス名/ビデオID

        fout.write(f"{new_path} {frame_num} {cls_id}\n")

5. 実践例:UCF5サブセットでの処理フロー

実際のワークフローとして、以下のように実行します:

  1. build_rawframes.py を修正し、フレームを 1.jpg 形式で出力。
  2. 抽出されたフレームを three_f.py でクラス別に再配置。
  3. 動画単位で6:2:2に分割(訓練/検証/テスト)。
  4. 各フレームのメタ情報を収集し、four_f.py で一時的なアノテーション生成。
  5. five_f.py で最終的な形式に整形。

これにより、MMAction2などの行動認識フレームワークで直接利用可能なアノテーションファイルが得られます。

タグ: Python MMAction2 UCF101 動画処理 フレーム抽出

7月25日 02:35 投稿