149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""篮子接触分段入口:OpenCV 框选篮子 → 手篮接触上升沿 → Phase2(跳过 ActionFormer)。"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import argparse
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
PACK_ROOT = Path(__file__).resolve().parent
|
|||
|
|
sys.path.insert(0, str(PACK_ROOT / "src"))
|
|||
|
|
|
|||
|
|
from paths import ensure_code_on_path
|
|||
|
|
|
|||
|
|
ensure_code_on_path(PACK_ROOT)
|
|||
|
|
|
|||
|
|
from config import load_run_config
|
|||
|
|
from orchestrator import run_basket_pipeline
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> int:
|
|||
|
|
os.environ.setdefault("OPENCV_FFMPEG_LOGLEVEL", "8")
|
|||
|
|
ap = argparse.ArgumentParser(
|
|||
|
|
description="手术室耗材篮子接触分段主流程(跳过 ActionFormer)"
|
|||
|
|
)
|
|||
|
|
ap.add_argument("--video", type=Path, required=True, help="输入 MP4")
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--excel",
|
|||
|
|
type=Path,
|
|||
|
|
required=True,
|
|||
|
|
help="商品表 Excel(C 列白名单 + 产品编码)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument("--out", type=Path, required=True, help="输出 TSV")
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--config",
|
|||
|
|
type=Path,
|
|||
|
|
default=PACK_ROOT / "configs" / "default_config.yaml",
|
|||
|
|
help="继承 weights / phase2 / classification / doctor / basket 的 YAML",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--save-basket-roi",
|
|||
|
|
type=Path,
|
|||
|
|
default=None,
|
|||
|
|
help="框选后将 ROI 保存为 JSON(可选;每次运行仍会先弹窗标框)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--det-conf",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="篮子扫描手部检测置信度(默认读 yaml basket.det_conf)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--contact-iou-on",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="篮子接触 IoU 进入阈值(默认读 yaml basket.contact_iou_on)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--contact-iou-off",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="篮子接触 IoU 退出阈值(默认读 yaml basket.contact_iou_off)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--confirm-seconds",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="连续接触确认时长(秒,默认 0.4)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--cooldown-seconds",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="触发后绝对冷却时长(秒,默认 5.0)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--contact-iou-threshold",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="手框与篮子 IoU 阈值(默认读 yaml basket.contact_iou_threshold)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--segment-start-offset-sec",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="段起点相对接触时刻偏移(秒,默认 1 → contact+1)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--segment-end-offset-sec",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="段终点相对接触时刻偏移(秒,默认 5 → contact+5)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--min-segment-sec",
|
|||
|
|
type=float,
|
|||
|
|
default=None,
|
|||
|
|
help="截断后段长不足此值则丢弃(秒,默认 4.0;0 表示不过滤)",
|
|||
|
|
)
|
|||
|
|
ap.add_argument(
|
|||
|
|
"--scan-frame-stride",
|
|||
|
|
type=int,
|
|||
|
|
default=None,
|
|||
|
|
help="全片接触扫描帧步长(默认 1)",
|
|||
|
|
)
|
|||
|
|
args = ap.parse_args()
|
|||
|
|
|
|||
|
|
cfg_path = args.config.resolve()
|
|||
|
|
if not cfg_path.is_file():
|
|||
|
|
print("找不到配置:", cfg_path, file=sys.stderr)
|
|||
|
|
return 1
|
|||
|
|
|
|||
|
|
run_cfg = load_run_config(PACK_ROOT, cfg_path)
|
|||
|
|
run_cfg.video = args.video.resolve()
|
|||
|
|
run_cfg.excel = args.excel.resolve()
|
|||
|
|
run_cfg.out = args.out.resolve()
|
|||
|
|
|
|||
|
|
# 每次运行均在首帧弹窗标框,不从 JSON / yaml 复用 ROI
|
|||
|
|
run_cfg.basket_load_roi_json = None
|
|||
|
|
run_cfg.basket_skip_roi_select = False
|
|||
|
|
if args.save_basket_roi is not None:
|
|||
|
|
run_cfg.basket_save_roi_json = args.save_basket_roi.resolve()
|
|||
|
|
if args.det_conf is not None:
|
|||
|
|
run_cfg.basket_det_conf = float(args.det_conf)
|
|||
|
|
if args.contact_iou_on is not None:
|
|||
|
|
run_cfg.basket_contact_iou_on = float(args.contact_iou_on)
|
|||
|
|
if args.contact_iou_off is not None:
|
|||
|
|
run_cfg.basket_contact_iou_off = float(args.contact_iou_off)
|
|||
|
|
if args.confirm_seconds is not None:
|
|||
|
|
run_cfg.basket_confirm_seconds = float(args.confirm_seconds)
|
|||
|
|
if args.cooldown_seconds is not None:
|
|||
|
|
run_cfg.basket_cooldown_seconds = float(args.cooldown_seconds)
|
|||
|
|
if args.contact_iou_threshold is not None:
|
|||
|
|
run_cfg.basket_contact_iou_threshold = float(args.contact_iou_threshold)
|
|||
|
|
if args.segment_start_offset_sec is not None:
|
|||
|
|
run_cfg.basket_segment_start_offset_sec = float(args.segment_start_offset_sec)
|
|||
|
|
if args.segment_end_offset_sec is not None:
|
|||
|
|
run_cfg.basket_segment_end_offset_sec = float(args.segment_end_offset_sec)
|
|||
|
|
if args.min_segment_sec is not None:
|
|||
|
|
run_cfg.basket_min_segment_sec = float(args.min_segment_sec)
|
|||
|
|
if args.scan_frame_stride is not None:
|
|||
|
|
run_cfg.basket_scan_frame_stride = int(args.scan_frame_stride)
|
|||
|
|
|
|||
|
|
return int(run_basket_pipeline(run_cfg))
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
raise SystemExit(main())
|