Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 13 additions & 148 deletions NV-Segment-CT/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@ conda activate vista3d-nv
git clone https://github.com/NVIDIA-Medtech/NV-Segment-CTMR.git
cd NV-Segment-CTMR/NV-Segment-CT;
pip install -r requirements.txt;
cd ..;
mkdir NV-Segment-CT/models;
# download from huggingface link
hf download nvidia/NV-Segment-CT vista3d_pretrained_model/model.pt --local-dir NV-Segment-CT/models/ && \
mv NV-Segment-CT/models/vista3d_pretrained_model/model.pt NV-Segment-CT/models/model.pt && \
rmdir NV-Segment-CT/models/vista3d_pretrained_model

mkdir -p models
# Option 1: Download using hf and move to expected location
hf download nvidia/NV-Segment-CT --local-dir models/ && \
mv models/vista3d_pretrained_model/model.pt models/model.pt
Comment on lines +20 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Empty vista3d_pretrained_model/ directory left behind after mv

After downloading with hf download nvidia/NV-Segment-CT --local-dir models/, HuggingFace Hub places the file at models/vista3d_pretrained_model/model.pt. The mv moves the file but the now-empty directory is never removed. Consider re-adding the cleanup step:

Suggested change
hf download nvidia/NV-Segment-CT --local-dir models/ && \
mv models/vista3d_pretrained_model/model.pt models/model.pt
hf download nvidia/NV-Segment-CT --local-dir models/ && \
mv models/vista3d_pretrained_model/model.pt models/model.pt && \
rmdir models/vista3d_pretrained_model

The same applies to NV-Segment-CTMR/docs/README.md line 41.

```

## 1.1 **NV-Segment-CT** [[Github]](https://github.com/NVIDIA-Medtech/NV-Segment-CTMR/tree/main/NV-Segment-CT) [[Huggingface]](https://huggingface.co/nvidia/NV-Segment-CT)
Expand All @@ -40,7 +39,9 @@ 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/"
```

```
Note: For more details about batch processing, please refer to NV-Segment-CTMR readme.md
```
Comment on lines 41 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing blank line between adjacent fenced code blocks

The closing triple backtick of the bash code block is immediately followed by another opening triple backtick with no blank line in between. Consider using a blockquote note instead for cleaner rendering.

Suggested change
```
```
Note: For more details about batch processing, please refer to NV-Segment-CTMR readme.md
```
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/"

Note: For more details about batch processing, please refer to NV-Segment-CTMR readme.md


<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

### Interactive segmentation

```bash
Expand All @@ -66,149 +67,13 @@ For more details, please refer to [this](inference.md).

## Continual learning / Finetuning

### Step1: Generate Data json file

Users need to provide a json data split for continuous learning (`configs/msd_task09_spleen_folds.json` from the [MSD](http://medicaldecathlon.com/) is provided as an example). The data split should meet the following format ('testing' labels are optional):

```json
{
"training": [
{"image": "img0001.nii.gz", "label": "label0001.nii.gz", "fold": 0},
{"image": "img0002.nii.gz", "label": "label0002.nii.gz", "fold": 2},
...
],
"testing": [
{"image": "img0003.nii.gz", "label": "label0003.nii.gz"},
{"image": "img0004.nii.gz", "label": "label0004.nii.gz"},
...
]
}
```

Example code for 5 fold cross-validation generation can be found [here](data.md)

```text
Note the data is not the absolute path to the image and label file. The actual image file will be `os.path.join(dataset_dir, data["training"][item]["image"])`, where `dataset_dir` is defined in `configs/train_continual.json`. Also 5-fold cross-validation is not required! `fold=0` is defined in train.json, which means any data item with fold==0 will be used as validation and other fold will be used for training. So if you only have train/val split, you can manually set validation data with "fold": 0 in its datalist and the other to be training by setting "fold" to any number other than 0.
```

### Step2: Changing hyperparameters

For continual learning, user can change `configs/train_continual.json`. More advanced users can change configurations in `configs/train.json`. Most hyperparameters are straighforward and user can tell based on their names. The users must manually change the following keys in `configs/train_continual.json`.

#### 1. `label_mappings`

```json
"label_mappings": {
"default": [
[
index_1_in_user_data, # e.g. 1
mapped_index_1, # e.g. 1
],
[
index_2_in_user_data, # e.g. 2
mapped_index_2, # e.g. 2
], ...,
[
index_last_in_user_data, # e.g. N
mapped_index_N, # e.g. N
]
]
},
```

`index_1_in_user_data`,...,`index_N_in_user_data` is the class index value in the groundtruth that user tries to segment. `mapped_index_1`,...,`mapped_index_N` is the mapped index value that the bundle will output. You can make these two the same for finetuning, but we suggest finding the semantic relevant mappings from our unified [global label index](../configs/metadata.json). For example, "Spleen" in MSD09 groundtruth label is represented by 1, but "Spleen" is 3 in `docs/labels.json`. So by defining label mapping `[[1, 3]]`, VISTA3D can segment "Spleen" using its pretrained weights out-of-the-box, and can speed up the finetuning convergence speed.
If you cannot find a relevant semantic label for your class, just use any value < `num_classes` defined in train_continue.json.
For more details about this label_mapping, please read [this](finetune.md).

#### 2. `data_list_file_path` and `dataset_dir`

Change `data_list_file_path` to the absolute path of your data json split. Change `dataset_dir` to the root folder that combines with the relative path in the data json split.

#### 3. Optional hyperparameters and details are [here](finetune.md)

Hyperparameteers finetuning is important and varies from task to task.

### Step3: Run finetuning

The hyperparameters in `configs/train_continual.json` will overwrite ones in `configs/train.json`. Configs in the back will overide the previous ones if they have the same key.

Single-GPU:

```bash
python -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json']"
```

Multi-GPU:

```bash
torchrun --nnodes=1 --nproc_per_node=8 -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/multi_gpu_train.json']"
```

#### MLFlow Visualization

MLFlow is enabled by default (defined in train.json, use_mlflow) and the data is stored in the `mlruns/` folder under the bundle's root directory. To launch the MLflow UI and track your experiment data, follow these steps:

1. Open a terminal and navigate to the root directory of your bundle where the `mlruns/` folder is located.

2. Execute the following command to start the MLflow server. This will make the MLflow UI accessible.

```bash
mlflow ui
```

## Evaluation

Evaluation can be used to calculate dice scores for the model or a finetuned model. Change the `ckpt_path` to the checkpoint you wish to evaluate. The dice score is calculated on the original image spacing using `invertd`, while the dice score during finetuning is calculated on resampled space.

```text
NOTE: Evaluation does not support point evaluation.`"validate#evaluator#hyper_kwargs#val_head` is always set to `auto`.
We provide predefined finetuning tutorial in [details](inference.md).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Finetuning link points to wrong file

[details](inference.md) links to NV-Segment-CT/docs/inference.md, which covers inference configuration — not the finetuning tutorial. The detailed finetuning steps (data preparation, hyperparameters, multi-GPU training, evaluation, FAQ) were explicitly moved to finetune.md in this PR. Users following this link will be misdirected.

Suggested change
We provide predefined finetuning tutorial in [details](inference.md).
We provide predefined finetuning tutorial in [details](finetune.md).

For complicated finetuning, we suggest users to do vibe coding to generate finetuning pipelines by simply reuse the model and checkpoint
```python
from monai.networks.nets.vista3d import vista3d132
vista3d132.load_state_dict(pretrained_ckpt, strict=True)
```

Single-GPU:

```bash
python -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/evaluate.json']"
```

Multi-GPU:

```bash
torchrun --nnodes=1 --nproc_per_node=8 -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/evaluate.json','configs/mgpu_evaluate.json']"
```

### Other explanatory items

The `label_mapping` in `evaluation.json` does not include `0` because the postprocessing step performs argmax (`VistaPostTransformd`), and a `0` prediction would negatively impact performance. In continuous learning, however, `0` is included for validation because no argmax is performed, and validation is done channel-wise (include_background=False). Additionally, `Relabeld` in `postprocessing` is required to map `label` and `pred` back to sequential indexes like `0, 1, 2, 3, 4` for dice calculation, as they are not in one-hot format. Evaluation does not support `point`, but finetuning does, as it does not perform argmax.

## FAQ

### TroubleShoot for Out-of-Memory

- Changing `patch_size` to a smaller value such as `"patch_size": [96, 96, 96]` would reduce the training/inference memory footprint.
- Changing `train_dataset_cache_rate` and `val_dataset_cache_rate` to a smaller value like `0.1` can solve the out-of-cpu memory issue when using huge finetuning dataset.
- Set `"postprocessing#transforms#0#_disabled_": false` to move the postprocessing to cpu to reduce the GPU memory footprint.

### Multi-channel input

- Change `input_channels` in `train.json` to your desired channel number
- Data split json can be a single multi-channel image or can be a list of single channeled images. Those images must have the same spatial shape and aligned/registered.

```json
{
"image": ["modality1.nii.gz", "modality2.nii.gz", "modality3.nii.gz"]
"label": "label.nii.gz"
},
```

### Wrong inference results from finetuned checkpoint

- Make sure you removed the `subclass` dictionary from inference.json if you ever mapped local index to [2,20,21]
- Make sure `0` is not included in your inference prompt for automatic segmentation.

## References
- He, Yufan, et al. "VISTA3D: A unified segmentation foundation model for 3D medical imaging." Proceedings of the Computer Vision and Pattern Recognition Conference. 2025. <https://openaccess.thecvf.com/content/CVPR2025/html/He_VISTA3D_A_Unified_Segmentation_Foundation_Model_For_3D_Medical_Imaging_CVPR_2025_paper.html>

Expand Down
144 changes: 144 additions & 0 deletions NV-Segment-CT/docs/finetune.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,149 @@
# Finetune configurations

### Step1: Generate Data json file

Users need to provide a json data split for continuous learning (`configs/msd_task09_spleen_folds.json` from the [MSD](http://medicaldecathlon.com/) is provided as an example). The data split should meet the following format ('testing' labels are optional):

```json
{
"training": [
{"image": "img0001.nii.gz", "label": "label0001.nii.gz", "fold": 0},
{"image": "img0002.nii.gz", "label": "label0002.nii.gz", "fold": 2},
...
],
"testing": [
{"image": "img0003.nii.gz", "label": "label0003.nii.gz"},
{"image": "img0004.nii.gz", "label": "label0004.nii.gz"},
...
]
}
```

Example code for 5 fold cross-validation generation can be found [here](data.md)

```text
Note the data is not the absolute path to the image and label file. The actual image file will be `os.path.join(dataset_dir, data["training"][item]["image"])`, where `dataset_dir` is defined in `configs/train_continual.json`. Also 5-fold cross-validation is not required! `fold=0` is defined in train.json, which means any data item with fold==0 will be used as validation and other fold will be used for training. So if you only have train/val split, you can manually set validation data with "fold": 0 in its datalist and the other to be training by setting "fold" to any number other than 0.
```

### Step2: Changing hyperparameters

For continual learning, user can change `configs/train_continual.json`. More advanced users can change configurations in `configs/train.json`. Most hyperparameters are straighforward and user can tell based on their names. The users must manually change the following keys in `configs/train_continual.json`.

#### 1. `label_mappings`

```json
"label_mappings": {
"default": [
[
index_1_in_user_data, # e.g. 1
mapped_index_1, # e.g. 1
],
[
index_2_in_user_data, # e.g. 2
mapped_index_2, # e.g. 2
], ...,
[
index_last_in_user_data, # e.g. N
mapped_index_N, # e.g. N
]
]
},
```

`index_1_in_user_data`,...,`index_N_in_user_data` is the class index value in the groundtruth that user tries to segment. `mapped_index_1`,...,`mapped_index_N` is the mapped index value that the bundle will output. You can make these two the same for finetuning, but we suggest finding the semantic relevant mappings from our unified [global label index](../configs/metadata.json). For example, "Spleen" in MSD09 groundtruth label is represented by 1, but "Spleen" is 3 in `docs/labels.json`. So by defining label mapping `[[1, 3]]`, VISTA3D can segment "Spleen" using its pretrained weights out-of-the-box, and can speed up the finetuning convergence speed.
If you cannot find a relevant semantic label for your class, just use any value < `num_classes` defined in train_continue.json.
For more details about this label_mapping, please read [this](finetune.md).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Self-referential links after content was moved from README

Both this line and line 61 contain links that point back to finetune.md — the file they are already in. These were originally cross-references from README.md that said "see finetune.md for details", but when the content was moved here, the link targets were not updated. Clicking them just reloads the current page with no useful navigation.

Line 55:

Suggested change
For more details about this label_mapping, please read [this](finetune.md).
For more details about this label_mapping, please read the [Configurations section below](#configurations).

Line 61 (#### 3. Optional hyperparameters and details are [here](finetune.md)) should similarly be updated to reference the Configurations section within this file, or the link can simply be removed since the reader is already in finetune.md.


#### 2. `data_list_file_path` and `dataset_dir`

Change `data_list_file_path` to the absolute path of your data json split. Change `dataset_dir` to the root folder that combines with the relative path in the data json split.

#### 3. Optional hyperparameters and details are [here](finetune.md)

Hyperparameteers finetuning is important and varies from task to task.

### Step3: Run finetuning

The hyperparameters in `configs/train_continual.json` will overwrite ones in `configs/train.json`. Configs in the back will overide the previous ones if they have the same key.

Single-GPU:

```bash
python -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json']"
```

Multi-GPU:

```bash
torchrun --nnodes=1 --nproc_per_node=8 -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/multi_gpu_train.json']"
```

#### MLFlow Visualization

MLFlow is enabled by default (defined in train.json, use_mlflow) and the data is stored in the `mlruns/` folder under the bundle's root directory. To launch the MLflow UI and track your experiment data, follow these steps:

1. Open a terminal and navigate to the root directory of your bundle where the `mlruns/` folder is located.

2. Execute the following command to start the MLflow server. This will make the MLflow UI accessible.

```bash
mlflow ui
```

## Evaluation

Evaluation can be used to calculate dice scores for the model or a finetuned model. Change the `ckpt_path` to the checkpoint you wish to evaluate. The dice score is calculated on the original image spacing using `invertd`, while the dice score during finetuning is calculated on resampled space.

```text
NOTE: Evaluation does not support point evaluation.`"validate#evaluator#hyper_kwargs#val_head` is always set to `auto`.
```

Single-GPU:

```bash
python -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/evaluate.json']"
```

Multi-GPU:

```bash
torchrun --nnodes=1 --nproc_per_node=8 -m monai.bundle run \
--config_file="['configs/train.json','configs/train_continual.json','configs/evaluate.json','configs/mgpu_evaluate.json']"
```

### Other explanatory items

The `label_mapping` in `evaluation.json` does not include `0` because the postprocessing step performs argmax (`VistaPostTransformd`), and a `0` prediction would negatively impact performance. In continuous learning, however, `0` is included for validation because no argmax is performed, and validation is done channel-wise (include_background=False). Additionally, `Relabeld` in `postprocessing` is required to map `label` and `pred` back to sequential indexes like `0, 1, 2, 3, 4` for dice calculation, as they are not in one-hot format. Evaluation does not support `point`, but finetuning does, as it does not perform argmax.

## FAQ

### TroubleShoot for Out-of-Memory

- Changing `patch_size` to a smaller value such as `"patch_size": [96, 96, 96]` would reduce the training/inference memory footprint.
- Changing `train_dataset_cache_rate` and `val_dataset_cache_rate` to a smaller value like `0.1` can solve the out-of-cpu memory issue when using huge finetuning dataset.
- Set `"postprocessing#transforms#0#_disabled_": false` to move the postprocessing to cpu to reduce the GPU memory footprint.

### Multi-channel input

- Change `input_channels` in `train.json` to your desired channel number
- Data split json can be a single multi-channel image or can be a list of single channeled images. Those images must have the same spatial shape and aligned/registered.

```json
{
"image": ["modality1.nii.gz", "modality2.nii.gz", "modality3.nii.gz"]
"label": "label.nii.gz"
},
```

### Wrong inference results from finetuned checkpoint

- Make sure you removed the `subclass` dictionary from inference.json if you ever mapped local index to [2,20,21]
- Make sure `0` is not included in your inference prompt for automatic segmentation.


## Configurations

### Best practice to set label_mapping
Expand Down
Loading
Loading