Spaces:
Running on Zero
Running on Zero
File size: 7,080 Bytes
6e4b62e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | #!/usr/bin/env python3
"""
Preprocess Context-as-Memory dataset folders into Echo-Memory metadata CSV.
Expected dataset layout:
- frames/: frame images organized by video
- jsons/: camera pose information for each video
- overlap_labels/: FOV overlap information for memory retrieval
- captions.txt: video segment captions
"""
import argparse
import csv
import json
import os
from typing import Dict, List, Tuple
def parse_caption_line(line: str) -> Tuple[str, str]:
"""
Parse a line from captions.txt.
Format: "video_name/start_end.mp4\tcaption text..."
Returns: (video_path, caption)
"""
parts = line.strip().split("\t", 1)
if len(parts) != 2:
return None, None
video_path = parts[0]
caption = parts[1]
return video_path, caption
def load_captions(captions_file: str) -> Dict[str, str]:
"""Load captions.txt as video_name -> caption."""
captions = {}
if not os.path.exists(captions_file):
print(f"Warning: Captions file not found: {captions_file}")
return captions
with open(captions_file, "r", encoding="utf-8") as f:
for line in f:
video_path, caption = parse_caption_line(line)
if video_path and caption:
video_name = video_path.split("/")[0]
if video_name not in captions:
captions[video_name] = []
captions[video_name].append(caption)
for video_name in captions:
captions[video_name] = captions[video_name][0] if captions[video_name] else ""
return captions
def get_frame_files(frames_dir: str, video_name: str) -> List[str]:
"""Get sorted frame paths for one video, relative to frames_dir."""
video_frames_dir = os.path.join(frames_dir, video_name)
if not os.path.exists(video_frames_dir):
return []
frame_files = []
for frame_file in sorted(os.listdir(video_frames_dir)):
if frame_file.endswith(".png"):
frame_files.append(os.path.join(video_name, frame_file))
return frame_files
def load_camera_poses(json_file: str) -> Dict:
"""Load camera poses from a JSON file."""
if not os.path.exists(json_file):
return {}
with open(json_file, "r", encoding="utf-8") as f:
data = json.load(f)
if "CineCameraActor" in data:
return data["CineCameraActor"]
if isinstance(data, dict):
return data
return {}
def load_overlap_labels(overlap_dir: str, video_name: str, frame_idx: int) -> List[int]:
"""Load overlapping frame indices for a given frame."""
overlap_file = os.path.join(overlap_dir, video_name, f"{frame_idx}.json")
if not os.path.exists(overlap_file):
return []
try:
with open(overlap_file, "r", encoding="utf-8") as f:
data = json.load(f)
overlapping_frames = data.get("overlapping_frames", [])
return [int(frame) for frame in overlapping_frames if str(frame).isdigit()]
except Exception:
return []
def create_metadata_csv(
dataset_base_path: str,
output_csv: str,
segment_length: int = 81,
context_frames: int = 5,
):
"""
Create metadata CSV for the Context-as-Memory dataset.
Args:
dataset_base_path: root of the dataset.
output_csv: output CSV path.
segment_length: frames per training segment.
context_frames: context frames reserved by downstream workflows.
"""
frames_dir = os.path.join(dataset_base_path, "frames")
captions_file = os.path.join(dataset_base_path, "captions.txt")
captions = load_captions(captions_file)
if not os.path.exists(frames_dir):
print(f"Error: Frames directory not found: {frames_dir}")
return
video_names = [
d for d in os.listdir(frames_dir)
if os.path.isdir(os.path.join(frames_dir, d))
]
print(f"Found {len(video_names)} videos")
print(f"Context frames: {context_frames}")
output_dir = os.path.dirname(output_csv)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
with open(output_csv, "w", newline="", encoding="utf-8") as csvfile:
fieldnames = [
"video",
"prompt",
"video_name",
"start_frame",
"end_frame",
]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
total_segments = 0
for video_name in sorted(video_names):
print(f"Processing video: {video_name}")
frame_files = get_frame_files(frames_dir, video_name)
if len(frame_files) < segment_length:
print(
f" Skipping {video_name}: only {len(frame_files)} frames "
f"(need at least {segment_length})"
)
continue
prompt = captions.get(video_name, f"A scene from {video_name}")
step = max(1, segment_length // 2)
video_segments = 0
for start_idx in range(0, len(frame_files) - segment_length + 1, step):
end_idx = start_idx + segment_length - 1
segment_frames = frame_files[start_idx:end_idx + 1]
if len(segment_frames) < segment_length:
continue
frame_paths = "|".join(segment_frames)
video_path = os.path.join("frames", frame_paths)
writer.writerow({
"video": video_path,
"prompt": prompt,
"video_name": video_name,
"start_frame": start_idx,
"end_frame": end_idx,
})
total_segments += 1
video_segments += 1
print(f" Created {video_segments} segments for {video_name}")
print(f"\nTotal segments created: {total_segments}")
print(f"Metadata CSV saved to: {output_csv}")
def main():
parser = argparse.ArgumentParser(description="Preprocess Context-as-Memory Dataset")
parser.add_argument(
"--dataset_base_path",
type=str,
required=True,
help="Base path to Context-as-Memory dataset",
)
parser.add_argument(
"--output_csv",
type=str,
default="metadata.csv",
help="Output CSV file path (default: metadata.csv)",
)
parser.add_argument(
"--segment_length",
type=int,
default=81,
help="Length of video segments (default: 81 frames)",
)
parser.add_argument(
"--context_frames",
type=int,
default=5,
help="Number of context frames (default: 5)",
)
args = parser.parse_args()
if not os.path.isabs(args.output_csv):
args.output_csv = os.path.join(args.dataset_base_path, args.output_csv)
create_metadata_csv(
dataset_base_path=args.dataset_base_path,
output_csv=args.output_csv,
segment_length=args.segment_length,
context_frames=args.context_frames,
)
if __name__ == "__main__":
main()
|