From af2bd97190d2491a04a3ec91c98ee2fb8419beb1 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 22:07:00 +0000
Subject: [PATCH 1/2] [pre-commit.ci] pre-commit suggestions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/pre-commit/pre-commit-hooks: v5.0.0 → v6.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v5.0.0...v6.0.0)
- https://github.com/psf/black → https://github.com/psf/black-pre-commit-mirror
- [github.com/psf/black-pre-commit-mirror: 24.10.0 → 26.5.1](https://github.com/psf/black-pre-commit-mirror/compare/24.10.0...26.5.1)
- [github.com/pycqa/isort: 5.13.2 → 9.0.0a3](https://github.com/pycqa/isort/compare/5.13.2...9.0.0a3)
- [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.15.20](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.15.20)
---
.pre-commit-config.yaml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a7b501f..882f833 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ ci:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
@@ -26,18 +26,18 @@ repos:
args: ['--autofix', '--no-sort-keys', '--indent=4']
- id: end-of-file-fixer
- id: mixed-line-ending
- - repo: https://github.com/psf/black
- rev: "24.10.0"
+ - repo: https://github.com/psf/black-pre-commit-mirror
+ rev: "26.5.1"
hooks:
- id: black
- id: black-jupyter
- repo: https://github.com/pycqa/isort
- rev: 5.13.2
+ rev: 9.0.0a3
hooks:
- id: isort
args: ["--profile", "black"]
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.8.6
+ rev: v0.15.20
hooks:
- id: ruff
args: ['--fix']
From 308060760f4aa588c24cb8f9b859823bbd0dd4ac Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 6 Jul 2026 22:08:11 +0000
Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---
vista3d/README.md | 20 ++++++++---------
vista3d/README_research.md | 2 +-
vista3d/cvpr_workshop/infer_cvpr.py | 1 +
vista3d/cvpr_workshop/train_cvpr.py | 30 ++++++++++++++++---------
vista3d/scripts/huggingface_download.py | 18 ++++++++++-----
5 files changed, 45 insertions(+), 26 deletions(-)
diff --git a/vista3d/README.md b/vista3d/README.md
index 71deffe..68301e3 100644
--- a/vista3d/README.md
+++ b/vista3d/README.md
@@ -18,7 +18,7 @@ limitations under the License.
## News!
-[10/27/2025] We release NV-Segment-CTMR, a joint CT-MR automatic segmentation model trained on over 30K CT and MRI scans, supporting over 300 classes.
+[10/27/2025] We release NV-Segment-CTMR, a joint CT-MR automatic segmentation model trained on over 30K CT and MRI scans, supporting over 300 classes.
[03/12/2025] We provide VISTA3D as a baseline for the challenge "CVPR 2025: Foundation Models for Interactive 3D Biomedical Image Segmentation"([link](https://www.codabench.org/competitions/5263/)). The simplified code based on MONAI 1.4 is provided in the [here](./cvpr_workshop/).
@@ -40,8 +40,8 @@ limitations under the License.
| **License** | [Commercial Friendly](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license/) | Same as VISTA3D | [Non-Commercial](https://developer.download.nvidia.com/licenses/NVIDIA-OneWay-Noncommercial-License-22Mar2022.pdf?t=eyJscyI6InJlZiIsImxzZCI6IlJFRi1naXRodWIuY29tL252aWRpYS1ob2xvc2NhbiJ9) |
```
-We recommend users to use NV-Segment-CTMR for large scale automatic segmentation for CT and MRI scans because it is trained with large and diverse datasets. For CT tumor or interactive refinement, user should try NV-Segment-CT.
-```
+We recommend users to use NV-Segment-CTMR for large scale automatic segmentation for CT and MRI scans because it is trained with large and diverse datasets. For CT tumor or interactive refinement, user should try NV-Segment-CT.
+```
**VISTA3D/NV-Segment-CT** ([`Paper`](https://arxiv.org/pdf/2406.05285)) is a foundation model trained systematically on 11,454 volumes encompassing 127 types of human anatomical structures and various lesions. The model provides State-of-the-art performances on:
@@ -93,22 +93,22 @@ python -m monai.bundle run --config_file="['configs/inference.json', 'configs/ba
# Automatic Batch segmentation for the whole folder with multi-gpu support. mgpu_inference.json is below. change nproc_per_node to your GPU number.
torchrun --nproc_per_node=2 --nnodes=1 -m monai.bundle run --config_file="['configs/inference.json', 'configs/batch_inference.json', 'configs/mgpu_inference.json']" --input_dir="example/" --output_dir="example/"
```
-#### Interactive segmentation
+#### Interactive segmentation
```bash
# Points must be three dimensional (x,y,z) in the shape of [[x,y,z],...,[x,y,z]]. Point labels can only be -1(ignore), 0(negative), 1(positive) and 2(negative for special overlaped class like tumor), 3(positive for special class). Only supporting 1 class per inference. The output 255 represents NaN value which means not processed region.
cd NV-Segment-CT
python -m monai.bundle run --config_file configs/inference.json --input_dict "{'image':'example/spleen_03.nii.gz','points':[[128,128,16], [100,100,16]],'point_labels':[1, 0]}"
```
-**NOTE** MONAI bundle accepts multiple json config files and input arguments. The latter configs/arguments will overide the previous configs/arguments if they have overlapping keys.
+**NOTE** MONAI bundle accepts multiple json config files and input arguments. The latter configs/arguments will overide the previous configs/arguments if they have overlapping keys.
## 1.2 **NV-Segment-CTMR**[[Github]](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/tree/main/NV-Segment-CTMR)[[Huggingface]](https://huggingface.co/nvidia/NV-Segment-CTMR/tree/main)
-Please read the complete usage in the NV-Segment-CTMR [[Github]](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/tree/main/NV-Segment-CTMR) repo.
+Please read the complete usage in the NV-Segment-CTMR [[Github]](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/tree/main/NV-Segment-CTMR) repo.
-We defined 345 classes as in [metadata.json](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/blob/main/NV-Segment-CTMR/configs/metadata.json) and details in [label_dict.json](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/blob/main/NV-Segment-CTMR/configs/label_dict.json). It shows the label organ name, index, training dataset, modality and evaluation dice score. If a class only comes from CT training dataset, it may not perform well on MRI, but the actual performance will vary case by case. We support three type of segment everything "CT_BODY", "MRI_BODY", and "MRI_BRAIN".
+We defined 345 classes as in [metadata.json](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/blob/main/NV-Segment-CTMR/configs/metadata.json) and details in [label_dict.json](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/blob/main/NV-Segment-CTMR/configs/label_dict.json). It shows the label organ name, index, training dataset, modality and evaluation dice score. If a class only comes from CT training dataset, it may not perform well on MRI, but the actual performance will vary case by case. We support three type of segment everything "CT_BODY", "MRI_BODY", and "MRI_BRAIN".
-- "CT_BODY" is the previous VISTA3D bundle supported 132 CT classes. Same as NV-Segment-CT everything prompts.
-- "MRI_BODY" shares the same 50 label class as TotalsegmentatorMR.
+- "CT_BODY" is the previous VISTA3D bundle supported 132 CT classes. Same as NV-Segment-CT everything prompts.
+- "MRI_BODY" shares the same 50 label class as TotalsegmentatorMR.
- "MRI_BRAIN" is trained on skull stripped [LUMIR](https://github.com/JHU-MedImage-Reg/LUMIR_L2R) dataset and will segment 133 brain MRI substructures. We followed [MIR Preprocessing](https://github.com/junyuchen245/MIR/tree/main/tutorials/brain_MRI_preprocessing) tutorials and put the corresponding components into this repo. `All contrasts of brain MRI are supported`
### Quick Start
@@ -144,7 +144,7 @@ For local `scripts.infer` usage in this research code, inference now prepares th
## 3. VISTA3D results postprocessing with [ShapeKit](https://arxiv.org/pdf/2506.24003)
VISTA3D is trained with binary segmentation, and may produce false positives due to weak false positive supervision. ShapeKit solves this problem with sophisticated postprocessing. ShapeKit requires segmentation mask for each class. VISTA3D by default performs argmax and collaps overlapping classes. Change the `monai.apps.vista3d.transforms.VistaPostTransformd` in `inference.json` to save each class segmentation as a separate channel. Then follow [ShapeKit](https://github.com/BodyMaps/ShapeKit) codebase for processing.
```json
-{
+{
"_target_": "Activationsd",
"sigmoid": true,
"keys": "pred"
diff --git a/vista3d/README_research.md b/vista3d/README_research.md
index df015ef..95bed29 100644
--- a/vista3d/README_research.md
+++ b/vista3d/README_research.md
@@ -80,7 +80,7 @@ To segment everything, run
```bash
export CUDA_VISIBLE_DEVICES=0; python -m scripts.infer --config_file 'configs/infer.yaml' - infer_everything --image_file 'example-1.nii.gz'
```
-To segment based on point clicks, provide `point` and `point_label`.
+To segment based on point clicks, provide `point` and `point_label`.
```bash
export CUDA_VISIBLE_DEVICES=0; python -m scripts.infer --config_file 'configs/infer.yaml' - infer --image_file 'example-1.nii.gz' --point "[[128,128,16],[100,100,6]]" --point_label "[1,0]" --save_mask true
```
diff --git a/vista3d/cvpr_workshop/infer_cvpr.py b/vista3d/cvpr_workshop/infer_cvpr.py
index 61d3536..0b3d7ef 100755
--- a/vista3d/cvpr_workshop/infer_cvpr.py
+++ b/vista3d/cvpr_workshop/infer_cvpr.py
@@ -16,6 +16,7 @@
from train_cvpr import ROI_SIZE
+
def convert_clicks(alldata):
# indexes = list(alldata.keys())
# data = [alldata[i] for i in indexes]
diff --git a/vista3d/cvpr_workshop/train_cvpr.py b/vista3d/cvpr_workshop/train_cvpr.py
index ac099f6..2bfac66 100755
--- a/vista3d/cvpr_workshop/train_cvpr.py
+++ b/vista3d/cvpr_workshop/train_cvpr.py
@@ -22,7 +22,8 @@
import matplotlib.pyplot as plt
NUM_PATCHES_PER_IMAGE = 2
-ROI_SIZE= [128, 128, 128]
+ROI_SIZE = [128, 128, 128]
+
def plot_to_tensorboard(writer, epoch, inputs, labels, points, outputs):
"""
@@ -109,7 +110,7 @@ def __getitem__(self, idx):
keys=["image", "label"],
label_key="label",
num_classes=label.max() + 1,
- ratios=tuple(float(i > 0) for i in range(label.max()+1)),
+ ratios=tuple(float(i > 0) for i in range(label.max() + 1)),
num_samples=NUM_PATCHES_PER_IMAGE,
),
monai.transforms.RandScaleIntensityd(
@@ -137,17 +138,19 @@ def __getitem__(self, idx):
mode=["constant", "constant"],
keys=["image", "label"],
spatial_size=ROI_SIZE,
- )
+ ),
]
)
data = transforms(data)
return data
+
import re
+
def get_latest_epoch(directory):
# Pattern to match filenames like 'model_epoch.pth'
- pattern = re.compile(r'model_epoch(\d+)\.pth')
+ pattern = re.compile(r"model_epoch(\d+)\.pth")
max_epoch = -1
for filename in os.listdir(directory):
@@ -159,6 +162,7 @@ def get_latest_epoch(directory):
return max_epoch if max_epoch != -1 else None
+
# Training function
def train():
json_file = "allset.json" # Update with your JSON file
@@ -169,7 +173,6 @@ def train():
start_epoch = get_latest_epoch(checkpoint_dir)
start_checkpoint = "./CPRR25_vista3D_model_final_10percent_data.pth"
-
os.makedirs(checkpoint_dir, exist_ok=True)
dist.init_process_group(backend="nccl")
world_size = int(os.environ["WORLD_SIZE"])
@@ -189,11 +192,12 @@ def train():
model.load_state_dict(pretrained_ckpt, strict=True)
else:
print(f"Resuming from epoch {start_epoch}")
- pretrained_ckpt = torch.load(os.path.join(checkpoint_dir, f"model_epoch{start_epoch}.pth"))
- model.load_state_dict(pretrained_ckpt['model'], strict=True)
+ pretrained_ckpt = torch.load(
+ os.path.join(checkpoint_dir, f"model_epoch{start_epoch}.pth")
+ )
+ model.load_state_dict(pretrained_ckpt["model"], strict=True)
model = DDP(model, device_ids=[local_rank], find_unused_parameters=True)
-
optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=1.0e-05)
lr_scheduler = monai.optimizers.WarmupCosineSchedule(
optimizer=optimizer,
@@ -265,10 +269,16 @@ def train():
if local_rank == 0:
writer.add_scalar("loss", loss.item(), step)
if local_rank == 0 and (epoch + 1) % save_interval == 0:
- checkpoint_path = os.path.join(checkpoint_dir, f"model_epoch{epoch + 1}.pth")
+ checkpoint_path = os.path.join(
+ checkpoint_dir, f"model_epoch{epoch + 1}.pth"
+ )
if world_size > 1:
torch.save(
- {"model": model.module.state_dict(), "epoch": epoch + 1, "step": step},
+ {
+ "model": model.module.state_dict(),
+ "epoch": epoch + 1,
+ "step": step,
+ },
checkpoint_path,
)
print(
diff --git a/vista3d/scripts/huggingface_download.py b/vista3d/scripts/huggingface_download.py
index edf97da..c8efbfe 100644
--- a/vista3d/scripts/huggingface_download.py
+++ b/vista3d/scripts/huggingface_download.py
@@ -26,7 +26,9 @@ def touch_huggingface_download_counter(
try:
from huggingface_hub import hf_hub_download
except ImportError:
- print("[vista3d] warning: huggingface_hub is not installed; skipping Hugging Face download counter touch.")
+ print(
+ "[vista3d] warning: huggingface_hub is not installed; skipping Hugging Face download counter touch."
+ )
return None
try:
@@ -37,8 +39,10 @@ def touch_huggingface_download_counter(
revision=revision,
force_download=True,
)
- except Exception as exc: # noqa: BLE001
- print(f"[vista3d] warning: could not touch Hugging Face download counter for {repo_id}/{filename}: {exc}")
+ except Exception as exc:
+ print(
+ f"[vista3d] warning: could not touch Hugging Face download counter for {repo_id}/{filename}: {exc}"
+ )
return None
print(f"[vista3d] touched Hugging Face download counter for {repo_id}/{filename}")
@@ -60,14 +64,18 @@ def prepare_huggingface_checkpoint(
if rank_zero_only and not _is_rank_zero():
return str(local_path)
- touch_huggingface_download_counter(repo_id, counter_filename, revision, rank_zero_only=False)
+ touch_huggingface_download_counter(
+ repo_id, counter_filename, revision, rank_zero_only=False
+ )
if local_path.exists():
return str(local_path)
try:
from huggingface_hub import hf_hub_download
except ImportError as exc:
- raise RuntimeError(f"{local_path} does not exist and huggingface_hub is not installed; cannot download {repo_id}.") from exc
+ raise RuntimeError(
+ f"{local_path} does not exist and huggingface_hub is not installed; cannot download {repo_id}."
+ ) from exc
checkpoint_path = hf_hub_download(
repo_id=repo_id,