Skip to content

Repository files navigation

L2L-Registration

End-to-end CBCT/IOS landmark detection and registration pipeline.

The pipeline runs four components:

  1. IOS-Normalizer
  2. IOS-Landmarks
  3. CBCT-Landmarks
  4. L2L-Registration

Two isolated Python environments are used because the validated components require different PyTorch and CUDA runtime versions:

Environment Main components PyTorch wheel
.venvs/torch25 IOS-Normalizer, IOS-Landmarks, L2L-Registration PyTorch 2.5.1, CUDA 12.4
.venvs/torch28 CBCT-Landmarks PyTorch 2.8.0, CUDA 12.6

The local CUDA Toolkit is used to compile the custom IOS-Landmarks extensions. For the validated setup, nvcc must report CUDA Toolkit 12.4.


1. Supported platform

Installation and inference must run in a Linux CUDA environment.

Supported examples include:

  • a native Linux workstation;
  • a Linux server or cluster node;
  • CUDA-enabled WSL2.

The setup script does not support native Windows Python environments.


2. Repository layout

The repository is expected to contain:

L2L-Registration/
├── data/
│   ├── inputs/
│   └── outputs/
│
├── model/
│   ├── IOS-Normalizer/
│   ├── IOS-Landmarks/
│   ├── CBCT-Landmarks/
│   └── L2L-Registration/
|
├── requirements/
│   ├── requirements-torch25.txt
│   └── requirements-torch28.txt
│
├── weights/
|   ├── IOS-Normalizer/
│   └── best.pt
│
├── IOS-Landmarks/
│   ├── segmentator_best.pth
│   └── heatmap_landmarks.pth
│
├── CBCT-Landmarks/
|   ├── checkpoint_best.pth
|   └── nnLM_results/
|       └── Dataset002_STS2026/
|           └── nnLandmark__nnUNetResEncUNetMPlans_torchres__3d_fullres/
|               ├── dataset.json
|               ├── dataset_fingerprint.json
|               └── plans.json
|
├── .venvs/
|   ├── torch25/
|   └── torch28/
|
├── .gitattributes
├── .gitignore 
├── .gitmodules
├── create_venvs_def.py
├── predict.sh
├── README.md
└── run_inference.py

IOS-Normalizer and IOS-Landmarks are Git submodules. Their exact commits are recorded by the parent repository.


3. Clone the repository

Clone the project together with all submodules:

git clone --recurse-submodules https://github.com/AImageLab-zip/L2L-Registration.git
cd L2L-Registration

For an existing clone created without --recurse-submodules, initialize the submodules with:

git submodule update --init --recursive

Verify the checked-out submodule commits:

git submodule status

Do not run git submodule update --remote unless you intentionally want to test newer component revisions. The parent repository pins the validated commits.


4. System requirements

The installation script expects:

  • Python 3.10;
  • Git;
  • an NVIDIA GPU and compatible driver;
  • CUDA Toolkit 12.4 with nvcc;
  • GCC/G++;
  • Ninja or compatible build tools;
  • sparsehash development headers;
  • sufficient free disk space for two PyTorch environments.

Verify the main tools:

python3.10 --version
nvidia-smi
nvcc --version
g++ --version
git --version

The setup script requires Python 3.10.x.

Ubuntu/Debian build dependencies

Package names may vary between distributions. A typical Ubuntu/Debian installation includes:

sudo apt update
sudo apt install -y \
    build-essential \
    git \
    ninja-build \
    libsparsehash-dev \
    python3.10 \
    python3.10-dev \
    python3.10-venv

Install or load CUDA Toolkit 12.4 separately so that:

nvcc --version

reports release 12.4.

The PyTorch 2.8 environment uses CUDA 12.6 wheels, but a separate local CUDA 12.6 Toolkit is not required by this setup script. The local Toolkit is used for compiling the PyTorch 2.5 custom extensions.


5. Create the Python environments

Run the setup script from the repository root:

python3.10 create_venvs_def.py

This creates:

.venvs/torch25
.venvs/torch28

The script installs:

  • PyTorch 2.5.1, torchvision 0.20.1 and torchaudio 2.5.1 from the CUDA 12.4 wheel index;
  • PyTorch 2.8.0, torchvision 0.23.0 and torchaudio 2.8.0 from the CUDA 12.6 wheel index;
  • OpenAI CLIP;
  • the required PyTorch Geometric extension wheels;
  • pointgroup_ops;
  • pointops;
  • CBCT-Landmarks and its nnLM_predict command.

CBCT-Landmarks is installed in editable mode by default, so changes to its Python source code are immediately available in .venvs/torch28.

Recreate existing environments

python3.10 create_venvs_def.py --recreate

This removes and recreates both environment directories.

Docker-equivalent CBCT installation

To install CBCT-Landmarks as a normal, non-editable package:

python3.10 create_venvs_def.py --non-editable-cbct

To recreate everything in non-editable mode:

python3.10 create_venvs_def.py \
    --recreate \
    --non-editable-cbct

Select the CUDA architecture

The default custom-extension architecture is:

8.6

Override it when required by the target GPU:

python3.10 create_venvs_def.py \
    --cuda-arch-list 8.6

Multiple architectures can be supplied using the syntax accepted by TORCH_CUDA_ARCH_LIST, for example:

python3.10 create_venvs_def.py \
    --cuda-arch-list "8.0;8.6"

Limit compilation jobs

python3.10 create_venvs_def.py \
    --max-jobs 2

Reducing this value can help on systems with limited RAM.

Attempt another CUDA Toolkit version

The validated extension build uses CUDA Toolkit 12.4. The setup script stops when another nvcc version is detected.

To attempt compilation anyway:

python3.10 create_venvs_def.py \
    --allow-cuda-toolkit-mismatch

This only bypasses the version check. It does not guarantee that the extensions will compile or run correctly.


6. Verify the installation

Verify PyTorch 2.5

.venvs/torch25/bin/python - <<'PY'
import torch
import torchvision
import pointgroup_ops
import pointops

print("PyTorch:", torch.__version__)
print("PyTorch CUDA:", torch.version.cuda)
print("torchvision:", torchvision.__version__)
print("pointgroup_ops:", pointgroup_ops.__file__)
print("pointops:", pointops.__file__)
PY

Expected PyTorch values:

PyTorch: 2.5.1
PyTorch CUDA: 12.4

Verify PyTorch 2.8 and CBCT-Landmarks

.venvs/torch28/bin/python - <<'PY'
import torch
import torchvision
import nnlandmark

print("PyTorch:", torch.__version__)
print("PyTorch CUDA:", torch.version.cuda)
print("torchvision:", torchvision.__version__)
print("nnlandmark:", nnlandmark.__file__)
PY

Expected PyTorch values:

PyTorch: 2.8.0
PyTorch CUDA: 12.6

Verify the command-line entry point:

.venvs/torch28/bin/nnLM_predict --help

7. Add the trained weights

The inference script expects this structure:

weights/
├── IOS-Normalizer/
│   └── best.pt
│
├── IOS-Landmarks/
│   ├── segmentator_best.pth
│   └── heatmap_landmarks.pth
│
└── CBCT-Landmarks/
    ├── checkpoint_best.pth
    └── nnLM_results/
        └── Dataset002_STS2026/
            └── nnLandmark__nnUNetResEncUNetMPlans_torchres__3d_fullres/
                ├── dataset.json
                ├── dataset_fingerprint.json
                └── plans.json

The complete CBCT-Landmarks result directory must be provided. dataset.json alone is not sufficient.

Download the model weights


8. Prepare the input data

The input directory must contain one top-level directory per case:

data/inputs/
├── case_001/
│   ├── CBCT.nii.gz
│   ├── lower.stl
│   └── upper.stl
|
├── case_002/
│   ├── CBCT.nii.gz
│   ├── lower.stl
│   └── upper.stl
│
└── ...

Important requirements:

  • every case must be stored in its own top-level directory;
  • CBCT files are found recursively;
  • every CBCT filename must end with CBCT.nii.gz;
  • top-level case directory names must be unique;
  • two CBCT files must not resolve to the same case identifier;
  • IOS data must follow the formats and naming conventions accepted by IOS-Normalizer.

Example CBCT names:

patient_001_CBCT.nii.gz
scan-CBCT.nii.gz
CBCT.nii.gz

The case identifier used by CBCT-Landmarks is normally derived from the parent case-directory name.


9. Run inference

Run the complete pipeline using predict.sh

The two virtual environments do not need to be activated manually.

run_inference.py automatically launches:

  • IOS-Normalizer with .venvs/torch25/bin/python;
  • IOS-Landmarks with .venvs/torch25/bin/python;
  • CBCT-Landmarks with .venvs/torch28/bin/nnLM_predict;
  • L2L-Registration with .venvs/torch25/bin/python.

The IOS and CBCT branches run in parallel for each split. L2L-Registration starts only after both branches finish successfully.

Input splits are processed sequentially.


10. Inference options

Display the available arguments:

.venvs/torch25/bin/python run_inference.py --help

Default command

Because the default directories are repository-relative, this is sufficient when the standard layout is used:

.venvs/torch25/bin/python run_inference.py

Default values:

--inputs       data/inputs
--outputs      data/outputs
--work-dir     .work
--weights-dir  weights
--num-splits   4

Custom paths

.venvs/torch25/bin/python run_inference.py \
    --inputs /absolute/path/to/inputs \
    --outputs /absolute/path/to/outputs \
    --weights-dir /absolute/path/to/weights \
    --work-dir /absolute/path/to/work \
    --num-splits 4

Change the number of splits

.venvs/torch25/bin/python run_inference.py \
    --num-splits 2

The number of non-empty splits cannot exceed the number of top-level cases.

Keep intermediate outputs

Intermediate files are deleted after successful processing by default.

To preserve them for debugging:

KEEP_INTERMEDIATES=1 \
.venvs/torch25/bin/python run_inference.py \
    --inputs data/inputs \
    --outputs data/outputs \
    --weights-dir weights

The intermediate data will remain under:

.work/

11. Use environments stored elsewhere

By default, inference looks for:

.venvs/torch25/bin/python
.venvs/torch28/bin/python
.venvs/torch28/bin/nnLM_predict

Override these locations with environment variables:

export TORCH25_PYTHON=/absolute/path/to/torch25/bin/python
export TORCH28_PYTHON=/absolute/path/to/torch28/bin/python
export NNLM_PREDICT=/absolute/path/to/torch28/bin/nnLM_predict

The whole virtual-environment root can also be changed:

export VENV_ROOT=/absolute/path/to/venvs

Other supported path overrides are:

export PROJECT_ROOT=/absolute/path/to/L2L-Registration
export INPUT_DIR=/absolute/path/to/inputs
export OUTPUT_DIR=/absolute/path/to/outputs
export WORK_DIR=/absolute/path/to/work
export WEIGHTS_DIR=/absolute/path/to/weights

Environment variables used to calculate module paths should be set before starting run_inference.py.


12. Outputs

Final registration results are written to:

data/outputs/

or to the directory supplied through --outputs.

The pipeline verifies that L2L-Registration creates or modifies at least one output file for every processed split. A split is reported as failed when no output is produced.

Temporary intermediate files are stored under .work/ and removed unless KEEP_INTERMEDIATES=1 is set.


13. Common problems

python3.10 is not found

Install Python 3.10 or pass the correct executable:

python3 create_venvs_def.py --python /path/to/python3.10

The selected interpreter must report Python 3.10.

An environment already exists

Recreate both environments:

python3.10 create_venvs_def.py --recreate

nvcc is not found

Install or load CUDA Toolkit 12.4 and ensure its bin directory is in PATH.

Example:

export CUDA_HOME=/usr/local/cuda-12.4
export PATH="$CUDA_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$CUDA_HOME/lib64:${LD_LIBRARY_PATH:-}"

Then verify:

nvcc --version

Sparsehash headers are missing

On Ubuntu/Debian:

sudo apt install libsparsehash-dev

pointgroup_ops or pointops fails to compile

Check:

nvcc --version
g++ --version
echo "$CUDA_HOME"
echo "$TORCH_CUDA_ARCH_LIST"

Then recreate the environments with fewer build jobs:

python3.10 create_venvs_def.py \
    --recreate \
    --max-jobs 2

nnLM_predict is not found

Verify:

ls -l .venvs/torch28/bin/nnLM_predict

Recreate the environments when it is missing:

python3.10 create_venvs_def.py --recreate

No CBCT files are found

At least one file under the selected input directory must end with:

CBCT.nii.gz

Check with:

find data/inputs -type f -iname '*CBCT.nii.gz'

A weight file is missing

Compare the local weights/ directory with the structure in the Add the trained weights section. The inference script validates every required checkpoint before starting the relevant component.

GPU memory is insufficient

The current CBCT-Landmarks command uses a patch batch size of 4. Reducing it requires changing the corresponding argument in run_inference.py.

Changing --num-splits controls how many cases are processed together, but it does not change the internal CBCT patch batch size.


14. Development notes

  • CBCT-Landmarks is editable by default.
  • IOS-Normalizer, IOS-Landmarks, and L2L-Registration are executed directly from their source directories.
  • Python source modifications in those directories are therefore used directly.
  • Changes to C++ or CUDA files in model/IOS-Landmarks/libs/ require rebuilding the custom extensions.
  • Do not commit virtual environments, private inputs, generated outputs, temporary files, compiled artifacts, or model weights.

Rebuild after changing extension sources:

python3.10 create_venvs_def.py --recreate

15. Updating submodules

Update a submodule only after selecting and testing a specific component commit.

Example:

git -C model/IOS-Normalizer fetch
git -C model/IOS-Normalizer checkout <COMMIT_HASH>

git add model/IOS-Normalizer
git commit -m "Update IOS-Normalizer submodule"

Repeat the same process for IOS-Landmarks.

Verify recorded revisions:

git submodule status

License

See the license files in this repository and in each third-party component.

The source-code license does not automatically grant permission to redistribute trained weights, challenge data, clinical data, or third-party datasets.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages