[853718]: / bm_experiments / bm_DROP2.py

Download this file

223 lines (177 with data), 8.4 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
"""
Benchmark for DROP.
DROP is a approach for image registration and motion estimation based on Markov Random Fields.
.. seealso:: https://github.com/biomedia-mira/drop2
Related Publication:
1. Deformable Medical Image Registration: Setting the State of the Art with Discrete Methods
Authors: Ben Glocker, Aristeidis Sotiras, Nikos Komodakis, Nikos Paragios
Published in: Annual Review of Biomedical Engineering, Vol. 12, 2011, pp. 219-244
**Installation for Linux**
1. Download source code: https://github.com/biomedia-mira/drop2
2. Install all required libraries such as ITK, and build following the instructions
OR run building script `build.sh` included in the repository
3. Test calling the executable `./build/drop/apps/dropreg/dropreg` which should return something like::
Usage: dropreg --help
Usage
-----
Sample run of DROP2::
./dropreg --mode2d
-s S1.jpg -t HE.jpg -o S1_to_HE.png
-l --ltype 0 --lsim 1 --llevels 32 32 32 16 16 16 --lsampling 0.2
-n --nffd 1000 --nsim 1 --nlevels 16 16 16 8 8 8 --nlambda 0.5 --npin
Sample run::
mkdir ./results
python bm_experiments/bm_DROP2.py \
-t ./data-images/pairs-imgs-lnds_histol.csv \
-d ./data-images \
-o ./results \
-DROP $HOME/Applications/DROP2/dropreg \
--path_config ./configs/DROP2.txt \
--visual --unique
.. note:: experiments was tested on Ubuntu (Linux) based OS system
Copyright (C) 2017-2019 Jiri Borovec <jiri.borovec@fel.cvut.cz>
"""
import logging
import os
import shutil
import sys
import numpy as np
import SimpleITK as sitk
sys.path += [os.path.abspath('.'), os.path.abspath('..')] # Add path to root
from birl.utilities.data_io import load_config_args, load_landmarks, save_landmarks
from bm_experiments import bm_comp_perform
from bm_experiments.bm_DROP import BmDROP
class BmDROP2(BmDROP):
""" Benchmark for DROP2
no run test while this method requires manual installation of DROP2
For the app installation details, see module details.
Example
-------
>>> from birl.utilities.data_io import create_folder, update_path
>>> path_out = create_folder('temp_results')
>>> path_csv = os.path.join(update_path('data-images'), 'pairs-imgs-lnds_mix.csv')
>>> params = {'path_table': path_csv,
... 'path_out': path_out,
... 'nb_workers': 2,
... 'unique': False,
... 'visual': True,
... 'exec_DROP': 'dropreg',
... 'path_config': os.path.join(update_path('configs'), 'DROP2.txt')}
>>> benchmark = BmDROP2(params)
>>> benchmark.run() # doctest: +SKIP
>>> import shutil
>>> shutil.rmtree(path_out, ignore_errors=True)
"""
#: command for executing the image registration
COMMAND_REGISTER = '%(dropRegistration)s \
--mode2d \
--source %(source)s \
--target %(target)s \
--output %(output)s.jpeg \
%(config)s'
def _prepare_img_registration(self, item):
""" converting the input images to gra-scale and MHD format
:param dict item: dictionary with registration params
:return dict: the same or updated registration info
"""
# this version uses full images
return item
def _generate_regist_command(self, item):
""" generate the registration command
:param dict item: dictionary with registration params
:return str|list(str): the execution commands
"""
logging.debug('.. prepare DROP registration command')
config = load_config_args(self.params['path_config'])
path_im_ref, path_im_move, _, _ = self._get_paths(item)
path_dir = self._get_path_reg_dir(item)
def __cmd(p_target, p_source):
command = self.COMMAND_REGISTER % {
'dropRegistration': self.params['exec_DROP'],
'source': p_source,
'target': p_target,
'output': os.path.join(path_dir, 'output'),
'config': config,
}
return command
cmd_direct = __cmd(path_im_ref, path_im_move) + ' --ocompose'
return [cmd_direct]
def _extract_warped_image_landmarks(self, item):
""" get registration results - warped registered images and landmarks
:param dict item: dictionary with registration params
:return dict: paths to warped images/landmarks
"""
path_reg_dir = self._get_path_reg_dir(item)
_, path_im_move, path_lnds_ref, path_lnds_move = self._get_paths(item)
path_img_warp = os.path.join(path_reg_dir, os.path.basename(path_im_move))
shutil.move(os.path.join(path_reg_dir, 'output.jpeg'), path_img_warp)
# load transform and warp landmarks
lnds_ = load_landmarks(path_lnds_ref)
# this was in case you run inverse registration co you could warp the landmarks directly
# lnds_ = load_landmarks(path_lnds_move)
lnds_name = os.path.basename(path_lnds_move)
path_lnds_warp = os.path.join(path_reg_dir, lnds_name)
if lnds_ is None:
raise ValueError('missing landmarks to be transformed "%s"' % lnds_name)
# extract deformation
path_deform_x = os.path.join(path_reg_dir, 'output_field_x.nii.gz')
path_deform_y = os.path.join(path_reg_dir, 'output_field_y.nii.gz')
try:
shift = self.extract_landmarks_shift_from_nifty(path_deform_x, path_deform_y, lnds_)
except Exception:
logging.exception(path_reg_dir)
shift = np.zeros(lnds_.shape)
lnds_warp = lnds_ + shift
save_landmarks(path_lnds_warp, lnds_warp)
# return formatted results
return {
self.COL_IMAGE_MOVE_WARP: path_img_warp,
self.COL_POINTS_REF_WARP: path_lnds_warp,
}
def _clear_after_registration(self, item, patterns=('output*', '*.nii.gz')):
""" clean unnecessarily files after the registration
:param dict item: dictionary with registration information
:param list(str) patterns: string patterns of file names
:return dict: the same or updated registration info
"""
return super(BmDROP2, self)._clear_after_registration(item, patterns)
@staticmethod
def extract_landmarks_shift_from_nifty(path_deform_x, path_deform_y, lnds):
""" given pair of deformation fields and landmark positions get shift
:param str path_deform_x: path to deformation field in X axis
:param str path_deform_y: path to deformation field in Y axis
:param ndarray lnds: landmarks
:return ndarray: shift for each landmarks
"""
# define function for parsing particular shift from MHD
def __parse_shift(path_deform_, axis, lnds):
if not os.path.isfile(path_deform_):
raise FileNotFoundError('missing deformation: %s' % path_deform_)
img_ = sitk.ReadImage(path_deform_)
spacing = img_.GetSpacing()
deform_ = sitk.GetArrayFromImage(img_)[0].T
# deform_ = nibabel.load(path_deform_).get_data()[:, :, 0].T
if deform_ is None:
raise ValueError('loaded deformation is Empty - %s' % path_deform_)
lnds_max = np.max(lnds, axis=0)
if not all(ln < dim for ln, dim in zip(lnds_max, deform_.shape)):
raise ValueError(
'landmarks max %s is larger then (exceeded) deformation shape %s' %
(lnds_max.tolist(), deform_.shape)
)
# see: https://github.com/biomedia-mira/drop2/issues/2#issuecomment-547340836
shift_ = deform_[lnds[:, 0], lnds[:, 1]] / spacing[axis]
return shift_
lnds = np.array(np.round(lnds), dtype=int)
# get shift in both axis
shift_x = __parse_shift(path_deform_x, 0, lnds)
shift_y = __parse_shift(path_deform_y, 1, lnds)
# concatenate
shift = np.array([shift_x, shift_y]).T
return shift
# RUN by given parameters
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
arg_params, path_expt = BmDROP2.main()
if arg_params.get('run_comp_benchmark', False):
bm_comp_perform.main(path_expt)