{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"PyAims tutorial : programming with AIMS in Python language\n",
"==========================================================\n",
"\n",
"This tutorial should work with Python 3 (Python 2 support has been dropped in pyaims 5.1).\n",
"\n",
"This is a [Jupyter/Ipython notebook](http://jupyter.org/):\n",
"\n",
"* [download notebook](pyaims_tutorial_nb.ipynb).\n",
"\n",
"pyaims_tutorial_nb.ipynb\n",
"\n",
"AIMS is a C++ library, but has python language bindings: [PyAIMS](https://brainvisa.info/pyaims/sphinx/). This means that the C++ classes and functions can be used from python. \n",
"This has many advantages compared to pure C++:\n",
"\n",
"* Writing python scripts and programs is much easier and faster than C++: there is no fastidious and long compilation step.\n",
"* Scripts are more flexible, can be modified on-the-fly, etc\n",
"* It can be used interactively in a python interactive shell.\n",
"* As pyaims is actually C++ code called from python, it is still fast to execute complex algorithms. \n",
" There is obviously an overhead to call C++ from python, but once in the C++ layer, it is C++ execution speed.\n",
"\n",
"A few examples of how to use and manipulate the main data structures will be shown here.\n",
"\n",
"The data for the examples in this section are available on the BrainVisa web site. New utility funcitons in pyaims 5.2 allow to download and install them very easily - see the beginning of the tutorial code. Alternatively they are here: [https://brainvisa.info/download/data/test_data.zip](https://brainvisa.info/download/data/test_data.zip)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"print(sys.version_info)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from soma.aims import demotools\n",
"import os\n",
"import tempfile\n",
"\n",
"# let's work in a temporary directory\n",
"tuto_dir = tempfile.mkdtemp(prefix='pyaims_tutorial_')\n",
"older_cwd = os.getcwd()\n",
"# download/install demo data from the server, if not already done\n",
"demotools.install_demo_data('test_data.zip', install_dir=tuto_dir)\n",
"\n",
"print('old cwd:', older_cwd)\n",
"os.chdir(tuto_dir)\n",
"print('we are working in:', tuto_dir)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We will need this function to print headers in a reproducible way in order to pass tests (it bascally removes the \"referential\" property which may be a randomly changing identifier)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def clean_h(hdr, hidden=None):\n",
" if hidden is None:\n",
" hidden = set(['referential'])\n",
" return {k: v for k, v in hdr.items() if k not in hidden}\n",
"\n",
"def print_h(hdr, hidden=None):\n",
" print(clean_h(hdr, hidden))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Using data structures\n",
"---------------------\n",
"\n",
"### Module importation\n",
"\n",
"In python, the aimsdata library is available as the [soma.aims](index.html#module-soma.aims) module.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import soma.aims\n",
"# the module is actually soma.aims:\n",
"vol = soma.aims.Volume(100, 100, 100, dtype='int16')"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true
}
},
"source": [
"or:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from soma import aims\n",
"# the module is available as aims (not soma.aims):\n",
"vol = aims.Volume(100, 100, 100, dtype='int16')\n",
"# in the following, we will be using this form because it is shorter."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### IO: reading and writing objects\n",
"\n",
"Reading operations are accessed via a single [soma.aims.read()](pyaims_highlevel.html#soma.aims.read) function, and writing through a single [soma.aims.write()](pyaims_highlevel.html#soma.aims.write) function. \n",
"[soma.aims.read()](pyaims_highlevel.html#soma.aims.read) function reads any object from a given file name, in any supported file format, and returns it:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from soma import aims\n",
"obj = aims.read('data_for_anatomist/subject01/subject01.nii')\n",
"print(obj.getSize())\n",
"obj2 = aims.read('data_for_anatomist/subject01/Audio-Video_T_map.nii')\n",
"print(obj2.getSize())\n",
"obj3 = aims.read('data_for_anatomist/subject01/subject01_Lhemi.mesh')\n",
"print(len(obj3.vertex(0)))\n",
"assert(obj.getSize() == [256, 256, 124, 1])\n",
"assert(obj2.getSize() == [53, 63, 46, 1])\n",
"assert(obj3.size() == 1 and len(obj3.vertex(0)) == 33837)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The returned object can have various types according to what is found in the disk file(s).\n",
"\n",
"Writing is just as easy. The file name extension generally determines the output format. \n",
"An object read from a given format can be re-written in any other supported format, provided the format can actually store the object type."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from soma import aims\n",
"obj2 = aims.read('data_for_anatomist/subject01/Audio-Video_T_map.nii')\n",
"aims.write(obj2, 'Audio-Video_T_map.ima')\n",
"obj3 = aims.read('data_for_anatomist/subject01/subject01_Lhemi.mesh')\n",
"aims.write(obj3, 'subject01_Lhemi.gii')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"
Exercise
\n", "Write a little file format conversion tool\n", "Exercise
\n", "\n", "Make a program which loads the image *data_for_anatomist/subject01/Audio-Video_T_map.nii* and thresholds it so as to keep values above 3.\n", "Exercise
\n", "\n", "Make a program to dowsample the anatomical image *data_for_anatomist/subject01/subject01.nii* and keeps one voxel out of two in every direction.\n", "Exercise
\n", "Make a deforming mesh that goes from the original mesh to 5mm away, by steps of 0.5 mm\n", "Exercise
\n", "Make a time-texture, with at each time/vertex of the previous mesh, sets the value of the underlying volume *data_for_anatomist/subject01/subject01.nii*\n", "Note
\n", "Only properties declared in a \"syntax\" file may be saved and re-loaded. Other properties are just not saved.\n", "Warning
\n", "warning:: Some algorithms need that the volume they process have a **border**: a few voxels all around the volume. \n", " Indeed, some algorithms can try to access voxels outside the boundaries of the volume which may cause a segmentation error if the volume doesn't have a border. \n", " That's the case for example for operations like erosion, dilation, closing. \n", " There's no test in each point to detect if the algorithm tries to access outside the volume because it would slow down the process.\n", "\n", " In the previous example, a 2 voxels border is added by passing a parameter *border=2* to [soma.aims.read](pyaims_highlevel.html#soma.aims.read) function.\n", "