echo / code /eval /metrics /common.py
amonshano's picture
Add Echo-Memory codebase used for this run (CC BY 4.0, JD Echo Team) (part 2)
eafbe80 verified
Raw
History Blame Contribute Delete
2.75 kB
"""
Shared utilities for eval_metrics: discover evals_ep0 outputs and load video frames.
"""
from __future__ import annotations
import os
from typing import List, Tuple
try:
import cv2
HAS_CV2 = True
except ImportError:
HAS_CV2 = False
import numpy as np
def discover_evals_videos(evals_root: str, pattern: str = "*_gen_only.mp4") -> List[Tuple[str, str]]:
"""
Discover all generated-only MP4s under evals_ep0 structure.
Returns list of (relative_path, absolute_path) for each video.
"""
out: List[Tuple[str, str]] = []
evals_root = os.path.abspath(evals_root)
for root, _dirs, files in os.walk(evals_root):
for f in files:
if f.endswith("_gen_only.mp4") or (pattern != "*_gen_only.mp4" and f.endswith(".mp4")):
absp = os.path.join(root, f)
rel = os.path.relpath(absp, evals_root)
out.append((rel, absp))
return sorted(out, key=lambda x: x[0])
def discover_loop_closure_videos(evals_root: str) -> List[Tuple[str, str]]:
"""Discover MP4s under 1_loop_4chunk and 3_multi_ctx_4chunk for loop closure (prefer gen_only)."""
out: List[Tuple[str, str]] = []
for sub in ("1_loop_4chunk", "3_multi_ctx_4chunk"):
d = os.path.join(evals_root, sub)
if not os.path.isdir(d):
continue
for root, _dirs, files in os.walk(d):
for f in files:
if f.endswith("_gen_only.mp4"):
absp = os.path.join(root, f)
rel = os.path.relpath(absp, evals_root)
out.append((rel, absp))
return sorted(out, key=lambda x: x[0])
def load_video_frames(path: str, max_frames: int | None = None) -> np.ndarray:
"""
Load video as array of frames (RGB, uint8).
Returns (N, H, W, 3). If max_frames set, stop after that many frames.
"""
if not HAS_CV2:
raise RuntimeError("opencv-python is required for video loading (pip install opencv-python)")
cap = cv2.VideoCapture(path)
frames = []
while True:
ret, frame = cap.read()
if not ret:
break
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(frame_rgb)
if max_frames is not None and len(frames) >= max_frames:
break
cap.release()
if not frames:
return np.zeros((0, 0, 0, 3), dtype=np.uint8)
return np.stack(frames, axis=0)
def load_video_frames_pil(path: str, max_frames: int | None = None):
"""Load video as list of PIL Images (for CLIP etc.)."""
from PIL import Image
arr = load_video_frames(path, max_frames=max_frames)
if arr.size == 0:
return []
return [Image.fromarray(arr[i]) for i in range(arr.shape[0])]