template_creation.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. from helper_functions import files_in_dir, make_dirs, add_slash_to_path, flip_volume_lr, robust_avg_volumes,\
  2. files_ind_dir_wo_string, avg_template
  3. import sys
  4. import getopt
  5. import os
  6. import shutil
  7. from nipype.interfaces import ants
  8. # Help message.
  9. tab = ' '
  10. usage_message = '\nCOMMAND:\n' + tab + sys.argv[0] + \
  11. '\n\nOPTIONS:\n' + \
  12. tab + '-h, help menu\n' + \
  13. tab + '-i, --images, <list of input images> or path to directory with only .nii images.\n' + \
  14. tab + '-o, --output, <path to output directory>\n' + \
  15. tab + '-s, --symmetric, [bool] use symmetric modelling. Default: False. Currently only works for 3d images.\n' + \
  16. tab + '-t, --template, <path to template> can include template. Will not be included in final average. \n' + \
  17. tab + '-w, --weight, [Double] specifies the weight a given template will have in averages. From 0 to 1. Default: 1/(number of images + 1)\n' + \
  18. tab + '-l, --log, [bool]>\n'
  19. # Registration
  20. def registration(fixed, moving, result, reg_type, sigma, shrink, param, dim, initial_trans='', inverse_result=''):
  21. """
  22. Registration function. Registeres the moving image to the fixed with given parameters. If initial transform is not
  23. given then the images are aligned by center of mass. Outputs inversely transformed result if given.
  24. :param fixed: Path to fixed image.
  25. :param moving: Path to moving image.
  26. :param result: Path to result image.
  27. :param reg_type: Registration type. List containing, Rigid, Affine, and/or SyN.
  28. :param sigma: List of sigma parameters. Ordered according to the reg_type parameter.
  29. :param shrink: List of shrink parameters. Ordered according to the reg_type parameter.
  30. :param param: List of transform parameters. Ordered according to the reg_type parameter.
  31. :param dim: Dimension of images. Typically 2 or 3.
  32. :param initial_trans: Optional path to initial moving transform. If not given, the images will be matched initially
  33. by aligning the center of mass.
  34. :param inverse_result: Optional path to the inversely transformed fixed image.
  35. :return: Returns nothing.
  36. """
  37. # Extract number of registrations.
  38. steps = len(reg_type)
  39. # Add 1000 iterations for each step.
  40. if steps == 1:
  41. iterations = [[1000] * len(sigma[0])]
  42. else:
  43. iterations = []
  44. for i, reg in enumerate(reg_type):
  45. iteration = [1000] * len(sigma[i])
  46. iterations.append(iteration)
  47. # Create registration instance.
  48. reg_instance = ants.Registration(dimension=dim,
  49. transforms=reg_type,
  50. transform_parameters=param,
  51. metric=['MI']*steps,
  52. interpolation='BSpline',
  53. fixed_image=[fixed],
  54. moving_image=[moving],
  55. metric_weight=[1]*steps,
  56. radius_or_number_of_bins=[32]*steps,
  57. number_of_iterations=iterations,
  58. smoothing_sigmas=sigma,
  59. shrink_factors=shrink,
  60. convergence_threshold=[1.e-7],
  61. sampling_strategy=['Regular']*steps,
  62. use_histogram_matching=True,
  63. winsorize_lower_quantile=0.05,
  64. winsorize_upper_quantile=0.95,
  65. output_warped_image=result,
  66. write_composite_transform=True,
  67. output_transform_prefix=result.split('.')[0] + '-Transformation',
  68. num_threads=12)
  69. # Add initial moving transform if given, else match by center of mass.
  70. if not initial_trans == '':
  71. reg_instance.inputs.initial_moving_transform = initial_trans
  72. else:
  73. reg_instance.inputs.initial_moving_transform_com = 1
  74. # Output reverse results if path is given.
  75. if not inverse_result == '':
  76. reg_instance.inputs.output_inverse_warped_image = inverse_result
  77. # Run registration.
  78. reg_instance.run()
  79. def create_template_from_images(images_input, results_dir, symmetric=False, template='', template_weight=1,
  80. r=3, a=2, nl=3, print_log=False):
  81. """
  82. Population based template creation from input images. If input template is given this will be part of the averaging
  83. with weight template_weight. Will perform r rigid, a rigid and affine, and nl rigid, affine, and non-linear
  84. iterations. Will flip all input images in the left-right plane if symmetric is set to true.
  85. :param images_input: List of paths to images.
  86. :param results_dir: Working directory.
  87. :param symmetric: Boolean [Default: False]. Will flip all input images in the left-right plane if symmetric is set
  88. to true.
  89. :param template: str [Default: '']. Will include template in averaging if path is specified.
  90. :param template_weight: float [Default: 1]. Will weight the template with given weight if set to different from 1.
  91. Can be in the range of 0 < template_weight < 1.
  92. :param r: Int [Default: 3]. Number of rigid registrations.
  93. :param a: Int [Default: 2]. Number of rigid and affine registrations.
  94. :param nl: Int [Default: 3]. Number of rigid, affine, and non-linear registrations.
  95. :param print_log: Boolean [Default: False]. Will print log if true.
  96. :return: Returns nothing.
  97. """
  98. out_dir = add_slash_to_path(results_dir)
  99. make_dirs(out_dir)
  100. template_present = not template == ''
  101. if print_log:
  102. print('Determining images-variable format and defining input images.')
  103. if isinstance(images_input, list):
  104. images = images_input
  105. elif isinstance(images_input, str):
  106. images_input = add_slash_to_path(images_input)
  107. images = files_in_dir(images_input, '.nii')
  108. all_images = images
  109. if symmetric:
  110. flipped_path = out_dir + 'raw_flipped/'
  111. flipped_images = []
  112. make_dirs(flipped_path)
  113. if print_log:
  114. print('Creating flipped images.')
  115. for image in images:
  116. flipped_image = flipped_path + image.split('/')[-1].split('.')[0] + '_flipped.nii'
  117. if not os.path.exists(flipped_image):
  118. flip_volume_lr(image, flipped_image)
  119. else:
  120. if print_log:
  121. print('Flipped image', flipped_image, 'already present.')
  122. flipped_images.append(flipped_image)
  123. all_images.extend(flipped_images)
  124. if print_log:
  125. print('Defining iterations.')
  126. reg_types = ['Rigid'] * r + ['Affine'] * a + ['SyN'] * nl
  127. if print_log:
  128. print(reg_types)
  129. for i, iteration in enumerate(reg_types):
  130. iteration_nr = str(i).zfill(2)
  131. cur_it_dir = out_dir + iteration_nr + '/'
  132. make_dirs(cur_it_dir)
  133. if i == 0:
  134. robust_avg_volumes(all_images, out_dir + '00-average.nii')
  135. if template_present:
  136. avg_template(out_dir + '00-average.nii',
  137. template,
  138. template_weight,
  139. out_dir + '00-average-template.nii')
  140. else:
  141. shutil.rmtree(out_dir + str(i-1).zfill(2))
  142. average = out_dir + iteration_nr + '-average-template.nii' if template_present else out_dir + iteration_nr + '-average.nii'
  143. # Iteration specific registration parameters.
  144. if iteration == 'Rigid':
  145. reg = ['Rigid']
  146. params = [(0.25,)]
  147. sigma = [[4, 4, 2, 2]]
  148. shrink = [[32, 16, 8, 4]]
  149. if i == 0:
  150. sigma = [[4, 4, 4]]
  151. shrink = [[32, 16, 8]]
  152. elif iteration == 'Affine':
  153. reg = ['Rigid', 'Affine']
  154. params = [(0.25,), (0.25,), ]
  155. sigma = [[4, 4, 2, 2], [4, 4, 2, 2]]
  156. shrink = [[32, 16, 8, 4], [32, 16, 8, 4]]
  157. elif iteration == 'SyN':
  158. reg = ['Rigid', 'Affine', 'SyN']
  159. params = [(0.25,), (0.25,), (0.15, 3, 0.5)]
  160. sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2]]
  161. shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8]]
  162. if i == len(reg_types) - 1:
  163. sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2, 1]]
  164. shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8, 4]]
  165. else:
  166. sys.exit('The registration type "' + str(iteration) + '" does not exist. Reconstruction stopped.')
  167. if template_present:
  168. all_images.extend([template])
  169. for image in all_images:
  170. reg_output = cur_it_dir + image.split('/')[-1]
  171. registration(average, image, reg_output, reg, sigma, shrink, params, dim=3)
  172. registered_images = files_ind_dir_wo_string(cur_it_dir, '-Transformation')
  173. registered_images_wo_allen = []
  174. for reg_image in registered_images:
  175. if 'allen' in reg_image:
  176. registered_allen = reg_image
  177. else:
  178. registered_images_wo_allen.append(reg_image)
  179. robust_avg_volumes(registered_images_wo_allen, out_dir + str(i+1).zfill(2) + '-average.nii')
  180. if template_present:
  181. avg_template(out_dir + str(i+1).zfill(2) + '-average.nii',
  182. registered_allen,
  183. template_weight,
  184. out_dir + str(i + 1).zfill(2) + '-average-template.nii')
  185. if __name__ == '__main__':
  186. arguments = sys.argv
  187. input_symmetric = False
  188. input_robust = True
  189. input_template = ''
  190. input_weight = 1
  191. show_log = False
  192. if len(arguments) == 1:
  193. print(usage_message)
  194. else:
  195. try:
  196. opts, args = getopt.getopt(arguments[1:], "hi:o:s:t:w:l:", ["images=", "output=", "symmetric=",
  197. "template=", "weight=", "log="])
  198. except getopt.GetoptError:
  199. print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
  200. sys.exit(2)
  201. for opt, arg in opts:
  202. if opt == "-h":
  203. print(usage_message)
  204. sys.exit()
  205. elif opt in ("-i", "--images"):
  206. input_images = arg
  207. elif opt in ("-o", "--output"):
  208. output_directory = arg
  209. elif opt in ("-s", "--symmetric"):
  210. if arg == 'True' or arg == 'true':
  211. input_symmetric = True
  212. elif arg == 'False' or arg == 'false':
  213. input_symmetric = False
  214. else:
  215. print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
  216. elif opt in ("-t", "--template"):
  217. input_template = arg
  218. elif opt in ("-w", "--weight"):
  219. input_weight = arg
  220. elif opt in ("-l", "--log"):
  221. if arg == 'True' or arg == 'true':
  222. show_log = True
  223. elif arg == 'False' or arg == 'false':
  224. show_log = False
  225. else:
  226. print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
  227. sys.exit(2)
  228. try:
  229. create_template_from_images(input_images,
  230. output_directory,
  231. symmetric=input_symmetric,
  232. template=input_template,
  233. template_weight=input_weight,
  234. print_log=show_log)
  235. except NameError:
  236. print('\nYou are not (properly) defining all the input variables. Tak a look at what is needed:\n',
  237. usage_message)