Skip to content

Commit

Permalink
Merge pull request #640 from ANTsX/update-docs
Browse files Browse the repository at this point in the history
DOCS: update docs
  • Loading branch information
Nicholas Cullen, PhD committed May 18, 2024
2 parents 3b5383e + 2f429ad commit 7ee42da
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 37 deletions.
136 changes: 115 additions & 21 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
# ANTsPy Contributors Guide
# Guide to contributing to ANTsPy

This guide tells you everything you need to know to set up the development workflow that will allow you to contribute code to antspy.
ANTsPy is a Python library that primarily wraps the ANTs C++ library, but also contains a lot of custom C++ and Python code. There are a decent amount of moving parts to get to familiar with before being able to contribute, but we've done our best to make the process easy.

## Installing ANTsPy Development Environment
This guide tells you everything you need to know about ANTsPy to add or change any code in the project. The guide is composed of the following sections.

To start developing, you need to install a development copy of ANTsPy. This follows the same steps as developing any python package.
- Project structure
- Setting up a dev environment
- Wrapping ANTs functions
- Adding C++ / ITK code
- Adding Python code
- Running tests

The first two sections and the last section should be read by everyone, but the other sections can be skipped depending on your goal.

<br />

## Project structure

The ANTsPy project consists of multiple folders which are listed and explained here.

- **.github/** : contains all GitHub actions
- **ants/** : contains the Python code for the library
- **data/** : contains any data (images) included with the installed library
- **docs/** : contains the structure for building the library documentation
- **scripts/** : contains scripts to build / clone ITK and ANTs during installation
- **src/** : contains the C++ code for the library
- **tests/** : contains all tests
- **tutorials/** contains all .md and .ipynb tutorials

If you are adding code to the library, the three folders you'll care about most are `ants/` (to add Python code), `src/` (to add C++ code), and `tests/` (to add tests).

### Nanobind

The C++ code is wrapped using [nanobind](https://nanobind.readthedocs.io/en/latest/). It is basically an updated version of pybind11 that makes it easy to call C++ functions from Python. Having a basic understanding of nanobind can help in some scenarios, but it's not strictly necessary.

The `CMakeLists.txt` file and the `src/main.cpp` file contains most of the information for determining how nanobind wraps and builds the C++ files in the project.

### Scikit-build

The library is built using [scikit-build](https://scikit-build.readthedocs.io/en/latest/), which is a modern alternative to `setup.py` files for projects that include C++ code.

The `pyproject.toml` file is the central location for steering the build process. If you need to change the way the library is built, that's the best place to start.

<br />

## Setting up a dev environment

To start developing, you need to build a development copy of ANTsPy. This process is the same as developing for any python package.

```bash
git clone https://github.com/ANTsX/ANTsPy.git
cd ANTsPy
python -m pip install -v -e .
```

Notice the `-v` flag to have a verbose output so you can follow the build process that can take ~45 minutes. Then there is also the `-e` flag that will build the antspy package in such a way that any changes to the python code will be automatically detected when you restart your python terminal without having to build the package again.
Notice the `-v` flag to have a verbose output so you can follow the build process that can take 30 - 45 minutes. Then there is also the `-e` flag that will build the library in such a way that any changes to the Python code will be automatically detected when you restart your python terminal without having to build the package again.

Any changes to C++ code will require you to run that last line (`python -m pip install -v -e .`) again to rebuild the compiled libraries.
Any changes to C++ code will require you to run that last line (`python -m pip install -v -e .`) again to rebuild the compiled libraries. However, it should not take more than a couple of minutes if you've only made minor changes or additions.

## What happens when I install ANTsPy?
### What happens when you install ANTsPy

When you run `python -m pip install .` or `python -m pip install -e .` to install antspy from source, the CMakeLists.txt file is run. Refer there if you want to change any part of the install process. Briefly, it performs the following steps:

Expand All @@ -25,7 +67,9 @@ When you run `python -m pip install .` or `python -m pip install -e .` to instal
3. The C++ files from the `src` directory are used to build the antspy library files
4. The antspy python package is built as normal

## Wrapping core ANTs functions
<br />

## Wrapping ANTs functions

Wrapping an ANTs function is easy since pybind11 implicitly casts between python and C++ standard types, allowing you to directly interface with C++ code. Here's an example:

Expand Down Expand Up @@ -77,7 +121,9 @@ The general workflow for wrapping a library calls involves the following steps:
- pass those raw arguments through the function `process_arguments(args)`
- pass those processed arguments into the library call (e.g. `lib.Atropos(processed_args)`).
## Writing custom code for antspy
<br />
## Adding C++ / ITK code
You can write any kind of custom code to process antspy images. The underlying image is ITK so the AntsImage class holds a pointer to the underlying ITK object in the in the property `self.pointer`.
Expand All @@ -99,7 +145,7 @@ Now, say you wrapped this code and wanted to call it from python. You wouldn't p
the Python ANTsImage object directly, you would pass in the `self.pointer` attribute which
contains the ITK image pointer.

### Example 1 - getOrigin
### Example - getOrigin

Let's do a full example where we get the origin of a Python AntsImage from the underlying ITK image.

Expand All @@ -126,7 +172,7 @@ std::vector getOrigin( AntsImage<ImageType> antsImage )
typedef typename ImageType::Pointer ImagePointerType;
ImagePointerType itkImage = antsImage.ptr;

// do everything else as normal with ITK Imaeg
// do everything else as normal with ITK Image
typename ImageType::PointType origin = image->GetOrigin();
unsigned int ndim = ImageType::GetImageDimension();

Expand All @@ -146,6 +192,7 @@ void getOrigin(nb::module_ &m)
m.def("getOrigin", &getOrigin<itk::Image<unsigned char,3>>);
m.def("getOrigin", &getOrigin<itk::Image<float,2>>);
m.def("getOrigin", &getOrigin<itk::Image<float,3>>);
// ...
}

```
Expand All @@ -167,22 +214,20 @@ Finally, we create a wrapper function in python file `get_origin.py`. Notice tha

```python

from ants import lib # use relative import e.g. "from .. import lib" in package code
from ants.decorators import image_method
from ants.internal import get_lib_fn

@image_method
def get_origin(img):
idim = img.dimension
ptype = img.pixeltype
libfn = get_lib_fn('getOrigin')
origin = libfn(img.pointer)

# call function - NOTE how we pass in `img.pointer`, not `img` directly
origin = lib.getOrigin(img.pointer)

# return as tuple
return tuple(origin)
```

And that's it! For more other return types, you should refer to the nanobind docs.
And that's it! More details about how to write Python code for ANTsPy is presented below. For other return types, consult the nanobind docs. However, most C++ types will be automatically converted to the corresponding Python types - both arguments and return values.

## Wrapping an ITK image for antspy
### Wrapping an ITK image

In the previous section, we saw how easy it is to cast from AntsImage to ITK Image by calling `antsImage.ptr`. It is also easy to go the other way and wrap an ITK image as an AntsImage.

Expand Down Expand Up @@ -232,7 +277,56 @@ AntsImage<OutImageType> someFunction( AntsImage<InImageType> antsImage )
}
```

## Running Tests
<br />

## Adding Python code

If you want to add custom Python code that calls other ANTsPy functions or the wrapped code, there are a few things to know. The `label_clusters` function provides a good example to show how to do so.

```python
import ants
from ants.internal import get_lib_fn, process_arguments
from ants.decorators import image_method

@image_method
def label_clusters(image, min_cluster_size=50, min_thresh=1e-6, max_thresh=1, fully_connected=False):
"""
This will give a unique ID to each connected
component 1 through N of size > min_cluster_size
"""
dim = image.dimension
clust = ants.threshold_image(image, min_thresh, max_thresh)
temp = int(fully_connected)
args = [dim, clust, clust, min_cluster_size, temp]
processed_args = process_arguments(args)
libfn = get_lib_fn('LabelClustersUniquely')
libfn(processed_args)
return clust
```

First, notice the imports at the top. You generally need three imports. First, you need to import the library so that all other internal functions (such as `ants.threshold_image`) are available.

```python
import ants
```

Next, you need import a few functions from `ants.internal` that let you get a function from the compiled C++ library (`get_lib_fn`) and that let you combined arguments into the format ANTs expects (`process_arguments`). Note that `process_arguments` is only needed if you are called a wrapped ANTs function.

```python
from ants.internal import get_lib_fn, process_arguments
```

Finally, you should import `image_method` from `ants.decorators`. This decorator lets you attach a function to the ANTsImage class so that the function can be chained to the image. This is why you can call `image.dosomething()` instead of only `ants.dosomething(image)`.

```python
from ants.decorators import image_method
```

With those three imports, you can call any internal Python function or any C++ function (wrapped or custom).

<br />

## Running tests

All tests can be executed by running the following command from the main directory:

Expand Down
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,29 @@

<br>

The ANTsPy library wraps the well-established C++ biomedical image processing framework <i>[ANTs](https://github.com/antsx/ants)</i>. It includes blazing-fast reading and writing of medical images, algorithms for registration, segmentation, and statistical learning, as well as functions to create publication-ready visualizations.
The ANTsPy library wraps the well-established C++ biomedical image processing framework [ANTs](https://github.com/antsx/ants). It includes blazing-fast reading and writing of medical images, algorithms for registration, segmentation, and statistical learning, as well as functions to create publication-ready visualizations.

If you are looking to train deep learning models on your medical images, you might be interested in [antspynet](https://github.com/antsx/antspy) which provides tools for training and visualizing deep learning models. ANTsPy and ANTsPyNet seamlessly integrate with the greater Python community, particularly deep learning libraries, scikit-learn, and numpy.
If you are looking to train deep learning models on medical imaging datasets, you might be interested in [ANTsPyNet](https://github.com/antsx/antspy) which provides tools for training and visualizing deep learning models.

<br>

## Installation

### Pre-compiled binaries

The easiest way to install ANTsPy is via the latest pre-compiled binaries from PyPI.

```bash
pip install antspyx
```

Because of limited storage space, pip binaries are not available for every combination of python
version and platform. If we do not have releases for your platform, you can check the
[Github Releases page](https://github.com/antsx/antspy/releases) or build from source:
version and platform. If we do not have releases for your platform on PyPI, you can check the
[Releases](https://github.com/antsx/antspy/releases) page for archived binaries.

### Building from source

In some scenarios, it can make sense to build from source. In general, you can build ANTsPy as you would any other Python package.

```
git clone https://github.com/antsx/antspy
Expand All @@ -38,7 +44,7 @@ python -m pip install .
```

Further details about installing ANTsPy or building it from source can be found in the
[installation tutorial](https://github.com/antsx/antspy/blob/master/tutorials/Installation.md).
[Installation Tutorial](https://github.com/antsx/antspy/blob/master/tutorials/Installation.md).

<br>

Expand All @@ -48,7 +54,7 @@ Here is an example of reading in an image, using various utility functions such

```python
import ants
img = ants.image_read(get_data("r16"))
img = ants.image_read(ants.get_data("r16"))
img = ants.resample_image(img, (64,64), 1, 0 )
mask = ants.get_mask(img)
segs1 = ants.atropos(a=img, m='[0.2,1x1]', c='[2,0]', i='kmeans[3]', x=mask)
Expand All @@ -58,23 +64,21 @@ segs1 = ants.atropos(a=img, m='[0.2,1x1]', c='[2,0]', i='kmeans[3]', x=mask)

## Tutorials

Resources for learning about ANTsPy can be found in the [tutorials](https://github.com/ANTsX/ANTsPy/tree/master/tutorials) folder. An overview of the available tutorials is presented below.

- [Basic overview](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/tutorial_5min.md)

- [Composite registrations](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/concatenateRegistrations.ipynb)

- [Multi-metric registration](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/concatenateRegistration/MultiMetricRegistration.ipynb)
Resources for learning about ANTsPy can be found in the [tutorials](https://github.com/ANTsX/ANTsPy/tree/master/tutorials) folder. A selection of especially useful tutorials is presented below.

- [Image math operations](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/iMath_help.ipynb)
- Basic overview [[Link](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/tutorial_5min.md)]
- Composite registrations [[Link](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/concatenateRegistrations.ipynb)]
- Multi-metric registration [[Link](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/concatenateRegistration/MultiMetricRegistration.ipynb)]
- Image math operations [[Link](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/iMath_help.ipynb)]
- Wrapping ITK code [[Link](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/UsingITK.ipynb)]

- [Wrapping ITK code](https://github.com/ANTsX/ANTsPy/blob/master/tutorials/UsingITK.ipynb)
More tutorials can be found in the [ANTs](https://github.com/ANTsX/ANTs) repository.

<br>

## Contributing

If you have a question, feature request, or bug report the best way to get help is by posting an issue on the GitHub page. We welcome and are thankful for new contributions and ideas. If you want to add code, the best way to get started is by reading the [contributors guide](https://github.com/ANTsX/ANTsPy/blob/master/CONTRIBUTING.md) that runs through the structure of the project and how we go about wrapping ITK and ANTs code in C++.
If you have a question, feature request, or bug report the best way to get help is by posting an issue on the GitHub page. We welcome any new contributions and ideas. If you want to add code, the best way to get started is by reading the [contributors guide](https://github.com/ANTsX/ANTsPy/blob/master/CONTRIBUTING.md) that runs through the structure of the project and how we go about wrapping ITK and ANTs code in C++.

<br>

Expand Down

0 comments on commit 7ee42da

Please sign in to comment.