Files
FishServer/fish_api/app/measure_debug_cli.py

128 lines
4.3 KiB
Python
Raw Permalink Normal View History

"""CLI对单条 fish 跑 FishMeasure 子进程(不写 SQLite、不发布 /media
2026-04-14 22:06:33 +08:00
使用 ``run_measure_batch_subprocess``合并点云线上 ``measure_watch`` 为逐段 ``run_full_measure``齐套后再 aggregate final
2026-04-14 22:06:33 +08:00
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any, Dict, List
from app.services.measure import run_measure_batch_subprocess
from app.settings import get_settings
def _sorted_svo2_in_folder(folder: Path) -> List[Path]:
"""与 measure_watch.iter_svo2_folders 中单目录内 .svo2 列表一致(按文件名排序)。"""
return sorted(
[p for p in folder.iterdir() if p.is_file() and p.suffix.lower() == ".svo2"],
key=lambda p: p.name,
)
def _print_weight_summary(output_root: Path) -> None:
combined = output_root / "weight_prediction.json"
if not combined.is_file():
print(f"[measure-debug] no combined file: {combined}", file=sys.stderr)
return
try:
with open(combined, encoding="utf-8") as f:
data: Dict[str, Any] = json.load(f)
except (OSError, json.JSONDecodeError) as e:
print(f"[measure-debug] failed to read {combined}: {e}", file=sys.stderr)
return
summary = data.get("dgcnn_summary") or data.get("weight_summary") or {}
pred_g = data.get("pred_weight_g")
rule = data.get("pred_weight_rule")
print("[measure-debug] --- weight_prediction.json (summary) ---")
if pred_g is not None:
print(f" pred_weight_g: {pred_g}")
if rule is not None:
print(f" pred_weight_rule: {rule}")
if summary:
top_k = summary.get("top_k")
tbl = summary.get("top_by_length")
print(f" dgcnn_summary.top_k: {top_k}")
print(f" dgcnn_summary.top_by_length: {tbl}")
mean_all = data.get("mean_all_pred_g_after_filters")
if mean_all is not None:
print(f" mean_all_pred_g_after_filters: {mean_all}")
def main() -> None:
parser = argparse.ArgumentParser(
description="Run FishMeasure for one fish folder (same subprocess as fish_api)."
)
parser.add_argument(
"--fish-id",
default="1",
help='Fish id used for output dir measure_output/fish{N} (default: 1).',
)
parser.add_argument(
"--batch-folder",
type=Path,
default=None,
help="Folder containing .svo2 files (overrides MEASURE_WATCH_DIR/fish{N}).",
)
parser.add_argument(
"--watch-dir",
type=Path,
default=None,
help="Override MEASURE_WATCH_DIR when resolving fish{N}/ (ignored if --batch-folder is set).",
)
parser.add_argument(
"--output-root",
type=Path,
default=None,
help="Override save-output directory (default: MEASURE_OUTPUT_ROOT/fish{N}).",
)
args = parser.parse_args()
settings = get_settings()
fish_id = str(args.fish_id).strip() or "1"
if args.batch_folder is not None:
batch_folder = args.batch_folder.expanduser().resolve()
else:
watch = args.watch_dir if args.watch_dir is not None else settings.measure_watch_dir
if watch is None:
print(
"未配置输入目录:请设置 --batch-folder或在 .env 中设置 MEASURE_WATCH_DIR",
file=sys.stderr,
)
raise SystemExit(2)
watch = watch.expanduser().resolve()
batch_folder = watch / f"fish{fish_id}"
if not batch_folder.is_dir():
print(f"不是目录或不存在: {batch_folder}", file=sys.stderr)
raise SystemExit(2)
svo_paths = _sorted_svo2_in_folder(batch_folder)
if not svo_paths:
print(f"目录下无 .svo2 文件: {batch_folder}", file=sys.stderr)
raise SystemExit(2)
if args.output_root is not None:
out_root = args.output_root.expanduser().resolve()
else:
out_root = settings.measure_output_root / f"fish{fish_id}"
out_root.mkdir(parents=True, exist_ok=True)
print(f"[measure-debug] batch_folder={batch_folder}")
print(f"[measure-debug] output_root={out_root}")
print(f"[measure-debug] {len(svo_paths)} SVO(s): {', '.join(p.name for p in svo_paths)}")
run_measure_batch_subprocess(svo_paths, settings, output_root=out_root)
_print_weight_summary(out_root)
print("[measure-debug] done.")
if __name__ == "__main__":
main()