diff --git a/.gitignore b/.gitignore index 6ecf05a..4eec5e6 100755 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,16 @@ backend/Ultralytics/ # Large model weights (deploy via mount or offline delivery) backend/app/resources/actionformer_epoch_045.pth.tar +# Algorithm subprocess bundles — runtime assets ignored via backend/algorithm_subprocesses/.gitignore +backend/algorithm_subprocesses/**/weights/* +!backend/algorithm_subprocesses/**/weights/.gitkeep +backend/algorithm_subprocesses/**/input/* +!backend/algorithm_subprocesses/**/input/.gitkeep +backend/algorithm_subprocesses/**/output/* +!backend/algorithm_subprocesses/**/output/.gitkeep +backend/algorithm_subprocesses/**/data/* +!backend/algorithm_subprocesses/**/data/.gitkeep + # --- Clients --- clients/demo-client/.runtime/ clients/demo-client/labels.json diff --git a/backend/.dockerignore b/backend/.dockerignore index b83ddab..8128252 100755 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -10,6 +10,6 @@ __pycache__ .env.* !.env.example !.env.prod -refs +algorithm_subprocesses *.md .dockerignore diff --git a/backend/algorithm_subprocesses/.gitignore b/backend/algorithm_subprocesses/.gitignore new file mode 100644 index 0000000..615b590 --- /dev/null +++ b/backend/algorithm_subprocesses/.gitignore @@ -0,0 +1,44 @@ +# 算法子进程包(algorithm subprocess bundles) +# 源码与配置保留在 git;权重、样本 I/O、构建缓存离线交付或挂载。 +# 详见 algorithm_subprocesses/5.15/README.md §「准备权重与输入」。 + +# --- 运行时 I/O(保留 .gitkeep)--- +**/weights/* +!**/weights/.gitkeep +**/input/* +!**/input/.gitkeep +**/output/* +!**/output/.gitkeep +**/data/* +!**/data/.gitkeep + +# --- 大文件 / 离线资产 --- +*.pt +*.pth +*.pth.tar +*.pkl +*.task +*.mp4 +*.xlsx +*.xls +**/商品信息表.xlsx + +# --- 子包运行时 --- +doctor_identity_package/.mediapipe_models/ +**/infer_single_*/input/ +**/*_run.log +**/output/result.txt + +# --- Python 构建与缓存(子进程内 pip install / 编译产物)--- +**/__pycache__/ +**/*.py[cod] +**/*$py.class +**/.pytest_cache/ +**/.mypy_cache/ +**/.ruff_cache/ +**/.cache/ +**/build/ +**/*.egg-info/ +**/*.so +**/*.o +**/dist/ diff --git a/backend/app/algorithm_runner/actionformer_gated/__init__.py b/backend/app/algorithm_runner/actionformer_gated/__init__.py index 6a1fe8d..a265019 100644 --- a/backend/app/algorithm_runner/actionformer_gated/__init__.py +++ b/backend/app/algorithm_runner/actionformer_gated/__init__.py @@ -1,4 +1,4 @@ -"""ActionFormer 实时段落检测 + 41 类耗材:使用 ``refs/5.15`` 源码,输入为 RTSP。""" +"""ActionFormer 实时段落检测 + 41 类耗材:使用 ``algorithm_subprocesses/5.15`` 源码,输入为 RTSP。""" from app.algorithm_runner.actionformer_gated.runner import ( ActionFormerSegmentRecord, diff --git a/backend/app/algorithm_runner/actionformer_gated/inference.py b/backend/app/algorithm_runner/actionformer_gated/inference.py index 52e34f2..59c3444 100644 --- a/backend/app/algorithm_runner/actionformer_gated/inference.py +++ b/backend/app/algorithm_runner/actionformer_gated/inference.py @@ -1,6 +1,6 @@ -"""ActionFormer 加载与单视频 forward(使用 refs/5.15 vendor 源码)。 +"""ActionFormer 加载与单视频 forward(使用 algorithm_subprocesses/5.15 vendor 源码)。 -复刻 refs/5.15 ``run_haocai_actionformer_consumables_e2e`` 写出的 ``infer_single.yaml`` +复刻 algorithm_subprocesses/5.15 ``run_haocai_actionformer_consumables_e2e`` 写出的 ``infer_single.yaml`` 推理配置(``haocai_main_perspective_videoswin``):input_dim=768, num_classes=1, feat_stride=32, num_frames=64, max_seq_len=2304, fpn_type=identity, n_mha_win_size=19, embd_dim=fpn_dim=head_dim=256, n_head=4, use_abs_pe=False。 @@ -35,7 +35,7 @@ def _make_inference_cfg( feat_stride: int, num_frames: int, ) -> dict: - """与 refs/5.15 e2e ``write_infer_yaml`` 同语义的推理时 cfg dict。""" + """与 algorithm_subprocesses/5.15 e2e ``write_infer_yaml`` 同语义的推理时 cfg dict。""" cfg = deepcopy(load_default_config()) cfg["dataset_name"] = "thumos" diff --git a/backend/app/algorithm_runner/actionformer_gated/runner.py b/backend/app/algorithm_runner/actionformer_gated/runner.py index b3f4e00..7de021a 100644 --- a/backend/app/algorithm_runner/actionformer_gated/runner.py +++ b/backend/app/algorithm_runner/actionformer_gated/runner.py @@ -1,6 +1,6 @@ """Online ActionFormer 子进程算法核心。 -把 refs/5.15 ``run_haocai_actionformer_consumables_e2e`` 的批处理流水线改造为可吃 RTSP +把 algorithm_subprocesses/5.15 ``run_haocai_actionformer_consumables_e2e`` 的批处理流水线改造为可吃 RTSP 的实时近似: * 逐帧维护 64 帧(``CLIP_LEN * FRAME_STRIDE``)的 RGB 224x224 ring。 @@ -90,7 +90,7 @@ class _FrameClsState: @dataclass class _Feature: - """特征缓冲单元:对应 refs/5.15 离线 ``feats[i]``。""" + """特征缓冲单元:对应 algorithm_subprocesses/5.15 离线 ``feats[i]``。""" t_center: float # 该 clip 中心对应的流时间(秒) feat: np.ndarray # shape [INPUT_DIM] float32 @@ -175,7 +175,7 @@ def _to_segment_records( class OnlineActionFormerRunner: - """RTSP 实时版的 refs/5.15 流水线,公共 API 与 ``TearGatedSegmentRunner`` 同形。""" + """RTSP 实时版的 algorithm_subprocesses/5.15 流水线,公共 API 与 ``TearGatedSegmentRunner`` 同形。""" def __init__( self, @@ -404,7 +404,7 @@ class OnlineActionFormerRunner: self._frame_ring_t.append(float(t_abs)) def _maybe_extract_videoswin_feature(self) -> None: - # 与 refs/5.15 离线对齐:feature i 对应 clip 起始帧 s = i * feat_stride_frames, + # 与 algorithm_subprocesses/5.15 离线对齐:feature i 对应 clip 起始帧 s = i * feat_stride_frames, # clip 末尾帧 s + (clip_len - 1) * frame_stride。当 ring 已塞满(clip_span # 帧),且当前 frame_idx == s + clip_span - 1(即 (frame_idx + 1) % # feat_stride_frames == 0 且 frame_idx >= clip_span - 1)即可产新特征。 diff --git a/backend/app/algorithm_runner/actionformer_gated/segment_helpers.py b/backend/app/algorithm_runner/actionformer_gated/segment_helpers.py index c3460d7..ea3392c 100644 --- a/backend/app/algorithm_runner/actionformer_gated/segment_helpers.py +++ b/backend/app/algorithm_runner/actionformer_gated/segment_helpers.py @@ -56,7 +56,7 @@ def haocai_softmax_probs( *, device: str | None = None, ) -> np.ndarray | None: - """耗材分类:返回长度 n_cls 的 softmax 概率向量(与 refs/5.15 同语义)。""" + """耗材分类:返回长度 n_cls 的 softmax 概率向量(与 algorithm_subprocesses/5.15 同语义)。""" if crop.size == 0: return None r = cls_model.predict(crop, imgsz=imgsz, verbose=False, device=device)[0] diff --git a/backend/app/algorithm_runner/actionformer_gated/videoswin.py b/backend/app/algorithm_runner/actionformer_gated/videoswin.py index bb9f963..989ff81 100644 --- a/backend/app/algorithm_runner/actionformer_gated/videoswin.py +++ b/backend/app/algorithm_runner/actionformer_gated/videoswin.py @@ -1,4 +1,4 @@ -"""VideoSwin (Swin3D-T, Kinetics-400) feature extractor backed by refs/5.15. +"""VideoSwin (Swin3D-T, Kinetics-400) feature extractor backed by algorithm_subprocesses/5.15. The online runner keeps a small wrapper API, but model construction and clip preprocessing are delegated to the configured reference bundle's @@ -50,7 +50,7 @@ class VideoSwinFeatureExtractor: """Thin wrapper over swin3d_t + preprocess_clip. 输入:``frames_rgb`` 为 32 帧(``CLIP_LEN``)RGB 帧(``np.ndarray[H, W, 3]``)。 - 输出:``[INPUT_DIM]`` 一维特征张量(CPU),与 refs/5.15 离线提特征同语义。 + 输出:``[INPUT_DIM]`` 一维特征张量(CPU),与 algorithm_subprocesses/5.15 离线提特征同语义。 """ def __init__(self, *, device: torch.device, image_size: int = 224) -> None: diff --git a/backend/app/algorithm_runner/cli.py b/backend/app/algorithm_runner/cli.py index 5a349e1..77bc9d2 100644 --- a/backend/app/algorithm_runner/cli.py +++ b/backend/app/algorithm_runner/cli.py @@ -1,6 +1,6 @@ """子进程入口:读 RTSP + 白名单 JSON,段级结果追加写入 JSONL。 -基于 refs/5.15 ``actionformer_gated`` 流水线(VideoSwin → ActionFormer + 手检/好坏帧/ +基于 algorithm_subprocesses/5.15 ``actionformer_gated`` 流水线(VideoSwin → ActionFormer + 手检/好坏帧/ 耗材分类投票);父进程通过 ``--events-jsonl`` tail JSONL 即可消费段事件。 """ diff --git a/backend/app/algorithm_runner/reference_bundle_runtime.py b/backend/app/algorithm_runner/reference_bundle_runtime.py index 7bab00a..c2cb396 100644 --- a/backend/app/algorithm_runner/reference_bundle_runtime.py +++ b/backend/app/algorithm_runner/reference_bundle_runtime.py @@ -11,7 +11,7 @@ from typing import Any import yaml REPO_ROOT = Path(__file__).resolve().parents[2] -DEFAULT_REFERENCE_BUNDLE_RELATIVE = "refs/5.15" +DEFAULT_REFERENCE_BUNDLE_RELATIVE = "algorithm_subprocesses/5.15" def configured_reference_bundle_relative() -> str: diff --git a/backend/app/baked/algorithm.py b/backend/app/baked/algorithm.py index 4be1edf..6fdfd1e 100644 --- a/backend/app/baked/algorithm.py +++ b/backend/app/baked/algorithm.py @@ -60,7 +60,7 @@ HAND_DETECTION_MIN_CROP_PX: int = 64 HAND_DETECTION_DEVICE: str = "" -# --- refs/5.15 ActionFormer 实时版(VideoSwin → ActionFormer + 手检/好坏帧/耗材投票)--- +# --- algorithm_subprocesses/5.15 ActionFormer 实时版(VideoSwin → ActionFormer + 手检/好坏帧/耗材投票)--- # 默认开启并与常见 demo 主摄 id 对齐;若缺少配置引用包 weights 下权重,开录时该路会记录异常。 ACTIONFORMER_ENABLED: bool = True # 检查期:单路算法固定拉流 or-cam-03(恢复按请求首路时请改回 "" 或按需配置)。 @@ -69,7 +69,7 @@ ACTIONFORMER_PRIMARY_CAMERA_ID: str = "or-cam-03" # RTSP 帧率假设(用于 frame_idx → t_abs 推算与 ActionFormer fps 入参;线上需与摄像机一致)。 ACTIONFORMER_ASSUMED_FPS: float = 25.0 -# 与 refs/5.15 ``run_haocai_actionformer_consumables_e2e`` 写出的 infer_single.yaml 同语义的 VideoSwin 参数: +# 与 algorithm_subprocesses/5.15 ``run_haocai_actionformer_consumables_e2e`` 写出的 infer_single.yaml 同语义的 VideoSwin 参数: # clip_len 32, frame_stride 2, feat_stride_frames 32 → feat_num_frames=64。 VIDEOSWIN_CLIP_LEN: int = 32 VIDEOSWIN_FRAME_STRIDE: int = 2 @@ -85,7 +85,7 @@ ACTIONFORMER_HAOCAI_WEIGHTS: str = default_actionformer_haocai_weights_path() ACTIONFORMER_TEAR_WEIGHTS: str = default_actionformer_tear_weights_path() ACTIONFORMER_NUM_CLASSES: int = 1 # 单类(Action)划段,与 e2e infer_single.yaml 对齐 -# 与 refs/5.15 e2e 默认对齐的运行时阈值 +# 与 algorithm_subprocesses/5.15 e2e 默认对齐的运行时阈值 ACTIONFORMER_DET_CONF: float = 0.5 ACTIONFORMER_PAD_RATIO: float = 0.30 ACTIONFORMER_DET_IMGSZ: int = 640 diff --git a/backend/app/config.py b/backend/app/config.py index 725fab6..e544f38 100755 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -193,9 +193,9 @@ class Settings(BaseSettings): default="vision", validation_alias=AliasChoices("VIDEO_RESULT_DOCTOR_ID", "video_result_doctor_id"), ) - #: 算法引用包目录(相对 backend 根目录,如 ``refs/5.15``);可用环境变量 ``REFERENCE_BUNDLE_RELATIVE`` 覆盖。 + #: 算法子进程包目录(相对 backend 根目录,如 ``algorithm_subprocesses/5.15``);可用环境变量 ``REFERENCE_BUNDLE_RELATIVE`` 覆盖。 reference_bundle_relative: str = Field( - default="refs/5.15", + default="algorithm_subprocesses/5.15", validation_alias=AliasChoices("REFERENCE_BUNDLE_RELATIVE", "reference_bundle_relative"), ) diff --git a/backend/app/resources/consumable_classifier_labels.yaml b/backend/app/resources/consumable_classifier_labels.yaml index 21cc880..eb56c60 100644 --- a/backend/app/resources/consumable_classifier_labels.yaml +++ b/backend/app/resources/consumable_classifier_labels.yaml @@ -1,4 +1,4 @@ -# 与训练 data.yaml 类名一致;label_id 来自 refs/商品信息表.xlsx(商品名称→产品编码;同名多规格以 / 连接)。 +# 与训练 data.yaml 类名一致;label_id 来自 algorithm_subprocesses 离线包内商品信息表.xlsx(商品名称→产品编码;同名多规格以 / 连接)。 # 推理时以权重内嵌 names 为准;本文件供业务 label_id 与开录空 candidate 全量类名。 names: 0: MCuⅡ功能性宫内节育器 diff --git a/backend/app/routers/demo_orch.py b/backend/app/routers/demo_orch.py index 61da974..bc8cd50 100755 --- a/backend/app/routers/demo_orch.py +++ b/backend/app/routers/demo_orch.py @@ -71,7 +71,7 @@ def _orchestrate_write_rtsp_host() -> str: response_model=VideoBatchSurgeryResponse, summary="非实时精确模式:上传单路 MP4 并跑配置引用包 batch", description=( - "仅当 DEMO_ORCHESTRATOR_ENABLED=true。保存上传视频,调用配置引用包 main.py(默认 refs/5.15)," + "仅当 DEMO_ORCHESTRATOR_ENABLED=true。保存上传视频,调用配置算法子进程包 main.py(默认 algorithm_subprocesses/5.15)," "解析 TSV 后写入最终结果,并调用 visualize_result_video.py 生成带标签视频。" ), ) diff --git a/backend/app/services/video_batch_runner.py b/backend/app/services/video_batch_runner.py index b12c735..545371f 100644 --- a/backend/app/services/video_batch_runner.py +++ b/backend/app/services/video_batch_runner.py @@ -38,7 +38,7 @@ from app.domain.consumption import SurgeryConsumptionStored @dataclass(frozen=True) class ReferenceDoctorInfo: - """Parsed from refs/5.15 result footer line ``医生信息:...``.""" + """Parsed from algorithm_subprocesses/5.15 result footer line ``医生信息:...``.""" doctor_id: str doctor_name: str | None @@ -362,7 +362,7 @@ _DOCTOR_ID_ONLY_RE = re.compile( def parse_reference_doctor_info(path: Path) -> ReferenceDoctorInfo | None: - """Read ``医生信息:姓名 (id=...)`` footer appended by refs/5.15 orchestrator.""" + """Read ``医生信息:姓名 (id=...)`` footer appended by algorithm_subprocesses/5.15 orchestrator.""" if not path.is_file(): return None @@ -421,7 +421,7 @@ def parse_reference_doctor_info(path: Path) -> ReferenceDoctorInfo | None: def is_reference_result_complete(path: Path) -> bool: - """True when refs/5.15 orchestrator has finished writing the TSV (incl. footer).""" + """True when algorithm_subprocesses/5.15 orchestrator has finished writing the TSV (incl. footer).""" if not path.is_file() or path.stat().st_size <= 0: return False @@ -911,7 +911,7 @@ class VideoBatchRunner: config_path=run_files.config_path.resolve(), ) logger.info( - "reference batch starting for surgery_id={} (refs main.py, work_dir={})", + "reference batch starting for surgery_id={} (algorithm_subprocesses main.py, work_dir={})", surgery_id, cache_work_dir, ) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 24e1ada..e0a2a09 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -34,9 +34,9 @@ dependencies = [ "scipy>=1.14.0", "pandas>=2.2.0", "openpyxl>=3.1.0", - # refs/5-6-code ActionFormer eval.py 导入 libs.datasets → anet.py 需要 h5py + # algorithm_subprocesses/5-6-code ActionFormer eval.py 导入 libs.datasets → anet.py 需要 h5py "h5py>=3.13.0", - # refs actionformer libs/utils/metrics.py(随 datasets 链路导入) + # algorithm_subprocesses actionformer libs/utils/metrics.py(随 datasets 链路导入) "joblib>=1.4.2", "mediapipe>=0.10.35", ] diff --git a/backend/tests/test_video_batch_runner.py b/backend/tests/test_video_batch_runner.py index 7de64ef..3604515 100644 --- a/backend/tests/test_video_batch_runner.py +++ b/backend/tests/test_video_batch_runner.py @@ -272,13 +272,13 @@ def test_build_reference_visualization_command_uses_hand_model_and_result_tsv( def test_build_reference_command_uses_5_15_main_py(tmp_path: Path) -> None: cmd = build_reference_command( - bundle_dir=tmp_path / "refs" / "5.15", + bundle_dir=tmp_path / "algorithm_subprocesses" / "5.15", config_path=tmp_path / "config.yaml", ) assert cmd[:3] == ["uv", "run", "python"] assert cmd[3:5] == ["-X", "faulthandler"] - assert cmd[5].endswith("refs/5.15/main.py") + assert cmd[5].endswith("algorithm_subprocesses/5.15/main.py") assert cmd[6:] == ["--config", str(tmp_path / "config.yaml")] @@ -286,7 +286,7 @@ def test_video_batch_runner_uses_reference_bundle_relative_env_override( tmp_path: Path, monkeypatch, ) -> None: - bundle = tmp_path / "refs" / "custom" + bundle = tmp_path / "algorithm_subprocesses" / "custom" _write_minimal_reference_bundle(bundle) video = tmp_path / "case.mp4" video.write_bytes(b"same-video") @@ -305,7 +305,7 @@ def test_video_batch_runner_uses_reference_bundle_relative_env_override( output.write_text(_complete_result_tsv_body(), encoding="utf-8") return _Proc() - monkeypatch.setenv("REFERENCE_BUNDLE_RELATIVE", "refs/custom") + monkeypatch.setenv("REFERENCE_BUNDLE_RELATIVE", "algorithm_subprocesses/custom") monkeypatch.setattr(reference_bundle_runtime, "REPO_ROOT", tmp_path) monkeypatch.setattr("app.services.video_batch_runner.subprocess.run", fake_run) diff --git a/clients/demo-client/index.html b/clients/demo-client/index.html index caad4f0..c67eef0 100755 --- a/clients/demo-client/index.html +++ b/clients/demo-client/index.html @@ -324,7 +324,7 @@

@@ -549,7 +549,7 @@ player.onloadeddata = () => { if (hint) { hint.innerHTML = - `已加载手部打框+耗材标签视频(refs/5.15 visualize_result_video.py):` + + `已加载手部打框+耗材标签视频(algorithm_subprocesses/5.15 visualize_result_video.py):` + `${path}`; } }; diff --git a/clients/demo-client/labels.yaml b/clients/demo-client/labels.yaml index 21cc880..eb56c60 100644 --- a/clients/demo-client/labels.yaml +++ b/clients/demo-client/labels.yaml @@ -1,4 +1,4 @@ -# 与训练 data.yaml 类名一致;label_id 来自 refs/商品信息表.xlsx(商品名称→产品编码;同名多规格以 / 连接)。 +# 与训练 data.yaml 类名一致;label_id 来自 algorithm_subprocesses 离线包内商品信息表.xlsx(商品名称→产品编码;同名多规格以 / 连接)。 # 推理时以权重内嵌 names 为准;本文件供业务 label_id 与开录空 candidate 全量类名。 names: 0: MCuⅡ功能性宫内节育器