Skip to content

Commit 05f0306

Browse files
committed
Merge remote-tracking branch 'origin/pr/847'
Conflicts: CHANGES nipype/algorithms/misc.py
2 parents 1a73d68 + 74b880a commit 05f0306

File tree

8 files changed

+190
-0
lines changed

8 files changed

+190
-0
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Next Release
55
``nipype`` namespace, and must be imported directly (e.g.
66
``from nipype.interfaces import fsl``).
77
* ENH: New FSL interface: ProbTrackX2
8+
* ENH: New misc algorithm: NormalizeProbabilityMapSet
89
* ENH: New ANTs interface: ApplyTransformsToPoints
910
* ENH: New metrics group in algorithms. Now Distance, Overlap, and FuzzyOverlap
1011
are found in nipype.algorithms.metrics instead of misc

nipype/algorithms/misc.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,110 @@ def calc_moments(timeseries_file, moment):
839839
zero = (m2 == 0)
840840
return np.where(zero, 0, m3 / m2**(moment/2.0))
841841

842+
843+
class NormalizeProbabilityMapSetInputSpec(TraitedSpec):
844+
in_files = InputMultiPath(File(exists=True, mandatory=True,
845+
desc='The tpms to be normalized') )
846+
in_mask = File(exists=True, mandatory=False,
847+
desc='Masked voxels must sum up 1.0, 0.0 otherwise.')
848+
849+
class NormalizeProbabilityMapSetOutputSpec(TraitedSpec):
850+
out_files = OutputMultiPath(File(exists=True),
851+
desc="normalized maps")
852+
853+
854+
class NormalizeProbabilityMapSet(BaseInterface):
855+
""" Returns the input tissue probability maps (tpms, aka volume fractions)
856+
normalized to sum up 1.0 at each voxel within the mask.
857+
858+
.. note:: Please recall this is not a spatial normalization algorithm
859+
860+
Example
861+
-------
862+
863+
>>> import nipype.algorithms.misc as misc
864+
>>> normalize = misc.NormalizeProbabilityMapSet()
865+
>>> normalize.inputs.in_files = [ 'tpm_00.nii.gz', 'tpm_01.nii.gz', 'tpm_02.nii.gz' ]
866+
>>> normalize.inputs.in_mask = 'tpms_msk.nii.gz'
867+
>>> normalize.run() # doctest: +SKIP
868+
"""
869+
input_spec = NormalizeProbabilityMapSetInputSpec
870+
output_spec = NormalizeProbabilityMapSetOutputSpec
871+
872+
def _run_interface(self, runtime):
873+
mask = None
874+
875+
if isdefined( self.inputs.in_mask ):
876+
mask = self.inputs.in_mask
877+
878+
self._out_filenames = normalize_tpms( self.inputs.in_files, mask )
879+
return runtime
880+
881+
def _list_outputs(self):
882+
outputs = self.output_spec().get()
883+
outputs['out_files'] = self._out_filenames
884+
return outputs
885+
886+
887+
def normalize_tpms( in_files, in_mask=None, out_files=[] ):
888+
"""
889+
Returns the input tissue probability maps (tpms, aka volume fractions)
890+
normalized to sum up 1.0 at each voxel within the mask.
891+
"""
892+
import nibabel as nib
893+
import numpy as np
894+
import os.path as op
895+
896+
in_files = np.atleast_1d( in_files ).tolist()
897+
898+
if len(out_files)!=len(in_files):
899+
for i,finname in enumerate( in_files ):
900+
fname,fext = op.splitext( op.basename( finname ) )
901+
if fext == '.gz':
902+
fname,fext2 = op.splitext( fname )
903+
fext = fext2 + fext
904+
905+
out_file = op.abspath(fname+'_norm'+('_%02d' % i)+fext)
906+
out_files+= [out_file]
907+
908+
imgs = [nib.load(fim) for fim in in_files]
909+
910+
if len(in_files)==1:
911+
img_data = imgs[0].get_data()
912+
img_data[img_data>0.0] = 1.0
913+
hdr = imgs[0].get_header().copy()
914+
hdr['data_type']= 16
915+
hdr.set_data_dtype( 'float32' )
916+
nib.save( nib.Nifti1Image( img_data.astype(np.float32), imgs[0].get_affine(), hdr ), out_files[0] )
917+
return out_files[0]
918+
919+
img_data = np.array( [ im.get_data() for im in imgs ] ).astype( 'f32' )
920+
#img_data[img_data>1.0] = 1.0
921+
img_data[img_data<0.0] = 0.0
922+
weights = np.sum( img_data, axis=0 )
923+
924+
msk = np.ones_like( imgs[0].get_data() )
925+
msk[ weights<= 0 ] = 0
926+
927+
if not in_mask is None:
928+
msk = nib.load( in_mask ).get_data()
929+
msk[ msk<=0 ] = 0
930+
msk[ msk>0 ] = 1
931+
932+
msk = np.ma.masked_equal( msk, 0 )
933+
934+
935+
for i,out_file in enumerate( out_files ):
936+
data = np.ma.masked_equal( img_data[i], 0 )
937+
probmap = data / weights
938+
hdr = imgs[i].get_header().copy()
939+
hdr['data_type']= 16
940+
hdr.set_data_dtype( 'float32' )
941+
nib.save( nib.Nifti1Image( probmap.astype(np.float32), imgs[i].get_affine(), hdr ), out_file )
942+
943+
return out_files
944+
945+
842946
# Deprecated interfaces ---------------------------------------------------------
843947
class Distance( nam.Distance ):
844948
"""Calculates distance between two volumes.
@@ -877,3 +981,4 @@ def __init__(self, **inputs):
877981
warnings.warn(("This interface has been deprecated since 0.10.0,"
878982
" please use nipype.algorithms.metrics.FuzzyOverlap"),
879983
DeprecationWarning)
984+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
2+
from nipype.testing import assert_equal
3+
from nipype.algorithms.misc import NormalizeProbabilityMapSet
4+
5+
def test_NormalizeProbabilityMapSet_inputs():
6+
input_map = dict(in_files=dict(),
7+
in_mask=dict(mandatory=False,
8+
),
9+
)
10+
inputs = NormalizeProbabilityMapSet.input_spec()
11+
12+
for key, metadata in input_map.items():
13+
for metakey, value in metadata.items():
14+
yield assert_equal, getattr(inputs.traits()[key], metakey), value
15+
16+
def test_NormalizeProbabilityMapSet_outputs():
17+
output_map = dict(out_files=dict(),
18+
)
19+
outputs = NormalizeProbabilityMapSet.output_spec()
20+
21+
for key, metadata in output_map.items():
22+
for metakey, value in metadata.items():
23+
yield assert_equal, getattr(outputs.traits()[key], metakey), value
24+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
4+
# vi: set ft=python sts=4 ts=4 sw=4 et:
5+
#
6+
# @Author: oesteban - code@oscaresteban.es
7+
# @Date: 2014-05-28 17:57:20
8+
# @Last Modified by: oesteban
9+
# @Last Modified time: 2014-05-29 13:43:09
10+
11+
import os
12+
from shutil import rmtree
13+
from tempfile import mkdtemp
14+
15+
from nipype.testing import (assert_equal,assert_raises,
16+
assert_almost_equal,example_data )
17+
18+
import numpy as np
19+
import nibabel as nb
20+
import nipype.testing as nit
21+
22+
from nipype.algorithms.misc import normalize_tpms
23+
24+
def test_normalize_tpms():
25+
tempdir = mkdtemp()
26+
27+
in_mask = example_data('tpms_msk.nii.gz' )
28+
mskdata = nb.load( in_mask ).get_data()
29+
mskdata[mskdata>0.0] = 1.0
30+
31+
mapdata = []
32+
in_files = []
33+
out_files = []
34+
35+
for i in range(3):
36+
mapname = example_data('tpm_%02d.nii.gz' % i)
37+
filename = os.path.join(tempdir, 'modtpm_%02d.nii.gz' % i )
38+
out_files.append(os.path.join(tempdir, 'normtpm_%02d.nii.gz' % i ))
39+
40+
im = nb.load(mapname)
41+
data = im.get_data()
42+
mapdata.append( data.copy() )
43+
44+
nb.Nifti1Image(2.0 * (data * mskdata), im.get_affine(),
45+
im.get_header() ).to_filename(filename)
46+
in_files.append( filename )
47+
48+
normalize_tpms( in_files, in_mask, out_files=out_files )
49+
50+
sumdata = np.zeros_like(mskdata)
51+
52+
for i,tstfname in enumerate( out_files ):
53+
normdata = nb.load( tstfname ).get_data()
54+
sumdata+=normdata
55+
yield assert_equal, np.all( normdata[mskdata==0]==0 ), True
56+
yield assert_equal, np.allclose( normdata, mapdata[i] ), True
57+
58+
yield assert_equal, np.allclose(sumdata[sumdata>0.0], 1.0 ), True
59+
60+
rmtree(tempdir)

nipype/testing/data/tpm_00.nii.gz

157 KB
Binary file not shown.

nipype/testing/data/tpm_01.nii.gz

137 KB
Binary file not shown.

nipype/testing/data/tpm_02.nii.gz

136 KB
Binary file not shown.

nipype/testing/data/tpms_msk.nii.gz

20 KB
Binary file not shown.

0 commit comments

Comments
 (0)