Spaces:
Running on Zero
Running on Zero
RootScope v4: manuscript model (LightGBM per round, 3 seeds, layer + soft-neighbor context, radial prior)
47c4bf8 verified | """ | |
| High-level Python API for Rootscope. | |
| from rootscope import predict_tif, predict_folder | |
| df = predict_tif("image.tif", out_dir="results/", gpu=True) | |
| df = predict_folder("my_tifs/", out_dir="results/", gpu=True) | |
| Both return a pandas DataFrame of per-cell predictions and also write | |
| per-image CSVs + labeled overlay PNGs into ``out_dir``. | |
| """ | |
| from pathlib import Path | |
| import pandas as pd | |
| from . import predict as _predict | |
| from .weights import is_v4, resolve_cnn_weights, resolve_model_dir | |
| def _load(model_dir=None, cnn_weights=None): | |
| """Resolve weights for the selected model version. | |
| v4 returns ``(model_dir, None, None, None, cnn)``: its classifiers are | |
| loaded inside the v4 stage (one bundle per seed, each carrying its own | |
| scaler and feature list), so there is nothing to pre-load here. | |
| """ | |
| mdir = resolve_model_dir(model_dir) | |
| cnn = resolve_cnn_weights(cnn_weights) | |
| if is_v4(): | |
| return mdir, None, None, None, cnn | |
| models_dict, scalers, feature_cols, le = _predict.load_models(str(mdir)) | |
| if not models_dict: | |
| raise RuntimeError(f"No usable models found in {mdir}.") | |
| return models_dict, scalers, feature_cols, le, cnn | |
| def _gpu_available(): | |
| try: | |
| import torch | |
| return bool(torch.cuda.is_available()) | |
| except Exception: # noqa: BLE001 | |
| return False | |
| def _run_one(tif, loaded, out_dir, gpu, um_per_px, max_rounds, label_cells): | |
| if gpu is None: # library default: use a GPU if there is one | |
| gpu = _gpu_available() | |
| models_dict, scalers, feature_cols, le, cnn = loaded | |
| if is_v4(): | |
| return _predict.predict_single_tif_v4( | |
| str(tif), model_dir=str(models_dict), um_per_px=um_per_px, gpu=gpu, | |
| out_dir=str(out_dir), max_rounds=max_rounds, | |
| cnn_weights=str(cnn) if cnn else None, label_cells=label_cells, | |
| ) | |
| return _predict.predict_single_tif( | |
| str(tif), models_dict, scalers, feature_cols, le, | |
| um_per_px=um_per_px, gpu=gpu, out_dir=str(out_dir), | |
| max_rounds=max_rounds, cnn_weights=str(cnn) if cnn else None, | |
| label_cells=label_cells, | |
| ) | |
| def predict_tif( | |
| tif, | |
| out_dir="results", | |
| gpu=None, | |
| model_dir=None, | |
| cnn_weights=None, | |
| um_per_px=None, | |
| max_rounds=None, | |
| label_cells=False, | |
| ): | |
| """Segment + predict cell types for a single TIFF. Returns a DataFrame. | |
| ``um_per_px=None`` reads the pixel size from the TIFF's OME metadata and | |
| fails loudly if it is absent -- a wrong scale distorts every size feature. | |
| ``max_rounds=None`` uses the version's own setting (6 for v4, 10 for v2). | |
| """ | |
| loaded = _load(model_dir, cnn_weights) | |
| return _run_one(tif, loaded, out_dir, gpu, um_per_px, | |
| _predict.default_rounds(max_rounds), label_cells) | |
| def predict_folder( | |
| tif_dir, | |
| out_dir="results", | |
| gpu=None, | |
| model_dir=None, | |
| cnn_weights=None, | |
| um_per_px=None, | |
| max_rounds=None, | |
| label_cells=False, | |
| pattern="*.tif", | |
| ): | |
| """Segment + predict for every TIFF in a folder. Returns a combined | |
| DataFrame and writes ``all_predictions.csv`` into ``out_dir``.""" | |
| loaded = _load(model_dir, cnn_weights) | |
| rounds = _predict.default_rounds(max_rounds) | |
| tif_paths = sorted(Path(tif_dir).glob(pattern)) | |
| if not tif_paths: | |
| raise FileNotFoundError(f"No files matching {pattern} in {tif_dir}") | |
| tables = [] | |
| for tp in tif_paths: | |
| try: | |
| df = _run_one(tp, loaded, out_dir, gpu, um_per_px, rounds, label_cells) | |
| if df is not None: | |
| tables.append(df) | |
| except Exception as e: # noqa: BLE001 | |
| print(f" FAILED on {tp.name}: {e}") | |
| if not tables: | |
| return None | |
| combined = pd.concat(tables, ignore_index=True) | |
| out = Path(out_dir) | |
| out.mkdir(parents=True, exist_ok=True) | |
| combined.to_csv(out / "all_predictions.csv", index=False) | |
| return combined | |