Add DeMemWM multiview selector benchmark
Browse files
.exp_artifact/dememwm_dynamic_multiview_memory_selection_plan.md
CHANGED
|
@@ -805,7 +805,7 @@ Use multiview dynamic memory online
|
|
| 805 |
|
| 806 |
## Substep 6: Add Selector Speed Benchmark
|
| 807 |
|
| 808 |
-
Status: `[
|
| 809 |
|
| 810 |
Goal:
|
| 811 |
|
|
|
|
| 805 |
|
| 806 |
## Substep 6: Add Selector Speed Benchmark
|
| 807 |
|
| 808 |
+
Status: `[x]`
|
| 809 |
|
| 810 |
Goal:
|
| 811 |
|
scripts/benchmark_dememwm_multiview_selection.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Benchmark DeMemWM dynamic multiview memory selectors on synthetic poses."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import argparse
|
| 6 |
+
import importlib.util
|
| 7 |
+
import statistics
|
| 8 |
+
import sys
|
| 9 |
+
import time
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
import numpy as np
|
| 13 |
+
import torch
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 17 |
+
if str(REPO_ROOT) not in sys.path:
|
| 18 |
+
sys.path.insert(0, str(REPO_ROOT))
|
| 19 |
+
|
| 20 |
+
SELECTORS = ("fov_greedy", "pose_plucker_fps")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _load_memory_selection_module():
|
| 24 |
+
module_path = REPO_ROOT / "datasets" / "video" / "memory_selection.py"
|
| 25 |
+
spec = importlib.util.spec_from_file_location("dememwm_memory_selection", module_path)
|
| 26 |
+
if spec is None or spec.loader is None:
|
| 27 |
+
raise ImportError(f"could not load memory selection module from {module_path}")
|
| 28 |
+
module = importlib.util.module_from_spec(spec)
|
| 29 |
+
sys.modules[spec.name] = module
|
| 30 |
+
spec.loader.exec_module(module)
|
| 31 |
+
return module
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
memory_selection = _load_memory_selection_module()
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _parse_args() -> argparse.Namespace:
|
| 38 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 39 |
+
parser.add_argument("--num-frames", type=int, required=True)
|
| 40 |
+
parser.add_argument("--target-start", type=int, required=True)
|
| 41 |
+
parser.add_argument("--target-len", type=int, required=True)
|
| 42 |
+
parser.add_argument("--num-iters", type=int, required=True)
|
| 43 |
+
parser.add_argument("--pose-preselect-topk", type=int, required=True)
|
| 44 |
+
parser.add_argument("--candidate-chunk-size", type=int, required=True)
|
| 45 |
+
parser.add_argument("--selectors", nargs="+", default=list(SELECTORS))
|
| 46 |
+
parser.add_argument("--write-report", type=Path, default=None)
|
| 47 |
+
return parser.parse_args()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def _synthetic_poses(num_frames: int) -> np.ndarray:
|
| 51 |
+
frame = np.arange(num_frames, dtype=np.float32)
|
| 52 |
+
poses = np.zeros((num_frames, 5), dtype=np.float32)
|
| 53 |
+
poses[:, 0] = 0.03 * frame + 24.0 * np.sin(frame * 0.031)
|
| 54 |
+
poses[:, 1] = 4.0 * np.cos(frame * 0.019)
|
| 55 |
+
poses[:, 2] = 0.015 * frame + 24.0 * np.cos(frame * 0.027)
|
| 56 |
+
poses[:, 3] = 18.0 * np.sin(frame * 0.017)
|
| 57 |
+
poses[:, 4] = np.remainder(2.7 * frame + 30.0 * np.sin(frame * 0.011) + 180.0, 360.0) - 180.0
|
| 58 |
+
return poses
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _target_positions(target_start: int, target_len: int, num_frames: int) -> np.ndarray:
|
| 62 |
+
stop = target_start + target_len
|
| 63 |
+
if target_start < 0 or target_len <= 0 or stop > num_frames:
|
| 64 |
+
raise ValueError(
|
| 65 |
+
f"target window [{target_start}, {stop}) must be non-empty and inside num_frames={num_frames}"
|
| 66 |
+
)
|
| 67 |
+
return np.arange(target_start, stop, dtype=np.int64)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _selection_cfg(selector: str, args: argparse.Namespace) -> dict:
|
| 71 |
+
return {
|
| 72 |
+
"enabled": True,
|
| 73 |
+
"causal": True,
|
| 74 |
+
"max_anchor_frames": 0,
|
| 75 |
+
"max_dynamic_frames": args.target_len,
|
| 76 |
+
"max_revisit_frames": 0,
|
| 77 |
+
"pose_similarity_threshold": 0.0,
|
| 78 |
+
"training_use_plucker": True,
|
| 79 |
+
"training_plucker_weight": 1.0,
|
| 80 |
+
"fov_overlap_threshold": 0.6,
|
| 81 |
+
"min_total_selected_coverage": 0.1,
|
| 82 |
+
"local_context_exclusion_frames": 8,
|
| 83 |
+
"plucker_moment_radius": 30.0,
|
| 84 |
+
"anchor_diverse_selection": True,
|
| 85 |
+
"pose_preselect_topk": args.pose_preselect_topk,
|
| 86 |
+
"candidate_chunk_size": args.candidate_chunk_size,
|
| 87 |
+
"dynamic": {
|
| 88 |
+
"selection_policy": "multiview",
|
| 89 |
+
"multiview_selector": selector,
|
| 90 |
+
},
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _percentile(values: list[float], fraction: float) -> float:
|
| 95 |
+
if not values:
|
| 96 |
+
return 0.0
|
| 97 |
+
ordered = sorted(values)
|
| 98 |
+
index = min(len(ordered) - 1, int(np.ceil(fraction * len(ordered))) - 1)
|
| 99 |
+
return ordered[index]
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def _base_candidates(poses: np.ndarray, target_positions: np.ndarray, cfg: dict) -> np.ndarray:
|
| 103 |
+
return memory_selection._memory_candidate_frames(
|
| 104 |
+
len(poses),
|
| 105 |
+
target_positions,
|
| 106 |
+
cfg,
|
| 107 |
+
"training",
|
| 108 |
+
min_candidate_frame=0,
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def _fov_candidate_count(poses: np.ndarray, target_positions: np.ndarray, cfg: dict) -> int:
|
| 113 |
+
candidates = _base_candidates(poses, target_positions, cfg)
|
| 114 |
+
poses_t = torch.as_tensor(poses, dtype=torch.float32)
|
| 115 |
+
preselected = memory_selection._pose_preselect(candidates, poses_t, target_positions, cfg)
|
| 116 |
+
return int(len(preselected))
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def _pose_plucker_candidate_count(poses: np.ndarray, target_positions: np.ndarray, cfg: dict) -> int:
|
| 120 |
+
candidates = _base_candidates(poses, target_positions, cfg)
|
| 121 |
+
ranked_ids, _, _ = memory_selection._rank_pose_plucker_candidates(poses, candidates, target_positions, cfg)
|
| 122 |
+
topk = memory_selection.cfg_get(cfg, "pose_preselect_topk", 64)
|
| 123 |
+
if topk is not None and int(topk) > 0:
|
| 124 |
+
return int(min(int(topk), ranked_ids.numel()))
|
| 125 |
+
return int(ranked_ids.numel())
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def _run_once(selector: str, poses: np.ndarray, target_positions: np.ndarray, cfg: dict, count: int) -> np.ndarray:
|
| 129 |
+
if selector == "fov_greedy":
|
| 130 |
+
candidates = _base_candidates(poses, target_positions, cfg)
|
| 131 |
+
pool = memory_selection._build_fov_candidate_pool(
|
| 132 |
+
poses,
|
| 133 |
+
candidates,
|
| 134 |
+
target_positions,
|
| 135 |
+
cfg,
|
| 136 |
+
use_plucker=True,
|
| 137 |
+
)
|
| 138 |
+
return memory_selection._select_dynamic_multiview(
|
| 139 |
+
poses,
|
| 140 |
+
target_positions,
|
| 141 |
+
cfg,
|
| 142 |
+
count,
|
| 143 |
+
split="training",
|
| 144 |
+
fov_pool=pool,
|
| 145 |
+
)
|
| 146 |
+
if selector == "pose_plucker_fps":
|
| 147 |
+
return memory_selection._select_dynamic_multiview(
|
| 148 |
+
poses,
|
| 149 |
+
target_positions,
|
| 150 |
+
cfg,
|
| 151 |
+
count,
|
| 152 |
+
split="training",
|
| 153 |
+
)
|
| 154 |
+
raise ValueError(f"unknown selector {selector!r}")
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def _benchmark_selector(selector: str, poses: np.ndarray, target_positions: np.ndarray, args: argparse.Namespace) -> dict:
|
| 158 |
+
cfg = _selection_cfg(selector, args)
|
| 159 |
+
count = int(args.target_len)
|
| 160 |
+
if selector == "fov_greedy":
|
| 161 |
+
candidate_count = _fov_candidate_count(poses, target_positions, cfg)
|
| 162 |
+
fov_pool_reuse = True
|
| 163 |
+
else:
|
| 164 |
+
candidate_count = _pose_plucker_candidate_count(poses, target_positions, cfg)
|
| 165 |
+
fov_pool_reuse = False
|
| 166 |
+
|
| 167 |
+
selected = _run_once(selector, poses, target_positions, cfg, count)
|
| 168 |
+
timings_ms = []
|
| 169 |
+
for _ in range(args.num_iters):
|
| 170 |
+
start = time.perf_counter()
|
| 171 |
+
selected = _run_once(selector, poses, target_positions, cfg, count)
|
| 172 |
+
timings_ms.append((time.perf_counter() - start) * 1000.0)
|
| 173 |
+
|
| 174 |
+
return {
|
| 175 |
+
"selector": selector,
|
| 176 |
+
"mean_ms": statistics.fmean(timings_ms),
|
| 177 |
+
"median_ms": statistics.median(timings_ms),
|
| 178 |
+
"p90_ms": _percentile(timings_ms, 0.90),
|
| 179 |
+
"selected_count": int(len(selected)),
|
| 180 |
+
"candidate_count_after_pose_preselection": candidate_count,
|
| 181 |
+
"fov_pool_reuse": fov_pool_reuse,
|
| 182 |
+
"device": str(torch.device("cpu")),
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def _format_results(results: list[dict]) -> str:
|
| 187 |
+
lines = [
|
| 188 |
+
"selector mean_ms median_ms p90_ms selected_count candidate_count_after_pose_preselection fov_pool_reuse device"
|
| 189 |
+
]
|
| 190 |
+
for row in results:
|
| 191 |
+
lines.append(
|
| 192 |
+
"{selector} {mean_ms:.3f} {median_ms:.3f} {p90_ms:.3f} {selected_count} "
|
| 193 |
+
"{candidate_count_after_pose_preselection} {fov_pool_reuse} {device}".format(**row)
|
| 194 |
+
)
|
| 195 |
+
return "\n".join(lines)
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def _write_report(path: Path, args: argparse.Namespace, results: list[dict]) -> None:
|
| 199 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 200 |
+
lines = [
|
| 201 |
+
"# DeMemWM Multiview Selection Speed Report",
|
| 202 |
+
"",
|
| 203 |
+
"This benchmark used deterministic synthetic poses only. It is not a substitute for a real dataset sampling benchmark.",
|
| 204 |
+
"",
|
| 205 |
+
"```text",
|
| 206 |
+
"python " + " ".join(sys.argv),
|
| 207 |
+
"```",
|
| 208 |
+
"",
|
| 209 |
+
"| selector | mean ms | median ms | p90 ms | selected frames | pose-preselected candidates | FOV pool reuse | device |",
|
| 210 |
+
"| --- | ---: | ---: | ---: | ---: | ---: | --- | --- |",
|
| 211 |
+
]
|
| 212 |
+
for row in results:
|
| 213 |
+
lines.append(
|
| 214 |
+
"| {selector} | {mean_ms:.3f} | {median_ms:.3f} | {p90_ms:.3f} | {selected_count} | "
|
| 215 |
+
"{candidate_count_after_pose_preselection} | {fov_pool_reuse} | {device} |".format(**row)
|
| 216 |
+
)
|
| 217 |
+
lines.extend(
|
| 218 |
+
[
|
| 219 |
+
"",
|
| 220 |
+
f"Synthetic frames: {args.num_frames}",
|
| 221 |
+
f"Target window: [{args.target_start}, {args.target_start + args.target_len})",
|
| 222 |
+
f"Iterations: {args.num_iters}",
|
| 223 |
+
f"pose_preselect_topk: {args.pose_preselect_topk}",
|
| 224 |
+
f"candidate_chunk_size: {args.candidate_chunk_size}",
|
| 225 |
+
]
|
| 226 |
+
)
|
| 227 |
+
path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def main() -> int:
|
| 231 |
+
args = _parse_args()
|
| 232 |
+
unknown = [selector for selector in args.selectors if selector not in SELECTORS]
|
| 233 |
+
if unknown:
|
| 234 |
+
valid = ", ".join(SELECTORS)
|
| 235 |
+
print(f"unknown selector(s): {', '.join(unknown)}; valid selectors: {valid}", file=sys.stderr)
|
| 236 |
+
return 2
|
| 237 |
+
|
| 238 |
+
poses = _synthetic_poses(args.num_frames)
|
| 239 |
+
target_positions = _target_positions(args.target_start, args.target_len, args.num_frames)
|
| 240 |
+
results = [_benchmark_selector(selector, poses, target_positions, args) for selector in args.selectors]
|
| 241 |
+
print(_format_results(results))
|
| 242 |
+
|
| 243 |
+
if args.write_report is not None:
|
| 244 |
+
_write_report(args.write_report, args, results)
|
| 245 |
+
print(f"wrote report: {args.write_report}")
|
| 246 |
+
return 0
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
if __name__ == "__main__":
|
| 250 |
+
raise SystemExit(main())
|
tests/test_dememwm_latent_dataset.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
import json
|
|
|
|
|
|
|
| 2 |
import tempfile
|
| 3 |
import unittest
|
| 4 |
from unittest import mock
|
|
@@ -59,6 +61,10 @@ def _selection_cfg(**overrides):
|
|
| 59 |
return OmegaConf.create(cfg)
|
| 60 |
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
def _poses(num_frames):
|
| 63 |
poses = np.zeros((num_frames, 5), dtype=np.float32)
|
| 64 |
poses[:, 0] = np.arange(num_frames, dtype=np.float32)
|
|
@@ -1030,6 +1036,66 @@ class MemorySelectionTests(unittest.TestCase):
|
|
| 1030 |
with self.assertRaisesRegex(ValueError, "fov_greedy, pose_plucker_fps"):
|
| 1031 |
select_memory_indices(poses, np.array([6]), cfg)
|
| 1032 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1033 |
def test_multiview_policy_selection_uses_shared_fov_pool_and_segments(self):
|
| 1034 |
import datasets.video.memory_selection as memory_selection
|
| 1035 |
|
|
|
|
| 1 |
import json
|
| 2 |
+
import subprocess
|
| 3 |
+
import sys
|
| 4 |
import tempfile
|
| 5 |
import unittest
|
| 6 |
from unittest import mock
|
|
|
|
| 61 |
return OmegaConf.create(cfg)
|
| 62 |
|
| 63 |
|
| 64 |
+
def _benchmark_script_path():
|
| 65 |
+
return Path(__file__).resolve().parents[1] / "scripts" / "benchmark_dememwm_multiview_selection.py"
|
| 66 |
+
|
| 67 |
+
|
| 68 |
def _poses(num_frames):
|
| 69 |
poses = np.zeros((num_frames, 5), dtype=np.float32)
|
| 70 |
poses[:, 0] = np.arange(num_frames, dtype=np.float32)
|
|
|
|
| 1036 |
with self.assertRaisesRegex(ValueError, "fov_greedy, pose_plucker_fps"):
|
| 1037 |
select_memory_indices(poses, np.array([6]), cfg)
|
| 1038 |
|
| 1039 |
+
def test_benchmark_script_runs_synthetic_mode_without_dataset_paths(self):
|
| 1040 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 1041 |
+
report_path = Path(tmpdir) / "speed_report.md"
|
| 1042 |
+
result = subprocess.run(
|
| 1043 |
+
[
|
| 1044 |
+
sys.executable,
|
| 1045 |
+
str(_benchmark_script_path()),
|
| 1046 |
+
"--num-frames",
|
| 1047 |
+
"32",
|
| 1048 |
+
"--target-start",
|
| 1049 |
+
"16",
|
| 1050 |
+
"--target-len",
|
| 1051 |
+
"2",
|
| 1052 |
+
"--num-iters",
|
| 1053 |
+
"1",
|
| 1054 |
+
"--pose-preselect-topk",
|
| 1055 |
+
"4",
|
| 1056 |
+
"--candidate-chunk-size",
|
| 1057 |
+
"4",
|
| 1058 |
+
"--write-report",
|
| 1059 |
+
str(report_path),
|
| 1060 |
+
],
|
| 1061 |
+
cwd=Path(__file__).resolve().parents[1],
|
| 1062 |
+
capture_output=True,
|
| 1063 |
+
text=True,
|
| 1064 |
+
)
|
| 1065 |
+
|
| 1066 |
+
self.assertEqual(result.returncode, 0, msg=result.stderr)
|
| 1067 |
+
self.assertIn("fov_greedy", result.stdout)
|
| 1068 |
+
self.assertIn("pose_plucker_fps", result.stdout)
|
| 1069 |
+
self.assertIn("not a substitute for a real dataset sampling benchmark", report_path.read_text(encoding="utf-8"))
|
| 1070 |
+
|
| 1071 |
+
def test_benchmark_script_rejects_unknown_selector(self):
|
| 1072 |
+
result = subprocess.run(
|
| 1073 |
+
[
|
| 1074 |
+
sys.executable,
|
| 1075 |
+
str(_benchmark_script_path()),
|
| 1076 |
+
"--num-frames",
|
| 1077 |
+
"32",
|
| 1078 |
+
"--target-start",
|
| 1079 |
+
"16",
|
| 1080 |
+
"--target-len",
|
| 1081 |
+
"2",
|
| 1082 |
+
"--num-iters",
|
| 1083 |
+
"1",
|
| 1084 |
+
"--pose-preselect-topk",
|
| 1085 |
+
"4",
|
| 1086 |
+
"--candidate-chunk-size",
|
| 1087 |
+
"4",
|
| 1088 |
+
"--selectors",
|
| 1089 |
+
"fov_fps",
|
| 1090 |
+
],
|
| 1091 |
+
cwd=Path(__file__).resolve().parents[1],
|
| 1092 |
+
capture_output=True,
|
| 1093 |
+
text=True,
|
| 1094 |
+
)
|
| 1095 |
+
|
| 1096 |
+
self.assertNotEqual(result.returncode, 0)
|
| 1097 |
+
self.assertIn("unknown selector", result.stderr)
|
| 1098 |
+
|
| 1099 |
def test_multiview_policy_selection_uses_shared_fov_pool_and_segments(self):
|
| 1100 |
import datasets.video.memory_selection as memory_selection
|
| 1101 |
|