Merge 2025-07-15 batch into the xlsx; tools to detect & re-track

- merge_2025_07_15_into_xlsx.py: pivot the legacy 2025_07_15_metadata_fixed.csv
  into the unified xlsx schema (one row per fly, training_date_time +
  testing_date_time). Backs up the xlsx before writing. 24 new rows
  across machines 076 / 139 / 145 / 268.
- pick_targets.py: --video flag to bypass the inventory's in_xlsx filter,
  so a specific mp4 can be picked outside the normal flow.
- explore_barrier_signal.py: visualises raw y(t), per-frame inter-fly
  distance, and sliding min/mean distance against a known
  barrier-opening time. Used for prototyping the detector.
- detect_barrier_opening.py: per-ROI sliding-window mean-distance
  change-point estimator (median across ROIs). Currently noisy on a
  one-video calibration set; will be re-tuned once the 4 missing
  2025-07-15 videos are re-tracked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-01 10:28:25 +01:00
parent 8f3c4ca89c
commit 847d2cbd1b
4 changed files with 480 additions and 1 deletions

View file

@ -363,6 +363,12 @@ def main() -> None:
"--limit", type=int, default=None,
help="only process the first N videos",
)
parser.add_argument(
"--video", action="append", default=[],
metavar="MP4_PATH",
help="explicit mp4 path to pick targets for (bypasses the inventory's "
"in_xlsx filter). Repeat to specify multiple videos.",
)
args = parser.parse_args()
if not INVENTORY_CSV.exists():
@ -372,7 +378,27 @@ def main() -> None:
)
inv = pd.read_csv(INVENTORY_CSV)
todo = inv[inv["in_xlsx"] & ~inv["already_tracked"]].copy()
if args.video:
# Reason: explicit --video paths skip the in_xlsx filter so we can
# re-track recordings that aren't in the merged xlsx (e.g. the
# 2025-07-15 multi-recording sessions). Each path must exist in
# the inventory so we still get machine_name / session_datetime
# for the prompt; build a small synthetic todo from those rows.
wanted = {str(Path(p).resolve()) for p in args.video}
inv["_resolved"] = inv["mp4_path"].apply(lambda p: str(Path(p).resolve()))
todo = inv[inv["_resolved"].isin(wanted)].drop(columns="_resolved").copy()
missing = wanted - set(
inv["mp4_path"].apply(lambda p: str(Path(p).resolve()))
)
if missing:
print(f"{len(missing)} requested video(s) not in inventory; "
"rebuild it with build_video_inventory.py if needed:")
for m in sorted(missing):
print(f" {m}")
if todo.empty:
sys.exit("No matching videos in inventory.")
else:
todo = inv[inv["in_xlsx"] & ~inv["already_tracked"]].copy()
todo = todo.sort_values(
["session_date", "machine_name", "session_time"]
).reset_index(drop=True)