\n",
"
Note
\n",
"Only properties declared in a \"syntax\" file may be saved and re-loaded. Other properties are just not saved.\n",
"
\n",
"\n",
"### Vertices\n",
"\n",
"Vertices (or nodes) can be accessed via the vertices() method. Each vertex is also a dictionary-like properties set."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Caude_droit\n",
"Caude_gauche\n",
"Corps_caude_droit\n",
"Corps_caude_gauche\n",
"Pallidum_droit\n",
"Pallidum_gauche\n",
"Putamen_droit_ant\n",
"Putamen_droit_post\n",
"Putamen_gauche_ant\n",
"Putamen_gauche_post\n",
"Striatum_ventral_droit\n",
"Striatum_ventral_gauche\n",
"Thalamus_droit\n",
"Thalamus_gauche\n",
"v1v2v3\n"
]
}
],
"source": [
"for v_name in sorted([v['name'] for v in graph.vertices()]):\n",
" print(v_name)"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"To insert a new vertex, the [soma.aims.Graph.addVertex()](pyaims_lowlevel.html#soma.aims.Graph) method should be used:"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{ '__syntax__' : 'roi' }\n",
"{ '__syntax__' : 'roi', 'name' : 'new ROI' }\n"
]
}
],
"source": [
"v = graph.addVertex('roi')\n",
"print(v)\n",
"assert(v.getSyntax() == 'roi')\n",
"v['name'] = 'new ROI'\n",
"print(v)\n",
"assert(v == {'name': 'new ROI'})"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"### Edges\n",
"\n",
"An edge, or relation, links nodes together. Up to now we have always used binary, unoriented, edges. \n",
"They can be added using the [soma.aims.Graph.addEdge()](pyaims_lowlevel.html#soma.aims.Graph) method. \n",
"Edges are also dictionary-like properties sets."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ { '__syntax__' : 'roi_link' } ]\n",
"['Pallidum_gauche', 'new ROI']\n"
]
}
],
"source": [
"v2 = [x for x in graph.vertices() if x['name'] == 'Pallidum_gauche'][0]\n",
"if sys.version_info[2] < 3:\n",
" del x # python2 keeps this intermediate variable allocated: clean it.\n",
"e = graph.addEdge(v, v2, 'roi_link')\n",
"print(graph.edges())\n",
"# get vertices linked by this edge\n",
"print(sorted([x['name'] for x in e.vertices()]))\n",
"assert(sorted([x['name'] for x in e.vertices()]) == ['Pallidum_gauche', 'new ROI'])"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"### Adding meshes or buckets in a graph vertex or relation\n",
"\n",
"Setting meshes or buckets in vertices properties is OK internally, \n",
"but for saving and loading, additional consistancy must be ensured and internal tables update is required. \n",
"Then, use the [soma.aims.GraphManip.storeAims](pyaims_highlevel.html#soma.aims.GraphManip) function:"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"mesh = aims.read('data_for_anatomist/subject01/subject01_Lwhite.mesh')\n",
"# store mesh in the 'roi' property of vertex v of graph graph\n",
"aims.GraphManip.storeAims(graph, v, 'roi', mesh)"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"### Other examples\n",
"\n",
"There are other examples for pyaims [here](../examples).\n",
"\n",
"\n",
"Using algorithms\n",
"----------------\n",
"\n",
"AIMS contains, in addition to the different data structures used in neuroimaging, a set of algorithms which operate on these structures. \n",
"Currently only a few of them have Python bindings, because we develop these bindings in a \"lazy\" way, only when they are needed. \n",
"The algorithms currently available include data conversion, resampling, thresholding, \n",
"mathematical morphology, distance maps, the mesher, some mesh generators, and a few others. \n",
"But most of the algorithms are still only available in C++.\n",
"\n",
"\n",
"### Volume Thresholding"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"32767\n"
]
}
],
"source": [
"from soma import aims, aimsalgo\n",
"# read a volume with 2 voxels border\n",
"vol = aims.read('data_for_anatomist/subject01/subject01.nii', border=2)\n",
"# use a thresholder which will keep values above 600\n",
"ta = aims.AimsThreshold(aims.AIMS_GREATER_OR_EQUAL_TO, 600, intype=vol)\n",
"# use it to make a binary thresholded volume\n",
"tvol = ta.bin(vol)\n",
"print(tvol.value(0, 0, 0))\n",
"assert(tvol.value(0, 0, 0) == 0)\n",
"print(tvol.value(100, 100, 50))\n",
"assert(tvol.value(100, 100, 50) == 32767)\n",
"aims.write(tvol, 'thresholded.nii')"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"\n",
"\n",
"\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",
"
\n",
"\n",
"### Mathematical morphology"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"# apply 5mm closing\n",
"clvol = aimsalgo.AimsMorphoClosing(tvol, 5)\n",
"aims.write(clvol, 'closed.nii')"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"\n",
"\n",
"\n",
"### Mesher"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"m = aimsalgo.Mesher()\n",
"mesh = aims.AimsSurfaceTriangle() # create an empty mesh\n",
"# the border should be -1\n",
"clvol.fillBorder(-1)\n",
"# get a smooth mesh of the interface of the biggest connected component\n",
"m.getBrain(clvol, mesh)\n",
"aims.write(mesh, 'head_mesh.gii')"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"\n",
" \n",
"The above examples make up a simplified version of the head mesh extraction algorithm in `VipGetHead`, used in the Morphologist pipeline.\n",
"\n",
"\n",
"### Surface generation\n",
"\n",
"The [soma.aims.SurfaceGenerator](pyaims_algo.html#soma.aims.SurfaceGenerator) allows to create simple meshes of predefined shapes: cube, cylinder, sphere, icosehedron, cone, arrow."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[ { 'arrow_length_factor' : 'relative length of the head', 'arrow_radius' : 'radius of the tail', 'facets' : '(optional) number of facets of the cone section (default: 4)', 'point1' : '3D position of the head', 'point2' : '3D position of the center of the bottom', 'radius' : 'radius of the head', 'type' : 'arrow' }, { 'closed' : '(optional) if non-zero, make polygons for the cone end (default: 0)', 'facets' : '(optional) number of facets of the cone section (default: 4)', 'point1' : '3D position of the sharp end', 'point2' : '3D position of the center of the other end', 'radius' : 'radius of the 2nd end', 'smooth' : '(optional) make smooth normals and shared vertices (default: 0)', 'type' : 'cone' }, { 'center' : '3D position of the center', 'radius' : 'half-length of the edge', 'smooth' : '(optional) make smooth normals and shared vertices (default: 0)', 'type' : 'cube' }, { 'closed' : '(optional) if non-zero, make polygons for the cylinder ends (default: 0)', 'facets' : '(optional) number of facets of the cylinder section (default: 4)', 'point1' : '3D position of the center of the 1st end', 'point2' : '3D position of the center of the 2nd end', 'radius' : 'radius of the 1st end', 'radius2' : '(optional) radius of the 2nd end (default: same as radius)', 'smooth' : '(optional) make smooth normals and shared vertices (default: 0)', 'type' : 'cylinder' }, { 'center' : '3D position of the center, may also be specified as \\'point1\\' parameter', 'facets' : '(optional) number of facets of the sphere. May also be specified as \\'nfacets\\' parameter (default: 225)', 'radius1' : 'radius1', 'radius2' : 'radius2', 'type' : 'ellipse', 'uniquevertices' : '(optional) if set to 1, the pole vertices are not duplicated( default: 0)' }, { 'center' : '3D position of the center', 'radius' : 'radius', 'type' : 'icosahedron' }, { 'center' : '3D position of the center, may also be specified as \\'point1\\' parameter', 'facets' : '(optional) minimum number of facets of the sphere. (default: 30)', 'radius' : 'radius', 'type' : 'icosphere' }, { 'center' : '3D position of the center, may also be specified as \\'point1\\' parameter', 'facets' : '(optional) number of facets of the sphere. May also be specified as \\'nfacets\\' parameter (default: 225)', 'radius' : 'radius', 'type' : 'sphere', 'uniquevertices' : '(optional) if set to 1, the pole vertices are not duplicated( default: 0)' } ]\n"
]
}
],
"source": [
"from soma import aims\n",
"center = (50, 25, 20)\n",
"radius = 53\n",
"mesh1 = aims.SurfaceGenerator.icosahedron(center, radius)\n",
"mesh2 = aims.SurfaceGenerator.generate(\n",
" {'type': 'arrow', 'point1': [30, 70, 0],\n",
" 'point2': [100, 100, 100], 'radius': 20, 'arrow_radius': 30,\n",
" 'arrow_length_factor': 0.7, 'facets': 50})\n",
"# get the list of all possible generated objects and parameters:\n",
"print(aims.SurfaceGenerator.description())\n",
"assert('arrow_length_factor' in aims.SurfaceGenerator.description()[0])"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"\n",
"\n",
"\n",
"### Interpolation\n",
"\n",
"Interpolators help to get values in millimeters coordinates in a discrete space (volume grid), and may allow voxels values mixing (linear interpolation, typically)."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"collapsed": true,
"deletable": true,
"editable": true
},
"outputs": [],
"source": [
"from soma import aims\n",
"# load a functional volume\n",
"vol = aims.read('data_for_anatomist/subject01/Audio-Video_T_map.nii')\n",
"# get the position of the maximum\n",
"pmax, maxval = aims.AimsData_DOUBLE(vol).maxIndex()\n",
"# set pmax in mm\n",
"vs = vol.header()['voxel_size']\n",
"pmax = [x * y for x,y in zip(pmax, vs)]\n",
"# take a sphere of 5mm radius, with about 200 vertices\n",
"mesh = aims.SurfaceGenerator.sphere(pmax[:3], 5., 200)\n",
"vert = mesh.vertex()\n",
"# get an interpolator\n",
"interpolator = aims.aims.getLinearInterpolator(vol)\n",
"# create a texture for that sphere\n",
"tex = aims.TimeTexture_FLOAT()\n",
"tx = tex[0]\n",
"tx2 = tex[1]\n",
"tx.reserve(len(vert))\n",
"tx2.reserve(len(vert))\n",
"for v in vert:\n",
" tx.append(interpolator.value(v))\n",
" # compare to non-interpolated value\n",
" tx2.append(vol.value(*[int(round(x / y)) for x,y in zip(v, vs)]))\n",
"aims.write(tex, 'functional_tex.gii')\n",
"aims.write(mesh, 'sphere.gii')"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": true,
"editable": true
},
"source": [
"Look at the difference between the two timesteps (interpolated and non-interpolated) of the texture in Anatomist.\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"### Types conversion\n",
"\n",
"The `Converter_*_*` classes allow to convert some data structures types to others. \n",
"Of course all types cannot be converted to any other, but they are typically used ton convert volumed from a given voxel type to another one. \n",
"A \"factory\" function may help to build the correct converter using input and output types. \n",
"For instance, to convert the anatomical volume of the previous examples to float type:"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"collapsed": false,
"deletable": true,
"editable": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"type of vol: