Introduce the third segmentation pipeline using MONAI (1.6.x) to allow direct comparison with Pipelines A and B. This includes the implementation of the iterative pseudo-labeling workflow, training scripts, and inference protocols. - Add `scripts_monai/` directory containing the MONAI pipeline scripts. - Update documentation in `README.md` and `AGENTS.md` to include MONAI package requirements and pipeline details. - Configure `.gitignore` to exclude MONAI-specific run directories. - Update data directory descriptions to include MONAI pseudo-labels.
64 lines
No EOL
2.7 KiB
Python
64 lines
No EOL
2.7 KiB
Python
"""Build the training manifest for one pseudo-labeling round.
|
|
|
|
base = labeled patient-level train split (w=1.0) — the same train/val usage as
|
|
Pipeline A: the val split is never trained on (it is the internal-validation
|
|
set). Accepted pseudo-label rows (pos + neg) are appended with w=<pseudo-weight>
|
|
(negative rows point at the zero mask written by stage 03), de-duplicated by
|
|
key (first occurrence wins). Every row must point at existing pimg/plabel
|
|
niftis.
|
|
|
|
Usage:
|
|
python scripts_monai/01_monai_build_rows.py \
|
|
--train data/manifests/split_train.jsonl \
|
|
--accepted data/pseudo_monai/round1/accepted.jsonl \
|
|
--pseudo-weight 0.3 --out data/pseudo_monai/round1_rows.jsonl
|
|
"""
|
|
import argparse
|
|
import os
|
|
import sys
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
from src.common import load_jsonl, save_jsonl
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--train", required=True, help="labeled train split jsonl ({key,pimg,plabel})")
|
|
ap.add_argument("--accepted", action="append", default=None,
|
|
help="accepted pseudo jsonl (repeatable); pos + neg rows")
|
|
ap.add_argument("--pseudo-weight", type=float, default=0.3)
|
|
ap.add_argument("--no-neg", action="store_true", help="drop negative pseudo rows")
|
|
ap.add_argument("--out", required=True)
|
|
args = ap.parse_args()
|
|
|
|
rows, seen = [], set()
|
|
for r in load_jsonl(args.train):
|
|
rows.append({"key": r["key"], "subject": r.get("subject"), "pimg": r["pimg"],
|
|
"plabel": r["plabel"], "w": 1.0, "role": "labeled"})
|
|
seen.add(r["key"])
|
|
n_pos, n_neg = 0, 0
|
|
for f in (args.accepted or []):
|
|
for r in load_jsonl(f):
|
|
if not r.get("label") or r["key"] in seen:
|
|
continue
|
|
if r["role"] == "neg" and args.no_neg:
|
|
continue
|
|
seen.add(r["key"])
|
|
rows.append({"key": r["key"], "subject": r.get("subject"), "pimg": r["pimg"],
|
|
"plabel": r["label"], "w": args.pseudo_weight, "role": r["role"]})
|
|
if r["role"] == "pos":
|
|
n_pos += 1
|
|
else:
|
|
n_neg += 1
|
|
missing = [r["key"] for r in rows
|
|
if not (os.path.exists(r["pimg"]) and os.path.exists(r["plabel"]))]
|
|
if missing:
|
|
raise FileNotFoundError(f"{len(missing)} rows missing pimg/plabel, e.g. {missing[:3]}")
|
|
save_jsonl(rows, args.out)
|
|
n_labeled = len(rows) - n_pos - n_neg
|
|
print(f"[monai:rows] {args.out}: total={len(rows)} labeled={n_labeled} pos={n_pos} neg={n_neg}",
|
|
flush=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |