Library / SDK
voxelmorph/voxelmorph avatar
voxelmorph/voxelmorph

VoxelMorph's two build files declare two different licenses

Unsupervised Learning for Image Registration

2,753 stars643 forksPythonApache-2.0

At a glance

What is it?
VoxelMorph is a library for learning-based image registration, and it is mid-migration: the default branch is called dev, the file warns that the PyTorch interfaces may change, and the stable TensorFlow version lives on a separate branch. Meanwhile the packaging is duplicated, with a pyproject.toml and a setup.py that disagree on the license, on where the version comes from, and on whether torch is a dependency.
Who is it for?
VoxelMorph suits research code that trains or applies deformable registration models on medical images and is willing to read generators.py before using it on its own data. Two things to settle first.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 23 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The license field says MIT, setup.py says Apache 2.0

The repository carries a LICENSE.md at the root and its license field reads Apache-2.0. Inside the packaging, the two build files disagree with each other and with one of those. pyproject.toml sets `license = { text = "MIT" }` and lists the classifier License :: OSI Approved :: MIT License. setup.py passes `license='Apache 2.0'` and lists License :: OSI Approved :: Apache Software License instead.

So there are three license statements in play: Apache-2.0 at the repository level, MIT in pyproject.toml, and Apache 2.0 in setup.py. The two build files are describing the same distribution and disagreeing about its terms.

It matters in practice because pyproject.toml is what a modern pip reads for metadata, while a setup.py based install path produces the Apache string. Anyone redistributing, vendoring or publishing a derivative has to open LICENSE.md to find out which one is real, and the two files give no way to tell which was last edited.

The version is pinned in one file and read from source in the other

pyproject.toml declares `version = "0.3.3"` as a literal. setup.py does something else entirely: it opens voxelmorph/__init__.py, searches the text with the pattern for a `__version__` assignment, and raises a RuntimeError naming the file if the search comes back empty.

python
init_file = pathlib.Path(__file__).parent.resolve().joinpath('voxelmorph/__init__.py')
init_text = open(init_file, 'rt').read()
pattern = r"^__version__ = ['\"]([^'\"]*)['\"]"
match = re.search(pattern, init_text, re.M)
if not match:
    raise RuntimeError(f'Unable to find __version__ in {init_file}')
version = match.group(1)

Two version sources means two places to bump, and only one of them fails loudly when it is wrong. The setup.py path fails the build outright if the version line is missing or reformatted. The pyproject path cannot fail, because the string is right there.

The build backend declared in pyproject.toml is setuptools.build_meta with setuptools 61.0 and wheel required, so both files sit in the same build. Which one supplies the version to the installed package depends on which the build ends up reading, and nothing in either file says which that is.

torch is in one dependency list and missing from the other

The pyproject.toml dependency list has nine entries: torch, scikit-image, packaging, numpy, scipy, nibabel, h5py and neurite at 0.3 or newer, which is eight names plus the version constraint. The setup.py install_requires list carries the same set minus torch.

That omission is the more consequential of the two differences. The repository's own warning is that VoxelMorph pytorch is under active development with interfaces that may change, and the tutorial material and pre-trained model links all belong to that PyTorch line, so a package built from setup.py would install without the framework the package is named for.

The rest of the two lists line up name for name, which makes the single missing entry harder to spot in a diff. Anyone packaging this for others should decide which list is the contract and make the other one follow it, rather than assuming the files are interchangeable.

Every example runs scripts/tf on the PyTorch branch

The default branch is named dev, and the file opens with a warning that VoxelMorph pytorch is under active development and that interfaces may change, then points users who want the stable TensorFlow version at a branch named dev-tensorflow. Every command in the tutorial, training, registration and testing sections nevertheless runs out of a tf subdirectory:

code
./scripts/tf/train.py --img-list /images/list.txt --model-dir /models/output --gpu 0
code
./scripts/tf/register.py --moving moving.nii.gz --fixed atlas.nii.gz --moved warped.nii.gz --model model.h5 --gpu 0
code
./scripts/tf/test.py --model model.h5 --atlas atlas.npz --scans scan01.npz scan02.npz scan03.npz --labels labels.npz

The repository has no GitHub releases at all, so there is no packaged artifact to compare against either. A reader who follows the warning to the stable branch and the commands to scripts/tf has to work out for themselves which combination is coherent.

Two instructions for installing from source, in the same file

The install section says to install the published package from PyPI:

code
pip install voxelmorph

Then it says that to work from a source checkout you should clone the repository and install the requirements listed in setup.py. The instructions section later repeats that same sentence about setup.py, and then contradicts it in the next line with a note saying that for source development you should install from GitHub instead:

code
pip install git+https://github.com/voxelmorph/voxelmorph.git

Those are different operations. Installing the requirements listed in a file leaves the repository on disk and the package uninstalled, while installing from the git URL builds and installs the package itself. The file states both, in two sections, without saying which applies to which case.

The surrounding detail is thin as well. No dependency list is reproduced anywhere, so a reader following the first sentence has to go and read setup.py or pyproject.toml, which is the file whose contents the two build files above already show disagreeing.

Running the original MICCAI 2018 mode means opting back into xy indexing

The parameter section is specific about defaults and about how to get the old behaviour. For the CC loss function a reg parameter of 1 is given, and for the MSE loss function 0.01. For the MICCAI version the file gives `image_sigma=0.01` and `prior_lambda=25`.

Then it explains the drift. In the original MICCAI code those parameters were applied after the scaling of the velocity field, and with the current code that has been fixed, which is why the defaults differ. Running the updated code is recommended, and the very original MICCAI2018 mode is still reachable by using the `xy` indexing and the `use_miccai_int` network option together with the MICCAI2018 parameters.

So the way to reproduce the older results is a two-part opt-in rather than a version pin, which is a different shape of guarantee from the one a frozen release would give.

The npz contract is two keys, and the file ends mid heading

Training data can be NIfTI, MGZ or npz. For the npz route the contract is spelled out: every file in the data list is assumed to have a `vol` parameter pointing at the image data to be registered, and an optional `seg` variable pointing at a matching discrete segmentation for semi-supervised learning. Shapes are assumed consistent across the training set, with a customised generator named as the way out if they are not. The expected place to change any of that is the data loading code in voxelmorph/generators.py.

The test command takes the same shape of input, with `vol` and `seg` in the atlas and scan files and a separate labels.npz holding the anatomical labels to include in the Dice score. Registration additionally takes a `--save-warp` flag to write the predicted deformation field alongside the moved image.

The file ends on a heading that stops after VoxelMorph pape, so whatever that section was meant to introduce is not in it.

Editorial conclusion

VoxelMorph suits research code that trains or applies deformable registration models on medical images and is willing to read generators.py before using it on its own data. Two things to settle first. Decide which line you are on, because the default dev branch is the PyTorch one carrying a warning that interfaces may change, while the stable TensorFlow version sits on a different branch. And decide which build file you trust, because the license, the version source and the dependency list each appear twice with different answers, and picking the wrong one produces a package whose metadata does not describe its own code.

Frequently asked questions

What is a voxel in an MRI?

That question is about MRI data generally, and this repository does not answer it. What VoxelMorph does state is its own scope: a general purpose library for learning-based alignment and registration, modelling with deformations, working on images in NIfTI, MGZ or npz form.

Which license does VoxelMorph use?

The repository's license field reads Apache-2.0 and setup.py passes license='Apache 2.0', but pyproject.toml sets license to MIT with the matching MIT classifier. A LICENSE.md file sits at the root, and the two build files do not reconcile their difference.

Does installing VoxelMorph from PyPI give me the PyTorch version?

The file does not say what the published package contains. It does warn that VoxelMorph pytorch is under active development with interfaces that may change, and it points users who want the stable TensorFlow version at the dev-tensorflow branch rather than at PyPI.

How do I train a VoxelMorph model on my own data?

Expect to customise the data loading code in voxelmorph/generators.py. Training data can be NIfTI, MGZ or npz, each npz file is expected to have a `vol` parameter and an optional `seg` segmentation, image shapes are assumed consistent, and the training script takes an image list plus a `--model-dir` for the saved weights.

What is the npz indexing convention in VoxelMorph?

The current code emphasises `ij` indexing, while original development used `xy`. The spatial transform code at voxelmorph.layers.SpatialTransformer accepts N-dimensional affine and dense transforms with linear and nearest neighbour interpolation, and the very original MICCAI2018 mode is reached by passing `xy` indexing with the `use_miccai_int` option.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. voxelmorph/voxelmorph on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/voxelmorph-voxelmorph.svg)](https://hysenlabs.com/projects/voxelmorph-voxelmorph)