|
14 | 14 | from imc.ops.quant import quantify_cells_rois |
15 | 15 | from imc.scripts import build_cli, find_tiffs |
16 | 16 |
|
| 17 | +def _make_anndata(quant, morphology): |
| 18 | + """Convert a quantification DataFrame to an AnnData object.""" |
| 19 | + v = len(str(quant["obj_id"].max())) |
| 20 | + idx = quant["roi"] + "-" + quant["obj_id"].astype(str).str.zfill(v) |
| 21 | + quant.index = idx |
| 22 | + |
| 23 | + cols = [ |
| 24 | + "sample", "roi", "obj_id", "X_centroid", "Y_centroid", "layer", |
| 25 | + "area", "perimeter", "minor_axis_length", "major_axis_length", |
| 26 | + "eccentricity", "solidity" |
| 27 | + ] |
| 28 | + cols = [c for c in cols if c in quant.columns] |
| 29 | + ann = anndata.AnnData( |
| 30 | + quant.drop(cols, axis=1, errors="ignore").astype(float), obs=quant[cols] |
| 31 | + ) |
| 32 | + if "X_centroid" in ann.obs.columns: |
| 33 | + ann.obsm["spatial"] = ann.obs[["Y_centroid", "X_centroid"]].values |
| 34 | + return ann |
| 35 | + |
| 36 | + |
17 | 37 | def main(cli: tp.Sequence[str] = None) -> int: |
18 | 38 | parser = build_cli("quantify") |
19 | 39 | args = parser.parse_args(cli) |
@@ -45,44 +65,39 @@ def main(cli: tp.Sequence[str] = None) -> int: |
45 | 65 | error = f"Not all cell masks exist! Missing:\n\t- {m}" |
46 | 66 | raise ValueError(error) |
47 | 67 |
|
48 | | - quant = quantify_cells_rois( |
49 | | - rois, args.layers.split(","), morphology=args.morphology |
50 | | - ).reset_index() |
51 | | - |
52 | | - # reorder columns for nice effect |
53 | | - ext = ["roi", "obj_id"] + (["X_centroid", "Y_centroid"] if args.morphology else []) |
54 | | - rem = [x for x in quant.columns if x not in ext] |
55 | | - quant = quant[ext + rem] |
56 | | - |
57 | | - if args.output is None: |
58 | | - f = Path("processed").mkdir() / "quantification.csv.gz" |
59 | | - else: |
60 | | - f = args.output |
61 | | - quant.to_csv(f, index=False) |
62 | | - print(f"Wrote CSV file to '{f.absolute()}'.") |
63 | | - |
64 | | - if args.output_h5ad: |
65 | | - v = len(str(quant["obj_id"].max())) |
66 | | - idx = quant["roi"] + "-" + quant["obj_id"].astype(str).str.zfill(v) |
67 | | - quant.index = idx |
68 | | - |
69 | | - # cols = ["sample", "roi", "obj_id", "X_centroid", "Y_centroid", "layer"] |
70 | | - cols = [ |
71 | | - "sample", "roi", "obj_id", "X_centroid", "Y_centroid", "layer", |
72 | | - "area", "perimeter", "minor_axis_length", "major_axis_length", |
73 | | - "eccentricity", "solidity" |
74 | | - ] |
75 | | - cols = [c for c in cols if c in quant.columns] |
76 | | - ann = anndata.AnnData( |
77 | | - quant.drop(cols, axis=1, errors="ignore").astype(float), obs=quant[cols] |
78 | | - ) |
79 | | - if "X_centroid" in ann.obs.columns: |
80 | | - ann.obsm["spatial"] = ann.obs[["Y_centroid", "X_centroid"]].values |
81 | | - f = f.replace_(".csv.gz", ".h5ad") |
82 | | - ann.write(f) |
83 | | - print(f"Wrote h5ad file to '{f.absolute()}'.") |
84 | | - ann2 = anndata.read_h5ad(f) |
85 | | - assert np.allclose(ann.X, ann2.X) |
| 68 | + # Process each TIFF individually so each gets its own output file |
| 69 | + for tiff, roi in zip(args.tiffs, rois): |
| 70 | + print(f"Quantifying '{roi.name}'...") |
| 71 | + quant = quantify_cells_rois( |
| 72 | + [roi], args.layers.split(","), morphology=args.morphology |
| 73 | + ).reset_index() |
| 74 | + |
| 75 | + # reorder columns for nice effect |
| 76 | + ext = ["roi", "obj_id"] + (["X_centroid", "Y_centroid"] if args.morphology else []) |
| 77 | + rem = [x for x in quant.columns if x not in ext] |
| 78 | + quant = quant[ext + rem] |
| 79 | + |
| 80 | + # Determine output path |
| 81 | + if args.output is not None: |
| 82 | + # -o flag: use as output directory, write <roi_name>_quantification files there |
| 83 | + out_dir = Path(args.output).mkdir() |
| 84 | + stem = tiff.stem.replace("_full", "") |
| 85 | + csv_path = out_dir / f"{stem}_quantification.csv.gz" |
| 86 | + else: |
| 87 | + # Default: write next to the input TIFF |
| 88 | + stem = tiff.stem.replace("_full", "") |
| 89 | + csv_path = tiff.parent / f"{stem}_quantification.csv.gz" |
| 90 | + |
| 91 | + quant.to_csv(csv_path, index=False) |
| 92 | + print(f"Wrote CSV file to '{csv_path.absolute()}'.") |
| 93 | + |
| 94 | + if args.output_h5ad: |
| 95 | + ann = _make_anndata(quant, args.morphology) |
| 96 | + h5ad_path = csv_path.replace_(".csv.gz", ".h5ad") |
| 97 | + ann.write(h5ad_path) |
| 98 | + print(f"Wrote h5ad file to '{h5ad_path.absolute()}'.") |
| 99 | + ann2 = anndata.read_h5ad(h5ad_path) |
| 100 | + assert np.allclose(ann.X, ann2.X) |
86 | 101 |
|
87 | 102 | print("Finished quantification step.") |
88 | 103 | return 0 |
|
0 commit comments