Skip to content

Commit

Permalink
Added the reporting of quality metric values.
Browse files Browse the repository at this point in the history
1) The distance between the images after registration is reported.
2) The metric of optiaml parameters is reported during the workflow.
3) The quality assurance metric can be saved optionally by the user.

4) Tested the workflows on simulated (coronal slice dataset) and real datasets (stanford hardi).
  • Loading branch information
Parichit Sharma committed Jul 1, 2018
1 parent c3b4786 commit 94c8b8f
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 48 deletions.
15 changes: 14 additions & 1 deletion dipy/align/imaffine.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ def _init_optimizer(self, static, moving, transform, params0,

def optimize(self, static, moving, transform, params0,
static_grid2world=None, moving_grid2world=None,
starting_affine=None):
starting_affine=None, ret_metric=False):
r''' Starts the optimization process
Parameters
Expand Down Expand Up @@ -993,11 +993,22 @@ def optimize(self, static, moving, transform, params0,
If None:
Start from identity.
The default is None.
ret_metric : boolean, optional
if True, it returns the parameters for measuring the
similarity between the images (default 'False').
The metric containing optimal parameters and
the distance between the images.
Returns
-------
affine_map : instance of AffineMap
the affine resulting affine transformation
xopt : similarity metric
the metric of optimal parameters
fopt : distance
the distance between the images
'''
self._init_optimizer(static, moving, transform, params0,
static_grid2world, moving_grid2world,
Expand Down Expand Up @@ -1074,6 +1085,8 @@ def optimize(self, static, moving, transform, params0,
self.params0 = self.transform.get_identity_parameters()

affine_map.set_affine(self.starting_affine)
if ret_metric:
return affine_map, opt.xopt, opt.fopt
return affine_map


Expand Down
25 changes: 24 additions & 1 deletion dipy/io/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,32 @@ def save_affine_matrix(fname, affine):
"""
Parameters
---------
fname : str
fname : string
File name to save the affine matrix.
affine : numpy array
The object containing the affine matrix.
"""
np.savetxt(fname, affine)


def save_quality_assur_metric(fname, xopt, fopt):
"""
Parameters
__________
fname: string
File name to save the metric values.
xopt: numpy array
The metric containing the
optimal parameters for
image registration.
fopt: int
The distance between the registered images.
"""
np.savetxt(fname, xopt, header="Optimal Parameter metric")
with open(fname,'a') as f:
f.write('# Distance after registration\n')
f.write(str(fopt))




101 changes: 66 additions & 35 deletions dipy/workflows/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
MutualInformationMetric, AffineRegistration)
from dipy.align.transforms import (TranslationTransform3D, RigidTransform3D,
AffineTransform3D)
from dipy.io.image import save_nifti, save_affine_matrix
from dipy.io.image import save_nifti, save_affine_matrix, save_quality_assur_metric


class ResliceFlow(Workflow):
Expand Down Expand Up @@ -163,13 +163,16 @@ def translate(self, static, static_grid2world, moving,
transform = TranslationTransform3D()
starting_affine = affine

img_registration = affreg.optimize(static, moving, transform,
params0, static_grid2world,
moving_grid2world,
starting_affine=starting_affine)
img_registration, xopt, fopt = affreg.optimize(static, moving,
transform, params0,
static_grid2world,
moving_grid2world,
starting_affine=
starting_affine,
ret_metric=True)

transformed = img_registration.transform(moving)
return transformed, img_registration.affine
return transformed, img_registration.affine, xopt, fopt

def rigid(self, static, static_grid2world, moving, moving_grid2world,
affreg, params0, progressive):
Expand Down Expand Up @@ -213,31 +216,32 @@ def rigid(self, static, static_grid2world, moving, moving_grid2world,
"""

if progressive:
moved, affine = self.translate(static,
static_grid2world,
moving, moving_grid2world,
affreg, params0)
moved, affine, xopt, fopt = self.translate(static,
static_grid2world,
moving,
moving_grid2world,
affreg, params0)

else:
moved, affine = self.center_of_mass(static,
static_grid2world,
moving,
moving_grid2world)
moved, affine = self.center_of_mass(static, static_grid2world,
moving, moving_grid2world)

transform = RigidTransform3D()
starting_affine = affine

img_registration = affreg.optimize(static, moving, transform,
params0, static_grid2world,
moving_grid2world,
starting_affine=starting_affine)
img_registration, xopt, fopt = affreg.optimize(static, moving,
transform, params0,
static_grid2world,
moving_grid2world,
starting_affine=
starting_affine,
ret_metric=True)

transformed = img_registration.transform(moving)
return transformed, img_registration.affine
return transformed, img_registration.affine, xopt, fopt

def affine(self, static, static_grid2world, moving, moving_grid2world,
affreg, params0,
progressive):
affreg, params0, progressive):

""" Function for full affine registration.
Expand Down Expand Up @@ -277,7 +281,7 @@ def affine(self, static, static_grid2world, moving, moving_grid2world,
"""
if progressive:
moved, affine = self.rigid(static, static_grid2world,
moved, affine, xopt, fopt = self.rigid(static, static_grid2world,
moving, moving_grid2world,
affreg, params0, progressive)

Expand All @@ -288,13 +292,16 @@ def affine(self, static, static_grid2world, moving, moving_grid2world,
transform = AffineTransform3D()
starting_affine = affine

img_registration = affreg.optimize(static, moving, transform,
params0, static_grid2world,
moving_grid2world,
starting_affine=starting_affine)
img_registration, xopt, fopt = affreg.optimize(static, moving,
transform, params0,
static_grid2world,
moving_grid2world,
starting_affine=
starting_affine,
ret_metric=True)

transformed = img_registration.transform(moving)
return transformed, img_registration.affine
return transformed, img_registration.affine, xopt, fopt

@staticmethod
def check_dimensions(static, moving):
Expand Down Expand Up @@ -338,8 +345,9 @@ def check_metric(metric):
def run(self, static_img_file, moving_img_file, transform='affine',
nbins=32, sampling_prop=None, metric='mi',
level_iters=[10000, 1000, 100], sigmas=[3.0, 1.0, 0.0],
factors=[4, 2, 1], progressive=True, out_dir='',
out_moved='moved.nii.gz', out_affine='affine.txt'):
factors=[4, 2, 1], progressive=True, save_metric=True,
out_dir='', out_moved='moved.nii.gz', out_affine='affine.txt',
out_quality='quality_metric.txt'):

"""
Parameters
Expand Down Expand Up @@ -392,6 +400,17 @@ def run(self, static_img_file, moving_img_file, transform='affine',
Flag for enabling/disabling the progressive registration.
(default 'True')
save_metric : boolean, optional
If true, the metric values are
saved in a file called 'quality_metric.txt'
(default 'False')
By default, the similarity measure
values such as the distance and the
metric of optimal parameters is only
displayed but not saved.
out_dir : string, optional
Directory to save the transformed image and the affine matrix.
(default '')
Expand All @@ -404,14 +423,19 @@ def run(self, static_img_file, moving_img_file, transform='affine',
Name for the saved affine matrix.
(default 'affine.txt')
out_quality : string, optional
Name of the file containing the saved quality
metrices (default 'quality_metric.txt')
"""

"""
setting up the io iterator to gobble the input and output paths
"""
io_it = self.get_io_iterator()

for static_img, mov_img, moved_file, affine_matrix_file in io_it:

for static_img, mov_img, moved_file, affine_matrix_file, qual_val_file in io_it:

"""
Load the data from the input files and store into objects.
Expand Down Expand Up @@ -447,22 +471,22 @@ def run(self, static_img_file, moving_img_file, transform='affine',
factors=factors)

if transform.lower() == 'trans':
moved_image, affine = self.translate(static,
moved_image, affine, xopt, fopt = self.translate(static,
static_grid2world,
moving,
moving_grid2world,
affreg, params0)

elif transform.lower() == 'rigid':
moved_image, affine = self.rigid(static,
moved_image, affine, xopt, fopt = self.rigid(static,
static_grid2world,
moving,
moving_grid2world,
affreg, params0,
progressive)

elif transform.lower() == 'affine':
moved_image, affine = self.affine(static,
moved_image, affine, xopt, fopt = self.affine(static,
static_grid2world,
moving,
moving_grid2world,
Expand All @@ -478,5 +502,12 @@ def run(self, static_img_file, moving_img_file, transform='affine',
Saving the moved image file and the affine matrix.
"""

save_nifti(moved_file, moved_image, static_grid2world)
save_affine_matrix(affine_matrix_file, affine)
logging.info("Similarity metric:"+str(xopt))
logging.info("Distance measure:"+str(fopt))

if save_metric:
save_quality_assur_metric(qual_val_file, xopt, fopt)

#save_nifti(moved_file, moved_image, static_grid2world)
#save_affine_matrix(affine_matrix_file, affine)

2 changes: 1 addition & 1 deletion dipy/workflows/flow_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def run_flow(flow):
help='Force overwriting output files.')

parser.add_argument('--out_strat', action='store', dest='out_strat',
metavar='string', required=False, default='append',
metavar='string', required=False, default='absolute',
help='Strategy to manage output creation.')

parser.add_argument('--mix_names', dest='mix_names',
Expand Down
28 changes: 18 additions & 10 deletions dipy/workflows/tests/test_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_reslice():


def test_image_registration():
folder_path = '/Users/schmuck/Documents/register_workflow_test/test'

with TemporaryDirectory() as temp_out_dir:
static, moving, static_g2w, moving_g2w, smask, mmask, M = setup_random_transform(
transform=regtransforms[('AFFINE', 3)], rfactor=0.1)
Expand All @@ -62,7 +62,8 @@ def test_image_registration():
transform='com',
out_dir=temp_out_dir
, out_moved=out_moved,
out_affine=out_affine)
out_affine=out_affine,
level_iters="100 10 1")

npt.assert_equal(os.path.exists(out_moved), True)
npt.assert_equal(os.path.exists(out_affine), True)
Expand All @@ -75,7 +76,8 @@ def test_image_registration():
transform='trans',
out_dir=temp_out_dir
, out_moved=out_moved,
out_affine=out_affine)
out_affine=out_affine,
level_iters="100 10 1")

npt.assert_equal(os.path.exists(out_moved), True)
npt.assert_equal(os.path.exists(out_affine), True)
Expand All @@ -88,7 +90,8 @@ def test_image_registration():
transform='rigid',
out_dir=temp_out_dir
, out_moved=out_moved,
out_affine=out_affine)
out_affine=out_affine,
level_iters="100 10 1")

npt.assert_equal(os.path.exists(out_moved), True)
npt.assert_equal(os.path.exists(out_affine), True)
Expand All @@ -101,7 +104,8 @@ def test_image_registration():
transform='affine',
out_dir=temp_out_dir
, out_moved=out_moved,
out_affine=out_affine)
out_affine=out_affine,
level_iters="100 10 1")

npt.assert_equal(os.path.exists(out_moved), True)
npt.assert_equal(os.path.exists(out_affine), True)
Expand All @@ -119,14 +123,18 @@ def test_image_registration():
moving_image_file,
metric='wrong_metric')

copy_output(temp_out_dir, folder_path)

# Uncomment for manual debugging
#copy_output(temp_out_dir)

def copy_output(temp_directory_path, folder_path):
out_files = list(glob(pjoin(temp_directory_path, '*.nii.gz')) + glob(pjoin(temp_directory_path, '*.txt')))

for out_file in out_files:
shutil.copy(out_file, folder_path)
# def copy_output(temp_directory_path):
# set the folder_path to the directory where the registered images will be copied.
# folder_path = ''
# out_files = list(glob(pjoin(temp_directory_path, '*.nii.gz')) + glob(pjoin(temp_directory_path, '*.txt')))
#
# for out_file in out_files:
# shutil.copy(out_file, folder_path)


if __name__ == '__main__':
Expand Down

0 comments on commit 94c8b8f

Please sign in to comment.