fix: v3 の動画位置を data レイアウトではなく meta/episodes から解決する - #286
Conversation
_build_episode_map は各エピソードの動画 chunk/file を data parquet の レイアウトから導出していたが、v3 では data と動画が独立に集約されるため、 存在しない mp4 を参照(黙ってスキップ)するか、同名ファイルが偶然存在する 場合は data ファイルの行オフセットを動画フレームオフセットとして使い 誤った区間を切り出していた。 - episode map を meta/episodes/chunk-*/file-*.parquet から構築し、 カメラごとの動画位置(chunk/file index, from/to_timestamp)を保持 - 動画フレームオフセットを from_timestamp × fps(meta/info.json)で算出 - 動画ファイル欠落時は黙って落とさず warning を出してスキップ。 切り出しが必要なのに fps が無い場合は例外 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| "chunk": _chunk_name(int(row[f"videos/{key}/chunk_index"])), | ||
| "file_stem": _file_stem(int(row[f"videos/{key}/file_index"])), | ||
| "from_timestamp": float(row[f"videos/{key}/from_timestamp"]), | ||
| "to_timestamp": float(row[f"videos/{key}/to_timestamp"]), | ||
| } | ||
| frame_offset += length | ||
| episode_map[int(row["episode_index"])] = { | ||
| "data_chunk": _chunk_name(int(row["data/chunk_index"])), | ||
| "data_file_stem": _file_stem(int(row["data/file_index"])), | ||
| "length": int(row["length"]), | ||
| "videos": videos, |
There was a problem hiding this comment.
video のチャンク番号と、episode のチャンク番号で同じものを使っていたことが根本原因でした。
| logger.warning( | ||
| "Video file not found, skipping camera %s: %s", | ||
| camera.key, | ||
| video_path, | ||
| ) |
There was a problem hiding this comment.
いままで暗黙に continue してしまっていたので、warning を表示するように修正しました。
カメラループ内でのチェックだと、ステージング作成や先行カメラの 切り出し後に失敗しうるため、選択カメラに動画セグメントがあるのに fps が無い場合は処理開始前に例外を出す。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes LeRobot v3 episode video extraction by resolving per-episode/per-camera video locations from meta/episodes (the authoritative source in v3) instead of inferring them from the data/ parquet layout, which can diverge after independent consolidation.
Changes:
- Reworked
_build_episode_map(v3) to readmeta/episodes/chunk-*/file-*.parquetand include per-camera video segment metadata. - Updated episode ZIP assembly to resolve video paths per camera and convert
from_timestampto a frame offset usingfpsfrommeta/info.json, with warnings on missing video files. - Added/updated tests to cover meta/episodes-driven mapping, timestamp offset extraction, missing video warnings, and missing-fps errors.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
fastlabel/lerobot/v3.py |
Switches episode mapping to meta/episodes, redesigns episode/video info, and updates ZIP assembly to use per-camera video metadata + fps. |
fastlabel/lerobot/__init__.py |
Adapts public helpers to the new episode-map shape and passes fps into ZIP assembly. |
tests/test_lerobot_v3_parquet.py |
Updates episode-map expectations and adds coverage for per-camera video metadata + missing episode metadata. |
tests/test_lerobot_v3_video.py |
Adds ZIP-assembly tests validating per-camera location resolution, timestamp offset handling, warnings on missing videos, and missing-fps failures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
yo-tak
left a comment
There was a problem hiding this comment.
一行一行細かくは把握できていないですが、大きな方針は理解しました。LGTM 🚀
問題
import_lerobot(v3)で、_build_episode_mapが data parquet のレイアウトから各エピソードの動画 chunk/file を導出していました。しかし LeRobot v3 では data と動画ファイルは独立に集約(consolidate)されるため chunk/file 番号が一致する保証がなく、video_path.exists()チェックで黙ってスキップされ、ZIP から動画が欠落という問題がありました。カメラごとに動画位置が異なる点も考慮されていませんでした。
修正内容
_build_episode_mapをmeta/episodes/chunk-*/file-*.parquetの読み取りに変更(v3 の正式な位置情報ソース)。エピソードメタデータが無い場合はFastLabelInvalidExceptionEpisodeInfoを再設計:data_chunk/data_file_stem/length+ カメラキーごとのvideosdict(chunk/file_stem/from_timestamp/to_timestamp)_assemble_episode_zipはカメラごとに動画パスを解決し、フレームオフセットをfrom_timestamp × fps(meta/info.json)で算出logger.warningを出してスキップ(ZIP にはテレメトリ JSON と他カメラの動画は含まれる)fpsが無い場合は例外(誤区間の切り出しを防ぐため)補足
chunk/file のパス形式は LeRobot デフォルトの
chunk-{i:03d}/file-{i:03d}を前提としています(info.jsonのdata_path/video_pathテンプレートは未参照。既存のdetect_versionと同じ前提)。テスト
meta/episodesベースの episode map 構築(data と異なる chunk/file を指す動画位置の読み取り含む)_assemble_episode_zipが正しいファイル・オフセットから切り出すこと(data=file-000 / 動画=file-001、from_timestamp=0.5s → フレーム5開始で4フレーム)fps欠落時の例外🤖 Generated with Claude Code