def test_get_displacement_norm_between_images(self): shapes = { 2: (100, 200), 3: (100, 200, 50), } origin = { 2: (-100, 10), 3: (-100, 10, 33.3), } direction = { 2: (0, -1, 1, 0), 3: (0, 1, 0, 1, 0, 0, 0, 0, -1), } spacing = { 2: (1.1, 2.5), 3: (1.1, 2.5, 5), } parameters = { 2: (-1.7, -10.23, 12), 3: (0.3, -1.3, 2.1, -10.23, 12, 4), } center = { 2: (-10, 15), 3: (-10, 15, 17.3), } for dim in [2, 3]: print("Dimension: %d" % dim) transform_sitk = getattr(sitk, "Euler%dDTransform" % dim)() transform_sitk.SetParameters(parameters[dim]) transform_sitk.SetCenter(center[dim]) image_sitk = sitk.Image(shapes[dim], sitk.sitkFloat32) image_sitk.SetOrigin(origin[dim]) image_sitk.SetDirection(direction[dim]) image_sitk.SetSpacing(spacing[dim]) # -------------------Compute mean displacements------------------- t0 = ph.start_timing() norm_disp = utils.get_voxel_displacements(image_sitk, transform_sitk) print("Time method: %s" % ph.stop_timing(t0)) # -----------------------Compute ref result----------------------- disp_shape = sitk.GetArrayFromImage(image_sitk).shape t0 = ph.start_timing() disp = np.zeros(disp_shape + (dim, )) for index in np.ndindex(shapes[dim]): point = image_sitk.TransformIndexToPhysicalPoint(index) point_ref = transform_sitk.TransformPoint(point) disp[index[::-1]] = point - np.array(point_ref) norm_disp_ref = np.sqrt(np.sum(np.square(disp), axis=-1)) print("Time reference: %s" % ph.stop_timing(t0)) self.assertAlmostEqual(np.linalg.norm(norm_disp - norm_disp_ref), 0, places=self.precision)
def main(): pid = "49" method = "manual" #auto, manual input_dir = "{0:}/input".format(pid) mask_dir = "{0:}/mask_{1:}/mask".format(pid, method) output_dir = "{0:}/input_preprocess".format(pid) # get input stack names files = os.listdir(input_dir) input_files = [] mask_files = [] for file in files: if (("nii.gz" in file)): input_files.append("{0:}/{1:}".format(input_dir, file)) mask_name = "{0:}/{1:}".format(mask_dir, file) assert(os.path.isfile(mask_name)) mask_files.append(mask_name) cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(input_files)) cmd_args.append("--filenames-masks %s" % (" ").join(mask_files)) cmd_args.append("--dir-output %s" % output_dir) cmd_args.append("--prefix-output ''") cmd = "niftymic_correct_bias_field %s" % (" ").join(cmd_args) time_start_bias = ph.start_timing() exit_code = ph.execute_command(cmd) elapsed_time_bias = ph.stop_timing(time_start_bias) print("Computational Time for Bias Field Correction: %s" % elapsed_time_bias) if exit_code != 0: raise RuntimeError("Bias field correction failed") return 0
def run_dilation(self): time_start = ph.start_timing() dilater = sitk.BinaryDilateImageFilter() dilater.SetKernelType(eval("sitk.sitk" + self._dilation_kernel)) dilater.SetKernelRadius(self._dilation_radius) if self._use_dilation_in_plane_only: shape = self._mask_sitk.GetSize() N_slices = shape[2] nda_mask = np.zeros(shape[::-1], dtype=np.uint8) for i in range(0, N_slices): slice_mask_sitk = self._mask_sitk[:, :, i:i + 1] mask_sitk = dilater.Execute(slice_mask_sitk) nda_mask[i, :, :] = sitk.GetArrayFromImage(mask_sitk) mask_sitk = sitk.GetImageFromArray(nda_mask) mask_sitk.CopyInformation(self._mask_sitk) self._mask_sitk = mask_sitk else: self._mask_sitk = dilater.Execute(self._mask_sitk) if self._stack is not None: self._stack = st.Stack.from_sitk_image( self._stack.sitk, image_sitk_mask=self._mask_sitk, filename=self._stack.get_filename()) self._computational_time = ph.stop_timing(time_start)
def run_bias_field_correction(self): time_start = ph.start_timing() bias_field_corrector = sitk.N4BiasFieldCorrectionImageFilter() bias_field_corrector.SetBiasFieldFullWidthAtHalfMaximum( self._bias_field_fwhm) bias_field_corrector.SetConvergenceThreshold( self._convergence_threshold) bias_field_corrector.SetSplineOrder(self._spline_order) bias_field_corrector.SetWienerFilterNoise(self._wiener_filter_noise) if self._use_mask: image_sitk = bias_field_corrector.Execute(self._stack.sitk, self._stack.sitk_mask) else: image_sitk = bias_field_corrector.Execute(self._stack.sitk) # Reading of image might lead to slight differences stack_corrected_sitk_mask = sitk.Resample( self._stack.sitk_mask, image_sitk, sitk.Euler3DTransform(), sitk.sitkNearestNeighbor, 0, self._stack.sitk_mask.GetPixelIDValue()) self._stack_corrected = st.Stack.from_sitk_image( image_sitk=image_sitk, image_sitk_mask=stack_corrected_sitk_mask, filename=self._prefix_corrected + self._stack.get_filename(), ) # Get computational time self._computational_time = ph.stop_timing(time_start)
def run(self): if not isinstance(self._fixed_itk, self._image_type): raise ValueError("Fixed image must be of type itk.Image") if not isinstance(self._moving_itk, self._image_type): raise ValueError("Moving image must be of type itk.Image") if self._fixed_itk_mask is not None and \ not isinstance(self._fixed_itk_mask, self._image_type): raise ValueError( "Fixed image mask must be of type itk.Image") if self._moving_itk_mask is not None and \ not isinstance(self._moving_itk_mask, self._image_type): raise ValueError( "Moving image mask must be of type itk.Image") time_start = ph.start_timing() # Execute registration method self._run_v3() # self._run_v4() # Get computational time self._computational_time = ph.stop_timing(time_start)
def from_volume(cls, file_paths_masks): t0 = ph.start_timing() target_stack_estimator = cls() volumes = np.array([ TargetStackEstimator._compute_volume(f) for f in file_paths_masks ]) # find index to smallest "valid" volume, i.e. volume > q * median index = np.argmax( volumes[np.argsort(volumes)] > 0.7 * np.median(volumes)) index = np.argsort(volumes)[index] # Get index corresponding to maximum volume stack mask # index = np.argmax(volumes) # index = np.argmin(volumes) # Get index corresponding to median volume stack mask # index = np.argsort(volumes)[len(volumes)//2] target_stack_estimator._target_stack_index = index # computational time target_stack_estimator._computational_time = ph.stop_timing(t0) return target_stack_estimator
def test_translation_registration_of_slices(self): filename_prefix = "TranslationOnly_" # filename_prefix = "RigidTransform_" filename_HRVolume = "HRVolume" filename_StackSim = filename_prefix + "StackSimulated" filename_transforms_prefix = filename_prefix + "TransformGroundTruth_slice" stack_sim = st.Stack.from_filename( os.path.join(self.dir_test_data, filename_StackSim + ".nii.gz")) HR_volume = st.Stack.from_filename( os.path.join(self.dir_test_data, filename_HRVolume + ".nii.gz")) slices_sim = stack_sim.get_slices() N_slices = len(slices_sim) scale = np.array( [180. / np.pi, 180. / np.pi, 180. / np.pi, 1., 1., 1.]) time_start = ph.start_timing() for j in range(0, N_slices): # for j in range(20, N_slices): rigid_transform_groundtruth_sitk = sitk.ReadTransform( self.dir_test_data + filename_transforms_prefix + str(j) + ".tfm") parameters_gd = np.array( rigid_transform_groundtruth_sitk.GetParameters()) angle_max = 5. * np.pi / 180. t_max = 5. registration = myreg.Registration( fixed=slices_sim[j], moving=HR_volume, # initializer_type="SelfGEOMETRY", use_verbose=0, # data_loss="soft_l1", # x_scale=[angle_max, angle_max, angle_max, t_max, t_max, t_max], ) registration.run_registration() # registration.print_statistics() # Check parameters transform_sitk = registration.get_registration_transform_sitk() parameters = np.array(transform_sitk.GetParameters()) norm_diff = np.linalg.norm(parameters - parameters_gd) params = parameters * scale params_gt = parameters_gd * scale print("Slice %s/%s: |parameters-parameters_gd| = %s" % (j, N_slices - 1, str(norm_diff))) print("\tEst: " + str(params) + " (deg/mmm)") print("\tGT: " + str(params_gt) + " (deg/mmm)") print("\tDiff: " + str(params - params_gt) + " (deg/mmm)") self.assertEqual(np.round(norm_diff, decimals=self.accuracy), 0) # Set elapsed time print("Translation: " + str(ph.stop_timing(time_start)))
def run(self): time_start = ph.start_timing() self._run() self._computational_time = ph.stop_timing(time_start) if self._verbose: ph.print_info("Required computational time: %s" % (self.get_computational_time()))
def run(self): ph.print_info("Chosen SDA approach: " + self._sda_approach) ph.print_info("Smoothing parameter sigma = " + str(self._sigma_array)) time_start = ph.start_timing() self._run[self._sda_approach]() # Get computational time self._computational_time = ph.stop_timing(time_start) if self._verbose: ph.print_info("Required computational time: %s" % (self.get_computational_time()))
def run(self): if not isinstance(self._fixed_points_nda, np.ndarray): raise IOError("Fixed points must be of type np.array") if not isinstance(self._moving_points_nda, np.ndarray): raise IOError("Moving points must be of type np.array") if self._fixed_points_nda.shape[1] != self._moving_points_nda.shape[1]: raise IOError( "Spatial dimensions of fixed and moving points must be equal") # Execute registration method time_start = ph.start_timing() self._run() # Get computational time self._computational_time = ph.stop_timing(time_start)
def run(self): if not isinstance(self._fixed, st.Stack) and \ not isinstance(self._fixed, sl.Slice): raise TypeError("Fixed image must be of type 'Stack' or 'Slice'") if not isinstance(self._moving, st.Stack) and \ not isinstance(self._moving, sl.Slice): raise TypeError("Moving image must be of type 'Stack' or 'Slice'") time_start = ph.start_timing() # Execute registration method self._run() # Get computational time self._computational_time = ph.stop_timing(time_start) if self._use_verbose: ph.print_info("Required computational time: %s" % (self.get_computational_time()))
def run(self): ph.create_directory(dir_tmp, delete_files=True) # Write images sitkh.write_nifti_image_sitk( self._stack1.sitk, self._dir_tmp + self._stack1.get_filename() + ".nii.gz") sitkh.write_nifti_image_sitk( self._stack2.sitk, self._dir_tmp + self._stack2.get_filename() + ".nii.gz") cmd = "siena " cmd += self._dir_tmp + self._stack1.get_filename() + ".nii.gz " cmd += self._dir_tmp + self._stack2.get_filename() + ".nii.gz " cmd += "-o " + self._dir_output + " " cmd += self._options time_start = ph.start_timing() ph.execute_command(cmd) self._elapsed_time = ph.stop_timing(time_start) # Extract measures from report self._extract_percentage_brain_volume_change()
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Register an obtained reconstruction (moving) " "to a template image/space (fixed) using rigid registration. " "The resulting registration can optionally be applied to previously " "obtained motion correction slice transforms so that a volumetric " "reconstruction is possible in the (standard anatomical) space " "defined by the fixed.", ) input_parser.add_fixed(required=True) input_parser.add_moving(required=True) input_parser.add_output(help="Path to registration transform (.txt)", required=True) input_parser.add_fixed_mask(required=False) input_parser.add_moving_mask(required=False) input_parser.add_option( option_string="--initial-transform", type=str, help="Path to initial transform. " "If not provided, registration will be initialized based on " "rigid alignment of eigenbasis of the fixed/moving image masks " "using principal component analysis", default=None) input_parser.add_v2v_method( option_string="--method", help="Registration method used for the registration.", default="RegAladin", ) input_parser.add_argument( "--refine-pca", "-refine-pca", action='store_true', help="If given, PCA-based initializations will be refined using " "RegAladin registrations.") input_parser.add_dir_input_mc() input_parser.add_verbose(default=0) input_parser.add_log_config(default=1) args = input_parser.parse_args() input_parser.print_arguments(args) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) if not args.output.endswith(".txt"): raise IOError("output transformation path must end in '.txt'") dir_output = os.path.dirname(args.output) ph.create_directory(dir_output) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") fixed = st.Stack.from_filename(file_path=args.fixed, file_path_mask=args.fixed_mask, extract_slices=False) moving = st.Stack.from_filename(file_path=args.moving, file_path_mask=args.moving_mask, extract_slices=False) path_to_tmp_output = os.path.join( DIR_TMP, ph.append_to_filename(os.path.basename(args.moving), "_warped")) # ---------------------------- Initialization ---------------------------- if args.initial_transform is None: ph.print_title("Estimate initial transform using PCA") if args.moving_mask is None or args.fixed_mask is None: ph.print_warning("Fixed and moving masks are strongly recommended") transform_initializer = tinit.TransformInitializer( fixed=fixed, moving=moving, similarity_measure="NMI", refine_pca_initializations=args.refine_pca, ) transform_initializer.run() transform_init_sitk = transform_initializer.get_transform_sitk() else: transform_init_sitk = sitkh.read_transform_sitk(args.initial_transform) sitk.WriteTransform(transform_init_sitk, args.output) # -------------------Register Reconstruction to Template------------------- ph.print_title("Registration") if args.method == "RegAladin": path_to_transform_regaladin = os.path.join(DIR_TMP, "transform_regaladin.txt") # Convert SimpleITK to RegAladin transform cmd = "simplereg_transform -sitk2nreg %s %s" % ( args.output, path_to_transform_regaladin) ph.execute_command(cmd, verbose=False) # Run NiftyReg cmd_args = ["reg_aladin"] cmd_args.append("-ref '%s'" % args.fixed) cmd_args.append("-flo '%s'" % args.moving) cmd_args.append("-res '%s'" % path_to_tmp_output) cmd_args.append("-inaff '%s'" % path_to_transform_regaladin) cmd_args.append("-aff '%s'" % path_to_transform_regaladin) cmd_args.append("-rigOnly") cmd_args.append("-ln 2") # seems to perform better for spina bifida cmd_args.append("-voff") if args.fixed_mask is not None: cmd_args.append("-rmask '%s'" % args.fixed_mask) # To avoid error "0 correspondences between blocks were found" that can # occur for some cases. Also, disable moving mask, as this would be ignored # anyway cmd_args.append("-noSym") # if args.moving_mask is not None: # cmd_args.append("-fmask '%s'" % args.moving_mask) ph.print_info("Run Registration (RegAladin) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=False) print("done") # Convert RegAladin to SimpleITK transform cmd = "simplereg_transform -nreg2sitk '%s' '%s'" % ( path_to_transform_regaladin, args.output) ph.execute_command(cmd, verbose=False) else: path_to_transform_flirt = os.path.join(DIR_TMP, "transform_flirt.txt") # Convert SimpleITK into FLIRT transform cmd = "simplereg_transform -sitk2flirt '%s' '%s' '%s' '%s'" % ( args.output, args.fixed, args.moving, path_to_transform_flirt) ph.execute_command(cmd, verbose=False) # Define search angle ranges for FLIRT in all three dimensions search_angles = [ "-searchr%s -%d %d" % (x, 180, 180) for x in ["x", "y", "z"] ] cmd_args = ["flirt"] cmd_args.append("-in '%s'" % args.moving) cmd_args.append("-ref '%s'" % args.fixed) if args.initial_transform is not None: cmd_args.append("-init '%s'" % path_to_transform_flirt) cmd_args.append("-omat '%s'" % path_to_transform_flirt) cmd_args.append("-out '%s'" % path_to_tmp_output) cmd_args.append("-dof 6") cmd_args.append((" ").join(search_angles)) if args.moving_mask is not None: cmd_args.append("-inweight '%s'" % args.moving_mask) if args.fixed_mask is not None: cmd_args.append("-refweight '%s'" % args.fixed_mask) ph.print_info("Run Registration (FLIRT) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=False) print("done") # Convert FLIRT to SimpleITK transform cmd = "simplereg_transform -flirt2sitk '%s' '%s' '%s' '%s'" % ( path_to_transform_flirt, args.fixed, args.moving, args.output) ph.execute_command(cmd, verbose=False) if args.dir_input_mc is not None: ph.print_title("Update Motion-Correction Transformations") transform_sitk = sitkh.read_transform_sitk(args.output, inverse=1) if args.dir_input_mc.endswith("/"): subdir_mc = args.dir_input_mc.split("/")[-2] else: subdir_mc = args.dir_input_mc.split("/")[-1] dir_output_mc = os.path.join(dir_output, subdir_mc) ph.create_directory(dir_output_mc, delete_files=True) pattern = REGEX_FILENAMES + "[.]tfm" p = re.compile(pattern) trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)] for t in trafos: path_to_input_transform = os.path.join(args.dir_input_mc, t) path_to_output_transform = os.path.join(dir_output_mc, t) t_sitk = sitkh.read_transform_sitk(path_to_input_transform) t_sitk = sitkh.get_composite_sitk_affine_transform( transform_sitk, t_sitk) sitk.WriteTransform(t_sitk, path_to_output_transform) ph.print_info("%d transformations written to '%s'" % (len(trafos), dir_output_mc)) if args.verbose: ph.show_niftis([args.fixed, path_to_tmp_output]) elapsed_time_total = ph.stop_timing(time_start) # Summary ph.print_title("Summary") print("Computational Time: %s" % (elapsed_time_total)) return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Run reconstruction pipeline including " "(i) bias field correction, " "(ii) volumetric reconstruction in subject space, " "and (iii) volumetric reconstruction in template space.", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks(required=True) input_parser.add_target_stack(required=False) input_parser.add_suffix_mask(default="''") input_parser.add_dir_output(required=True) input_parser.add_alpha(default=0.01) input_parser.add_verbose(default=0) input_parser.add_gestational_age(required=False) input_parser.add_prefix_output(default="") input_parser.add_search_angle(default=180) input_parser.add_multiresolution(default=0) input_parser.add_log_config(default=1) input_parser.add_isotropic_resolution() input_parser.add_reference() input_parser.add_reference_mask() input_parser.add_bias_field_correction(default=1) input_parser.add_intensity_correction(default=1) input_parser.add_iter_max(default=10) input_parser.add_two_step_cycles(default=3) input_parser.add_option( option_string="--run-bias-field-correction", type=int, help="Turn on/off bias field correction. " "If off, it is assumed that this step was already performed", default=1) input_parser.add_option( option_string="--run-recon-subject-space", type=int, help="Turn on/off reconstruction in subject space. " "If off, it is assumed that this step was already performed", default=1) input_parser.add_option( option_string="--run-recon-template-space", type=int, help="Turn on/off reconstruction in template space. " "If off, it is assumed that this step was already performed", default=1) input_parser.add_option( option_string="--run-data-vs-simulated-data", type=int, help="Turn on/off comparison of data vs data simulated from the " "obtained volumetric reconstruction. " "If off, it is assumed that this step was already performed", default=0) input_parser.add_option( option_string="--initial-transform", type=str, help="Set initial transform to be used for register_image.", default=None) input_parser.add_outlier_rejection(default=1) input_parser.add_argument( "--sda", "-sda", action='store_true', help="If given, the volume is reconstructed using " "Scattered Data Approximation (Vercauteren et al., 2006). " "--alpha is considered the value for the standard deviation then. " "Recommended value is, e.g., --alpha 0.8") args = input_parser.parse_args() input_parser.print_arguments(args) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) filename_srr = "srr" dir_output_preprocessing = os.path.join(args.dir_output, "preprocessing_n4itk") dir_output_recon_subject_space = os.path.join(args.dir_output, "recon_subject_space") dir_output_recon_template_space = os.path.join(args.dir_output, "recon_template_space") dir_output_data_vs_simulatd_data = os.path.join(args.dir_output, "data_vs_simulated_data") srr_subject = os.path.join(dir_output_recon_subject_space, "%s_subject.nii.gz" % filename_srr) srr_subject_mask = ph.append_to_filename(srr_subject, "_mask") srr_template = os.path.join(dir_output_recon_template_space, "%s_template.nii.gz" % filename_srr) srr_template_mask = ph.append_to_filename(srr_template, "_mask") trafo_template = os.path.join(dir_output_recon_template_space, "registration_transform_sitk.txt") if args.target_stack is None: target_stack = args.filenames[0] else: target_stack = args.target_stack if args.bias_field_correction and args.run_bias_field_correction: for i, f in enumerate(args.filenames): output = os.path.join(dir_output_preprocessing, os.path.basename(f)) cmd_args = [] cmd_args.append("--filename %s" % f) cmd_args.append("--filename-mask %s" % args.filenames_masks[i]) cmd_args.append("--output %s" % output) # cmd_args.append("--verbose %d" % args.verbose) cmd = "niftymic_correct_bias_field %s" % (" ").join(cmd_args) time_start_bias = ph.start_timing() exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Bias field correction failed") elapsed_time_bias = ph.stop_timing(time_start_bias) filenames = [ os.path.join(dir_output_preprocessing, os.path.basename(f)) for f in args.filenames ] elif args.bias_field_correction and not args.run_bias_field_correction: elapsed_time_bias = ph.get_zero_time() filenames = [ os.path.join(dir_output_preprocessing, os.path.basename(f)) for f in args.filenames ] else: elapsed_time_bias = ph.get_zero_time() filenames = args.filenames if args.run_recon_subject_space: target_stack_index = args.filenames.index(target_stack) cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(filenames)) if args.filenames_masks is not None: cmd_args.append("--filenames-masks %s" % (" ").join(args.filenames_masks)) cmd_args.append("--multiresolution %d" % args.multiresolution) cmd_args.append("--target-stack-index %d" % target_stack_index) cmd_args.append("--output %s" % srr_subject) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--intensity-correction %d" % args.intensity_correction) cmd_args.append("--alpha %s" % args.alpha) cmd_args.append("--iter-max %d" % args.iter_max) cmd_args.append("--two-step-cycles %d" % args.two_step_cycles) cmd_args.append("--outlier-rejection %d" % args.outlier_rejection) cmd_args.append("--verbose %d" % args.verbose) if args.isotropic_resolution is not None: cmd_args.append("--isotropic-resolution %f" % args.isotropic_resolution) if args.reference is not None: cmd_args.append("--reference %s" % args.reference) if args.reference_mask is not None: cmd_args.append("--reference-mask %s" % args.reference_mask) if args.sda: cmd_args.append("--sda") cmd = "niftymic_reconstruct_volume %s" % (" ").join(cmd_args) time_start_volrec = ph.start_timing() exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Reconstruction in subject space failed") # Compute SRR mask in subject space # (Approximated using SDA within reconstruct_volume) if 0: dir_motion_correction = os.path.join( dir_output_recon_subject_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % " ".join(args.filenames_masks)) cmd_args.append("--dir-input-mc %s" % dir_motion_correction) cmd_args.append("--output %s" % srr_subject_mask) cmd_args.append("--reconstruction-space %s" % srr_subject) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--mask") if args.sda: cmd_args.append("--sda") cmd_args.append("--alpha 1") else: cmd_args.append("--alpha 0.1") cmd_args.append("--iter-max 5") cmd = (" ").join(cmd_args) ph.execute_command(cmd) elapsed_time_volrec = ph.stop_timing(time_start_volrec) else: elapsed_time_volrec = ph.get_zero_time() if args.run_recon_template_space: if args.gestational_age is None: template_stack_estimator = \ tse.TemplateStackEstimator.from_mask(srr_subject_mask) gestational_age = template_stack_estimator.get_estimated_gw() ph.print_info("Estimated gestational age: %d" % gestational_age) else: gestational_age = args.gestational_age template = os.path.join(DIR_TEMPLATES, "STA%d.nii.gz" % gestational_age) template_mask = os.path.join(DIR_TEMPLATES, "STA%d_mask.nii.gz" % gestational_age) cmd_args = [] cmd_args.append("--fixed %s" % template) cmd_args.append("--moving %s" % srr_subject) cmd_args.append("--fixed-mask %s" % template_mask) cmd_args.append("--moving-mask %s" % srr_subject_mask) cmd_args.append( "--dir-input-mc %s" % os.path.join(dir_output_recon_subject_space, "motion_correction")) cmd_args.append("--output %s" % trafo_template) cmd_args.append("--verbose %s" % args.verbose) if args.initial_transform is not None: cmd_args.append("--initial-transform %s" % args.initial_transform) cmd_args.append("--use-flirt 0") cmd_args.append("--test-ap-flip 0") cmd = "niftymic_register_image %s" % (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Registration to template space failed") # reconstruct volume in template space # pattern = "[a-zA-Z0-9_.]+(ResamplingToTemplateSpace.nii.gz)" # p = re.compile(pattern) # reconstruction_space = [ # os.path.join(dir_output_recon_template_space, p.match(f).group(0)) # for f in os.listdir(dir_output_recon_template_space) # if p.match(f)][0] dir_input_mc = os.path.join(dir_output_recon_template_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % (" ").join(filenames)) cmd_args.append("--dir-input-mc %s" % dir_input_mc) cmd_args.append("--output %s" % srr_template) cmd_args.append("--reconstruction-space %s" % template) cmd_args.append("--iter-max %d" % args.iter_max) cmd_args.append("--alpha %s" % args.alpha) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--verbose %s" % args.verbose) if args.sda: cmd_args.append("--sda") cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Reconstruction in template space failed") # Compute SRR mask in template space if 1: dir_motion_correction = os.path.join( dir_output_recon_template_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % " ".join(args.filenames_masks)) cmd_args.append("--dir-input-mc %s" % dir_motion_correction) cmd_args.append("--output %s" % srr_template_mask) cmd_args.append("--reconstruction-space %s" % srr_template) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--mask") if args.sda: cmd_args.append("--sda") cmd_args.append("--alpha 1") else: cmd_args.append("--alpha 0.1") cmd_args.append("--iter-max 5") cmd = (" ").join(cmd_args) ph.execute_command(cmd) # Copy SRR to output directory output = "%sSRR_Stacks%d.nii.gz" % (args.prefix_output, len(args.filenames)) path_to_output = os.path.join(args.dir_output, output) cmd = "cp -p %s %s" % (srr_template, path_to_output) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Copy of SRR to output directory failed") # Multiply template mask with reconstruction cmd_args = ["niftymic_multiply"] fnames = [ srr_template, srr_template_mask, ] output_masked = "Masked_%s" % output path_to_output_masked = os.path.join(args.dir_output, output_masked) cmd_args.append("--filenames %s" % " ".join(fnames)) cmd_args.append("--output %s" % path_to_output_masked) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("SRR brain masking failed") else: elapsed_time_template = ph.get_zero_time() if args.run_data_vs_simulated_data: dir_input_mc = os.path.join(dir_output_recon_template_space, "motion_correction") # Get simulated/projected slices cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(filenames)) if args.filenames_masks is not None: cmd_args.append("--filenames-masks %s" % (" ").join(args.filenames_masks)) cmd_args.append("--dir-input-mc %s" % dir_input_mc) cmd_args.append("--dir-output %s" % dir_output_data_vs_simulatd_data) cmd_args.append("--reconstruction %s" % srr_template) cmd_args.append("--copy-data 1") cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) # cmd_args.append("--verbose %s" % args.verbose) exe = os.path.abspath(simulate_stacks_from_reconstruction.__file__) cmd = "python %s %s" % (exe, (" ").join(cmd_args)) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("SRR slice projections failed") filenames_simulated = [ os.path.join(dir_output_data_vs_simulatd_data, os.path.basename(f)) for f in filenames ] dir_output_evaluation = os.path.join(dir_output_data_vs_simulatd_data, "evaluation") dir_output_figures = os.path.join(dir_output_data_vs_simulatd_data, "figures") dir_output_side_by_side = os.path.join(dir_output_figures, "side-by-side") # Evaluate slice similarities to ground truth cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(filenames_simulated)) if args.filenames_masks is not None: cmd_args.append("--filenames-masks %s" % (" ").join(args.filenames_masks)) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--measures NCC SSIM") cmd_args.append("--dir-output %s" % dir_output_evaluation) exe = os.path.abspath(evaluate_simulated_stack_similarity.__file__) cmd = "python %s %s" % (exe, (" ").join(cmd_args)) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Evaluation of slice similarities failed") # Generate figures showing the quantitative comparison cmd_args = [] cmd_args.append("--dir-input %s" % dir_output_evaluation) cmd_args.append("--dir-output %s" % dir_output_figures) exe = os.path.abspath( show_evaluated_simulated_stack_similarity.__file__) cmd = "python %s %s" % (exe, (" ").join(cmd_args)) exit_code = ph.execute_command(cmd) if exit_code != 0: ph.print_warning("Visualization of slice similarities failed") # Generate pdfs showing all the side-by-side comparisons cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(filenames_simulated)) cmd_args.append("--dir-output %s" % dir_output_side_by_side) exe = os.path.abspath( export_side_by_side_simulated_vs_original_slice_comparison.__file__ ) cmd = "python %s %s" % (exe, (" ").join(cmd_args)) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Generation of PDF overview failed") ph.print_title("Summary") print("Computational Time for Bias Field Correction: %s" % elapsed_time_bias) print("Computational Time for Volumetric Reconstruction: %s" % elapsed_time_volrec) print("Computational Time for Pipeline: %s" % ph.stop_timing(time_start)) return 0
def main(): time_start = ph.start_timing() # Read input parser = argparse.ArgumentParser( description="Perform rigid registration using landmarks", prog=None, epilog="Author: Michael Ebner ([email protected])", ) parser.add_argument( "-f", "--fixed", help="Path to fixed image landmarks.", type=str, required=1, ) parser.add_argument( "-m", "--moving", help="Path to moving image landmarks.", type=str, required=1, ) parser.add_argument( "-o", "--output", help="Path for obtained SimpleITK registration transform (.txt)", type=str, required=1, ) parser.add_argument( "-v", "--verbose", help="Turn on/off verbose output", type=int, required=0, default=0, ) parser.add_argument( "--pca", "-pca", action="store_true", help="If given, principal component analysis (PCA) is used " "to test various initializations for the point based registrations.") args = parser.parse_args() landmarks_fixed_nda = dr.DataReader.read_landmarks(args.fixed) landmarks_moving_nda = dr.DataReader.read_landmarks(args.moving) if args.pca: ph.print_subtitle("Use PCA to initialize registrations") pca_fixed = pca.PrincipalComponentAnalysis(landmarks_fixed_nda) pca_fixed.run() eigvec_fixed = pca_fixed.get_eigvec() mean_fixed = pca_fixed.get_mean() pca_moving = pca.PrincipalComponentAnalysis(landmarks_moving_nda) pca_moving.run() eigvec_moving = pca_moving.get_eigvec() mean_moving = pca_moving.get_mean() # test different initializations based on eigenvector orientations orientations = [ [1, 1], [1, -1], [-1, 1], [-1, -1], ] error = np.inf for i_o, orientation in enumerate(orientations): eigvec_moving_o = np.array(eigvec_moving) eigvec_moving_o[:, 0] *= orientation[0] eigvec_moving_o[:, 1] *= orientation[1] # get right-handed coordinate system cross = np.cross(eigvec_moving_o[:, 0], eigvec_moving_o[:, 1]) eigvec_moving_o[:, 2] = cross # transformation to align fixed with moving eigenbasis R = eigvec_moving_o.dot(eigvec_fixed.transpose()) t = mean_moving - R.dot(mean_fixed) ph.print_info( "Registration based on PCA eigenvector initialization " "%d/%d ... " % (i_o + 1, len(orientations)), newline=False) reg = pycpd.rigid_registration( **{ "Y": landmarks_fixed_nda, "X": landmarks_moving_nda, "max_iterations": 100, "R": R, "t": t, }) reg.register() params = reg.get_registration_parameters() scale, R, t = params error_o = reg.err print("done. Error: %.2f" % error_o) if error_o < error: error = error_o rotation_matrix_nda = np.array(R) translation_nda = np.array(t) ph.print_info("Currently best estimate") else: reg = pycpd.rigid_registration( **{ "Y": landmarks_fixed_nda, "X": landmarks_moving_nda, "max_iterations": 100, }) if args.verbose: fig = plt.figure() ax = fig.add_subplot(111, projection='3d') callback = partial(visualize, ax=ax) else: callback = None ph.print_info("Registration ... ", newline=False) reg.register(callback) if args.verbose: plt.show(block=False) # reg.register() scale, R, t = reg.get_registration_parameters() rotation_matrix_nda = R translation_nda = t print("done. Error: %.2f" % reg.err) rigid_transform_sitk = sitk.Euler3DTransform() rigid_transform_sitk.SetMatrix(rotation_matrix_nda.flatten()) rigid_transform_sitk.SetTranslation(translation_nda) dw.DataWriter.write_transform(rigid_transform_sitk, args.output, verbose=True) elapsed_time_total = ph.stop_timing(time_start) ph.print_info("Computational Time: %s" % elapsed_time_total) return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) input_parser = InputArgparser( description="Volumetric MRI reconstruction framework to reconstruct " "an isotropic, high-resolution 3D volume from multiple stacks of 2D " "slices with motion correction. The resolution of the computed " "Super-Resolution Reconstruction (SRR) is given by the in-plane " "spacing of the selected target stack. A region of interest can be " "specified by providing a mask for the selected target stack. Only " "this region will then be reconstructed by the SRR algorithm which " "can substantially reduce the computational time.", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks() input_parser.add_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_target_stack(default=None) input_parser.add_search_angle(default=45) input_parser.add_multiresolution(default=0) input_parser.add_shrink_factors(default=[3, 2, 1]) input_parser.add_smoothing_sigmas(default=[1.5, 1, 0]) input_parser.add_sigma(default=1) input_parser.add_reconstruction_type(default="TK1L2") input_parser.add_iterations(default=15) input_parser.add_alpha(default=0.015) input_parser.add_alpha_first(default=0.2) input_parser.add_iter_max(default=10) input_parser.add_iter_max_first(default=5) input_parser.add_dilation_radius(default=3) input_parser.add_extra_frame_target(default=10) input_parser.add_bias_field_correction(default=0) input_parser.add_intensity_correction(default=1) input_parser.add_isotropic_resolution(default=1) input_parser.add_log_config(default=1) input_parser.add_subfolder_motion_correction() input_parser.add_write_motion_correction(default=1) input_parser.add_verbose(default=0) input_parser.add_two_step_cycles(default=3) input_parser.add_use_masks_srr(default=0) input_parser.add_boundary_stacks(default=[10, 10, 0]) input_parser.add_metric(default="Correlation") input_parser.add_metric_radius(default=10) input_parser.add_reference() input_parser.add_reference_mask() input_parser.add_outlier_rejection(default=1) input_parser.add_threshold_first(default=0.5) input_parser.add_threshold(default=0.8) input_parser.add_interleave(default=3) input_parser.add_slice_thicknesses(default=None) input_parser.add_viewer(default="itksnap") input_parser.add_v2v_method(default="RegAladin") input_parser.add_argument( "--v2v-robust", "-v2v-robust", action='store_true', help="If given, a more robust volume-to-volume registration step is " "performed, i.e. four rigid registrations are performed using four " "rigid transform initializations based on " "principal component alignment of associated masks." ) input_parser.add_argument( "--s2v-hierarchical", "-s2v-hierarchical", action='store_true', help="If given, a hierarchical approach for the first slice-to-volume " "registration cycle is used, i.e. sub-packages defined by the " "specified interleave (--interleave) are registered until each " "slice is registered independently." ) input_parser.add_argument( "--sda", "-sda", action='store_true', help="If given, the volumetric reconstructions are performed using " "Scattered Data Approximation (Vercauteren et al., 2006). " "'alpha' is considered the final 'sigma' for the " "iterative adjustment. " "Recommended value is, e.g., --alpha 0.8" ) input_parser.add_option( option_string="--transforms-history", type=int, help="Write entire history of applied slice motion correction " "transformations to motion correction output directory", default=0, ) args = input_parser.parse_args() input_parser.print_arguments(args) rejection_measure = "NCC" threshold_v2v = -2 # 0.3 debug = False if args.v2v_method not in V2V_METHOD_OPTIONS: raise ValueError("v2v-method must be in {%s}" % ( ", ".join(V2V_METHOD_OPTIONS))) if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]): raise ValueError( "output filename invalid; allowed extensions are: %s" % ", ".join(ALLOWED_EXTENSIONS)) if args.alpha_first < args.alpha and not args.sda: raise ValueError("It must hold alpha-first >= alpha") if args.threshold_first > args.threshold: raise ValueError("It must hold threshold-first <= threshold") dir_output = os.path.dirname(args.output) ph.create_directory(dir_output) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") data_reader = dr.MultipleImagesReader( file_paths=args.filenames, file_paths_masks=args.filenames_masks, suffix_mask=args.suffix_mask, stacks_slice_thicknesses=args.slice_thicknesses, ) if len(args.boundary_stacks) is not 3: raise IOError( "Provide exactly three values for '--boundary-stacks' to define " "cropping in i-, j-, and k-dimension of the input stacks") data_reader.read_data() stacks = data_reader.get_data() ph.print_info("%d input stacks read for further processing" % len(stacks)) if all(s.is_unity_mask() is True for s in stacks): ph.print_warning("No mask is provided! " "Generated reconstruction space may be very big!") ph.print_warning("Consider using a mask to speed up computations") # args.extra_frame_target = 0 # ph.wrint_warning("Overwritten: extra-frame-target set to 0") # Specify target stack for intensity correction and reconstruction space if args.target_stack is None: target_stack_index = 0 else: try: target_stack_index = args.filenames.index(args.target_stack) except ValueError as e: raise ValueError( "--target-stack must correspond to an image as provided by " "--filenames") # ---------------------------Data Preprocessing--------------------------- ph.print_title("Data Preprocessing") segmentation_propagator = segprop.SegmentationPropagation( # registration_method=regflirt.FLIRT(use_verbose=args.verbose), # registration_method=niftyreg.RegAladin(use_verbose=False), dilation_radius=args.dilation_radius, dilation_kernel="Ball", ) data_preprocessing = dp.DataPreprocessing( stacks=stacks, segmentation_propagator=segmentation_propagator, use_cropping_to_mask=True, use_N4BiasFieldCorrector=args.bias_field_correction, target_stack_index=target_stack_index, boundary_i=args.boundary_stacks[0], boundary_j=args.boundary_stacks[1], boundary_k=args.boundary_stacks[2], unit="mm", ) data_preprocessing.run() time_data_preprocessing = data_preprocessing.get_computational_time() # Get preprocessed stacks stacks = data_preprocessing.get_preprocessed_stacks() # Define reference/target stack for registration and reconstruction if args.reference is not None: reference = st.Stack.from_filename( file_path=args.reference, file_path_mask=args.reference_mask, extract_slices=False) else: reference = st.Stack.from_stack(stacks[target_stack_index]) # ------------------------Volume-to-Volume Registration-------------------- if len(stacks) > 1: if args.v2v_method == "FLIRT": # Define search angle ranges for FLIRT in all three dimensions search_angles = ["-searchr%s -%d %d" % (x, args.search_angle, args.search_angle) for x in ["x", "y", "z"]] options = (" ").join(search_angles) # options += " -noresample" vol_registration = regflirt.FLIRT( registration_type="Rigid", use_fixed_mask=True, use_moving_mask=True, options=options, use_verbose=False, ) else: vol_registration = niftyreg.RegAladin( registration_type="Rigid", use_fixed_mask=True, use_moving_mask=True, # options="-ln 2 -voff", use_verbose=False, ) v2vreg = pipeline.VolumeToVolumeRegistration( stacks=stacks, reference=reference, registration_method=vol_registration, verbose=debug, robust=args.v2v_robust, ) v2vreg.run() stacks = v2vreg.get_stacks() time_registration = v2vreg.get_computational_time() else: time_registration = ph.get_zero_time() # ---------------------------Intensity Correction-------------------------- if args.intensity_correction: ph.print_title("Intensity Correction") intensity_corrector = ic.IntensityCorrection() intensity_corrector.use_individual_slice_correction(False) intensity_corrector.use_reference_mask(True) intensity_corrector.use_stack_mask(True) intensity_corrector.use_verbose(False) for i, stack in enumerate(stacks): if i == target_stack_index: ph.print_info("Stack %d (%s): Reference image. Skipped." % ( i + 1, stack.get_filename())) continue else: ph.print_info("Stack %d (%s): Intensity Correction ... " % ( i + 1, stack.get_filename()), newline=False) intensity_corrector.set_stack(stack) intensity_corrector.set_reference( stacks[target_stack_index].get_resampled_stack( resampling_grid=stack.sitk, interpolator="NearestNeighbor", )) intensity_corrector.run_linear_intensity_correction() stacks[i] = intensity_corrector.get_intensity_corrected_stack() print("done (c1 = %g) " % intensity_corrector.get_intensity_correction_coefficients()) # ---------------------------Create first volume--------------------------- time_tmp = ph.start_timing() # Isotropic resampling to define HR target space ph.print_title("Reconstruction Space Generation") HR_volume = reference.get_isotropically_resampled_stack( resolution=args.isotropic_resolution) ph.print_info( "Isotropic reconstruction space with %g mm resolution is created" % HR_volume.sitk.GetSpacing()[0]) if args.reference is None: # Create joint image mask in target space joint_image_mask_builder = imb.JointImageMaskBuilder( stacks=stacks, target=HR_volume, dilation_radius=1, ) joint_image_mask_builder.run() HR_volume = joint_image_mask_builder.get_stack() ph.print_info( "Isotropic reconstruction space is centered around " "joint stack masks. ") # Crop to space defined by mask (plus extra margin) HR_volume = HR_volume.get_cropped_stack_based_on_mask( boundary_i=args.extra_frame_target, boundary_j=args.extra_frame_target, boundary_k=args.extra_frame_target, unit="mm", ) # Create first volume # If outlier rejection is activated, eliminate obvious outliers early # from stack and re-run SDA to get initial volume without them ph.print_title("First Estimate of HR Volume") if args.outlier_rejection and threshold_v2v > -1: ph.print_subtitle("SDA Approximation") SDA = sda.ScatteredDataApproximation( stacks, HR_volume, sigma=args.sigma) SDA.run() HR_volume = SDA.get_reconstruction() # Identify and reject outliers ph.print_subtitle("Eliminate slice outliers (%s < %g)" % ( rejection_measure, threshold_v2v)) outlier_rejector = outre.OutlierRejector( stacks=stacks, reference=HR_volume, threshold=threshold_v2v, measure=rejection_measure, verbose=True, ) outlier_rejector.run() stacks = outlier_rejector.get_stacks() ph.print_subtitle("SDA Approximation Image") SDA = sda.ScatteredDataApproximation( stacks, HR_volume, sigma=args.sigma) SDA.run() HR_volume = SDA.get_reconstruction() ph.print_subtitle("SDA Approximation Image Mask") SDA = sda.ScatteredDataApproximation( stacks, HR_volume, sigma=args.sigma, sda_mask=True) SDA.run() # HR volume contains updated mask based on SDA HR_volume = SDA.get_reconstruction() HR_volume.set_filename(SDA.get_setting_specific_filename()) time_reconstruction = ph.stop_timing(time_tmp) if args.verbose: tmp = list(stacks) tmp.insert(0, HR_volume) sitkh.show_stacks(tmp, segmentation=HR_volume, viewer=args.viewer) # -----------Two-step Slice-to-Volume Registration-Reconstruction---------- if args.two_step_cycles > 0: # Slice-to-volume registration set-up if args.metric == "ANTSNeighborhoodCorrelation": metric_params = {"radius": args.metric_radius} else: metric_params = None registration = regsitk.SimpleItkRegistration( moving=HR_volume, use_fixed_mask=True, use_moving_mask=True, interpolator="Linear", metric=args.metric, metric_params=metric_params, use_multiresolution_framework=args.multiresolution, shrink_factors=args.shrink_factors, smoothing_sigmas=args.smoothing_sigmas, initializer_type="SelfGEOMETRY", optimizer="ConjugateGradientLineSearch", optimizer_params={ "learningRate": 1, "numberOfIterations": 100, "lineSearchUpperLimit": 2, }, scales_estimator="Jacobian", use_verbose=debug, ) # Volumetric reconstruction set-up if args.sda: recon_method = sda.ScatteredDataApproximation( stacks, HR_volume, sigma=args.sigma, use_masks=args.use_masks_srr, ) alpha_range = [args.sigma, args.alpha] else: recon_method = tk.TikhonovSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TK1", minimizer="lsmr", alpha=args.alpha_first, iter_max=np.min([args.iter_max_first, args.iter_max]), verbose=True, use_masks=args.use_masks_srr, ) alpha_range = [args.alpha_first, args.alpha] # Define the regularization parameters for the individual # reconstruction steps in the two-step cycles alphas = np.linspace( alpha_range[0], alpha_range[1], args.two_step_cycles) # Define outlier rejection threshold after each S2V-reg step thresholds = np.linspace( args.threshold_first, args.threshold, args.two_step_cycles) two_step_s2v_reg_recon = \ pipeline.TwoStepSliceToVolumeRegistrationReconstruction( stacks=stacks, reference=HR_volume, registration_method=registration, reconstruction_method=recon_method, cycles=args.two_step_cycles, alphas=alphas[0:args.two_step_cycles - 1], outlier_rejection=args.outlier_rejection, threshold_measure=rejection_measure, thresholds=thresholds, interleave=args.interleave, viewer=args.viewer, verbose=args.verbose, use_hierarchical_registration=args.s2v_hierarchical, ) two_step_s2v_reg_recon.run() HR_volume_iterations = \ two_step_s2v_reg_recon.get_iterative_reconstructions() time_registration += \ two_step_s2v_reg_recon.get_computational_time_registration() time_reconstruction += \ two_step_s2v_reg_recon.get_computational_time_reconstruction() stacks = two_step_s2v_reg_recon.get_stacks() # no two-step s2v-registration/reconstruction iterations else: HR_volume_iterations = [] # Write motion-correction results ph.print_title("Write Motion Correction Results") if args.write_motion_correction: dir_output_mc = os.path.join( dir_output, args.subfolder_motion_correction) ph.clear_directory(dir_output_mc) for stack in stacks: stack.write( dir_output_mc, write_stack=False, write_mask=False, write_slices=False, write_transforms=True, write_transforms_history=args.transforms_history, ) if args.outlier_rejection: deleted_slices_dic = {} for i, stack in enumerate(stacks): deleted_slices = stack.get_deleted_slice_numbers() deleted_slices_dic[stack.get_filename()] = deleted_slices # check whether any stack was removed entirely stacks0 = data_preprocessing.get_preprocessed_stacks() if len(stacks) != len(stacks0): stacks_remain = [s.get_filename() for s in stacks] for stack in stacks0: if stack.get_filename() in stacks_remain: continue # add info that all slices of this stack were rejected deleted_slices = [ slice.get_slice_number() for slice in stack.get_slices() ] deleted_slices_dic[stack.get_filename()] = deleted_slices ph.print_info( "All slices of stack '%s' were rejected entirely. " "Information added." % stack.get_filename()) ph.write_dictionary_to_json( deleted_slices_dic, os.path.join( dir_output, args.subfolder_motion_correction, "rejected_slices.json" ) ) # ---------------------Final Volumetric Reconstruction--------------------- ph.print_title("Final Volumetric Reconstruction") if args.sda: recon_method = sda.ScatteredDataApproximation( stacks, HR_volume, sigma=args.alpha, use_masks=args.use_masks_srr, ) else: if args.reconstruction_type in ["TVL2", "HuberL2"]: recon_method = pd.PrimalDualSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TV" if args.reconstruction_type == "TVL2" else "huber", iterations=args.iterations, use_masks=args.use_masks_srr, ) else: recon_method = tk.TikhonovSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TK1" if args.reconstruction_type == "TK1L2" else "TK0", use_masks=args.use_masks_srr, ) recon_method.set_alpha(args.alpha) recon_method.set_iter_max(args.iter_max) recon_method.set_verbose(True) recon_method.run() time_reconstruction += recon_method.get_computational_time() HR_volume_final = recon_method.get_reconstruction() ph.print_subtitle("Final SDA Approximation Image Mask") SDA = sda.ScatteredDataApproximation( stacks, HR_volume_final, sigma=args.sigma, sda_mask=True) SDA.run() # HR volume contains updated mask based on SDA HR_volume_final = SDA.get_reconstruction() time_reconstruction += SDA.get_computational_time() elapsed_time_total = ph.stop_timing(time_start) # Write SRR result filename = recon_method.get_setting_specific_filename() HR_volume_final.set_filename(filename) dw.DataWriter.write_image( HR_volume_final.sitk, args.output, description=filename) dw.DataWriter.write_mask( HR_volume_final.sitk_mask, ph.append_to_filename(args.output, "_mask"), description=SDA.get_setting_specific_filename()) HR_volume_iterations.insert(0, HR_volume_final) for stack in stacks: HR_volume_iterations.append(stack) if args.verbose: sitkh.show_stacks( HR_volume_iterations, segmentation=HR_volume_final, viewer=args.viewer, ) # Summary ph.print_title("Summary") exe_file_info = os.path.basename(os.path.abspath(__file__)).split(".")[0] print("%s | Computational Time for Data Preprocessing: %s" % (exe_file_info, time_data_preprocessing)) print("%s | Computational Time for Registrations: %s" % (exe_file_info, time_registration)) print("%s | Computational Time for Reconstructions: %s" % (exe_file_info, time_reconstruction)) print("%s | Computational Time for Entire Reconstruction Pipeline: %s" % (exe_file_info, elapsed_time_total)) ph.print_line_separator() return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) # Read input input_parser = InputArgparser( description="Volumetric MRI reconstruction framework to reconstruct " "an isotropic, high-resolution 3D volume from multiple stacks of 2D " "slices with motion correction. The resolution of the computed " "Super-Resolution Reconstruction (SRR) is given by the in-plane " "spacing of the selected target stack. A region of interest can be " "specified by providing a mask for the selected target stack. Only " "this region will then be reconstructed by the SRR algorithm which " "can substantially reduce the computational time.", ) input_parser.add_dir_input() input_parser.add_filenames() input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_target_stack_index(default=0) input_parser.add_search_angle(default=90) input_parser.add_multiresolution(default=0) input_parser.add_shrink_factors(default=[2, 1]) input_parser.add_smoothing_sigmas(default=[1, 0]) input_parser.add_sigma(default=0.9) input_parser.add_reconstruction_type(default="TK1L2") input_parser.add_iterations(default=15) input_parser.add_alpha(default=0.02) input_parser.add_alpha_first(default=0.05) input_parser.add_iter_max(default=10) input_parser.add_iter_max_first(default=5) input_parser.add_dilation_radius(default=3) input_parser.add_extra_frame_target(default=10) input_parser.add_bias_field_correction(default=0) input_parser.add_intensity_correction(default=0) input_parser.add_isotropic_resolution(default=None) input_parser.add_log_script_execution(default=1) input_parser.add_subfolder_motion_correction() input_parser.add_provide_comparison(default=0) input_parser.add_subfolder_comparison() input_parser.add_write_motion_correction(default=1) input_parser.add_verbose(default=0) input_parser.add_two_step_cycles(default=3) input_parser.add_use_masks_srr(default=1) input_parser.add_boundary_stacks(default=[10, 10, 0]) input_parser.add_reference() input_parser.add_reference_mask() args = input_parser.parse_args() input_parser.print_arguments(args) # Write script execution call if args.log_script_execution: input_parser.write_performed_script_execution( os.path.abspath(__file__)) # Use FLIRT for volume-to-volume reg. step. Otherwise, RegAladin is used. use_flirt_for_v2v_registration = True # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") # Neither '--dir-input' nor '--filenames' was specified if args.filenames is not None and args.dir_input is not None: raise IOError("Provide input by either '--dir-input' or '--filenames' " "but not both together") # '--dir-input' specified elif args.dir_input is not None: data_reader = dr.ImageDirectoryReader(args.dir_input, suffix_mask=args.suffix_mask) # '--filenames' specified elif args.filenames is not None: data_reader = dr.MultipleImagesReader(args.filenames, suffix_mask=args.suffix_mask) else: raise IOError("Provide input by either '--dir-input' or '--filenames'") if len(args.boundary_stacks) is not 3: raise IOError( "Provide exactly three values for '--boundary-stacks' to define " "cropping in i-, j-, and k-dimension of the input stacks") data_reader.read_data() stacks = data_reader.get_data() ph.print_info("%d input stacks read for further processing" % len(stacks)) if all(s.is_unity_mask() is True for s in stacks): ph.print_warning("No mask is provided! " "Generated reconstruction space may be very big!") # ---------------------------Data Preprocessing--------------------------- ph.print_title("Data Preprocessing") segmentation_propagator = segprop.SegmentationPropagation( # registration_method=regflirt.FLIRT(use_verbose=args.verbose), dilation_radius=args.dilation_radius, dilation_kernel="Ball", ) data_preprocessing = dp.DataPreprocessing( stacks=stacks, segmentation_propagator=segmentation_propagator, use_cropping_to_mask=True, use_N4BiasFieldCorrector=args.bias_field_correction, use_intensity_correction=args.intensity_correction, target_stack_index=args.target_stack_index, boundary_i=args.boundary_stacks[0], boundary_j=args.boundary_stacks[1], boundary_k=args.boundary_stacks[2], unit="mm", ) data_preprocessing.run() time_data_preprocessing = data_preprocessing.get_computational_time() # Get preprocessed stacks stacks = data_preprocessing.get_preprocessed_stacks() # Define reference/target stack for registration and reconstruction if args.reference is not None: reference = st.Stack.from_filename(file_path=args.reference, file_path_mask=args.reference_mask, extract_slices=False) else: reference = st.Stack.from_stack(stacks[args.target_stack_index]) # ------------------------Volume-to-Volume Registration-------------------- if args.two_step_cycles > 0: # Define search angle ranges for FLIRT in all three dimensions search_angles = [ "-searchr%s -%d %d" % (x, args.search_angle, args.search_angle) for x in ["x", "y", "z"] ] search_angles = (" ").join(search_angles) if use_flirt_for_v2v_registration: vol_registration = regflirt.FLIRT( registration_type="Rigid", use_fixed_mask=True, use_moving_mask=True, options=search_angles, use_verbose=False, ) else: vol_registration = niftyreg.RegAladin( registration_type="Rigid", use_fixed_mask=True, use_moving_mask=True, use_verbose=False, ) v2vreg = pipeline.VolumeToVolumeRegistration( stacks=stacks, reference=reference, registration_method=vol_registration, verbose=args.verbose, ) v2vreg.run() stacks = v2vreg.get_stacks() time_registration = v2vreg.get_computational_time() else: time_registration = ph.get_zero_time() # ---------------------------Create first volume--------------------------- time_tmp = ph.start_timing() # Isotropic resampling to define HR target space ph.print_title("Reconstruction Space Generation") HR_volume = reference.get_isotropically_resampled_stack( resolution=args.isotropic_resolution) ph.print_info( "Isotropic reconstruction space with %g mm resolution is created" % HR_volume.sitk.GetSpacing()[0]) if args.reference is None: # Create joint image mask in target space joint_image_mask_builder = imb.JointImageMaskBuilder( stacks=stacks, target=HR_volume, dilation_radius=1, ) joint_image_mask_builder.run() HR_volume = joint_image_mask_builder.get_stack() ph.print_info("Isotropic reconstruction space is centered around " "joint stack masks. ") # Crop to space defined by mask (plus extra margin) HR_volume = HR_volume.get_cropped_stack_based_on_mask( boundary_i=args.extra_frame_target, boundary_j=args.extra_frame_target, boundary_k=args.extra_frame_target, unit="mm", ) # Scattered Data Approximation to get first estimate of HR volume ph.print_title("First Estimate of HR Volume") SDA = sda.ScatteredDataApproximation(stacks, HR_volume, sigma=args.sigma) SDA.run() HR_volume = SDA.get_reconstruction() time_reconstruction = ph.stop_timing(time_tmp) if args.verbose: tmp = list(stacks) tmp.insert(0, HR_volume) sitkh.show_stacks(tmp, segmentation=HR_volume) # ----------------Two-step Slice-to-Volume Registration SRR---------------- SRR = tk.TikhonovSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TK1", minimizer="lsmr", alpha=args.alpha_first, iter_max=args.iter_max_first, verbose=True, use_masks=args.use_masks_srr, ) if args.two_step_cycles > 0: registration = regsitk.SimpleItkRegistration( moving=HR_volume, use_fixed_mask=True, use_moving_mask=True, use_verbose=args.verbose, interpolator="Linear", metric="Correlation", use_multiresolution_framework=args.multiresolution, shrink_factors=args.shrink_factors, smoothing_sigmas=args.smoothing_sigmas, initializer_type="SelfGEOMETRY", optimizer="ConjugateGradientLineSearch", optimizer_params={ "learningRate": 1, "numberOfIterations": 100, "lineSearchUpperLimit": 2, }, scales_estimator="Jacobian", ) two_step_s2v_reg_recon = \ pipeline.TwoStepSliceToVolumeRegistrationReconstruction( stacks=stacks, reference=HR_volume, registration_method=registration, reconstruction_method=SRR, cycles=args.two_step_cycles, alpha_range=[args.alpha_first, args.alpha], verbose=args.verbose, ) two_step_s2v_reg_recon.run() HR_volume_iterations = \ two_step_s2v_reg_recon.get_iterative_reconstructions() time_registration += \ two_step_s2v_reg_recon.get_computational_time_registration() time_reconstruction += \ two_step_s2v_reg_recon.get_computational_time_reconstruction() else: HR_volume_iterations = [] # Write motion-correction results if args.write_motion_correction: for stack in stacks: stack.write( os.path.join(args.dir_output, args.subfolder_motion_correction), write_mask=True, write_slices=True, write_transforms=True, suffix_mask=args.suffix_mask, ) # ------------------Final Super-Resolution Reconstruction------------------ ph.print_title("Final Super-Resolution Reconstruction") if args.reconstruction_type in ["TVL2", "HuberL2"]: SRR = pd.PrimalDualSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TV" if args.reconstruction_type == "TVL2" else "huber", iterations=args.iterations, ) else: SRR = tk.TikhonovSolver( stacks=stacks, reconstruction=HR_volume, reg_type="TK1" if args.reconstruction_type == "TK1L2" else "TK0", use_masks=args.use_masks_srr, ) SRR.set_alpha(args.alpha) SRR.set_iter_max(args.iter_max) SRR.set_verbose(True) SRR.run() time_reconstruction += SRR.get_computational_time() elapsed_time_total = ph.stop_timing(time_start) # Write SRR result HR_volume_final = SRR.get_reconstruction() HR_volume_final.set_filename(SRR.get_setting_specific_filename()) HR_volume_final.write(args.dir_output, write_mask=True, suffix_mask=args.suffix_mask) HR_volume_iterations.insert(0, HR_volume_final) for stack in stacks: HR_volume_iterations.append(stack) if args.verbose and not args.provide_comparison: sitkh.show_stacks(HR_volume_iterations, segmentation=HR_volume) # HR_volume_final.show() # Show SRR together with linearly resampled input data. # Additionally, a script is generated to open files if args.provide_comparison: sitkh.show_stacks( HR_volume_iterations, segmentation=HR_volume, show_comparison_file=args.provide_comparison, dir_output=os.path.join(args.dir_output, args.subfolder_comparison), ) # Summary ph.print_title("Summary") print("Computational Time for Data Preprocessing: %s" % (time_data_preprocessing)) print("Computational Time for Registrations: %s" % (time_registration)) print("Computational Time for Reconstructions: %s" % (time_reconstruction)) print("Computational Time for Entire Reconstruction Pipeline: %s" % (elapsed_time_total)) ph.print_line_separator() return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform (linear) intensity correction across " "stacks/images given a reference stack/image", ) input_parser.add_filenames(required=True) input_parser.add_dir_output(required=True) input_parser.add_reference(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_search_angle(default=180) input_parser.add_prefix_output(default="IC_") input_parser.add_log_config(default=1) input_parser.add_option( option_string="--registration", type=int, help="Turn on/off registration from image to reference prior to " "intensity correction.", default=0) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) if args.reference in args.filenames: args.filenames.remove(args.reference) # Read data data_reader = dr.MultipleImagesReader(args.filenames, suffix_mask=args.suffix_mask, extract_slices=False) data_reader.read_data() stacks = data_reader.get_data() data_reader = dr.MultipleImagesReader([args.reference], suffix_mask=args.suffix_mask, extract_slices=False) data_reader.read_data() reference = data_reader.get_data()[0] if args.registration: # Define search angle ranges for FLIRT in all three dimensions search_angles = [ "-searchr%s -%d %d" % (x, args.search_angle, args.search_angle) for x in ["x", "y", "z"] ] search_angles = (" ").join(search_angles) registration = regflirt.FLIRT( moving=reference, registration_type="Rigid", use_fixed_mask=True, use_moving_mask=True, options=search_angles, use_verbose=False, ) # Perform Intensity Correction ph.print_title("Perform Intensity Correction") intensity_corrector = ic.IntensityCorrection( use_reference_mask=True, use_individual_slice_correction=False, prefix_corrected=args.prefix_output, use_verbose=False, ) stacks_corrected = [None] * len(stacks) for i, stack in enumerate(stacks): if args.registration: ph.print_info("Image %d/%d: Registration ... " % (i + 1, len(stacks)), newline=False) registration.set_fixed(stack) registration.run() transform_sitk = registration.get_registration_transform_sitk() stack.update_motion_correction(transform_sitk) print("done") ph.print_info("Image %d/%d: Intensity Correction ... " % (i + 1, len(stacks)), newline=False) ref = reference.get_resampled_stack(stack.sitk) ref = st.Stack.from_sitk_image(image_sitk=ref.sitk, image_sitk_mask=stack.sitk_mask * ref.sitk_mask, filename=reference.get_filename()) intensity_corrector.set_stack(stack) intensity_corrector.set_reference(ref) intensity_corrector.run_linear_intensity_correction() # intensity_corrector.run_affine_intensity_correction() stacks_corrected[i] = \ intensity_corrector.get_intensity_corrected_stack() print("done (c1 = %g) " % intensity_corrector.get_intensity_correction_coefficients()) # Write Data stacks_corrected[i].write(args.dir_output, write_mask=True, suffix_mask=args.suffix_mask) if args.verbose: sitkh.show_stacks( [ reference, stacks_corrected[i], # stacks[i], ], segmentation=stacks_corrected[i]) # ph.pause() # Write reference too (although not intensity corrected) reference.write(args.dir_output, filename=args.prefix_output + reference.get_filename(), write_mask=True, suffix_mask=args.suffix_mask) elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Intensity Correction(s): %s" % (elapsed_time)) return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform Bias Field correction on images based on N4ITK.", ) input_parser.add_filenames(required=True) input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_prefix_output(default="N4ITK_") input_parser.add_option( option_string="--convergence-threshold", type=float, help="Specify the convergence threshold.", default=1e-6, ) input_parser.add_option( option_string="--spline-order", type=int, help="Specify the spline order defining the bias field estimate.", default=3, ) input_parser.add_option( option_string="--wiener-filter-noise", type=float, help="Specify the noise estimate defining the Wiener filter.", default=0.11, ) input_parser.add_option( option_string="--bias-field-fwhm", type=float, help="Specify the full width at half maximum parameter characterizing " "the width of the Gaussian deconvolution.", default=0.15, ) input_parser.add_log_script_execution(default=1) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) # Write script execution call if args.log_script_execution: input_parser.write_performed_script_execution( os.path.abspath(__file__)) # Read data data_reader = dr.MultipleImagesReader(args.filenames, suffix_mask=args.suffix_mask) data_reader.read_data() stacks = data_reader.get_data() # Perform Bias Field Correction ph.print_title("Perform Bias Field Correction") bias_field_corrector = n4itk.N4BiasFieldCorrection( convergence_threshold=args.convergence_threshold, spline_order=args.spline_order, wiener_filter_noise=args.wiener_filter_noise, bias_field_fwhm=args.bias_field_fwhm, prefix_corrected=args.prefix_output, ) stacks_corrected = [None] * len(stacks) for i, stack in enumerate(stacks): ph.print_info("Image %d/%d: N4ITK Bias Field Correction ... " % (i + 1, len(stacks)), newline=False) bias_field_corrector.set_stack(stack) bias_field_corrector.run_bias_field_correction() stacks_corrected[i] = \ bias_field_corrector.get_bias_field_corrected_stack() print("done") ph.print_info("Image %d/%d: Computational time = %s" % (i + 1, len(stacks), bias_field_corrector.get_computational_time())) # Write Data stacks_corrected[i].write(args.dir_output, write_mask=True, suffix_mask=args.suffix_mask) if args.verbose: sitkh.show_stacks([stacks[i], stacks_corrected[i]], segmentation=stacks[i]) elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Bias Field Correction(s): %s" % (elapsed_time)) return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) # Read input input_parser = InputArgparser( description="Script to study reconstruction parameters and their " "impact on the volumetric reconstruction quality.", ) input_parser.add_dir_input() input_parser.add_filenames() input_parser.add_image_selection() input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_reconstruction_space() input_parser.add_reference( help="Path to reference NIfTI image file. If given the volumetric " "reconstructed is performed in this physical space. " "Either a reconstruction space or a reference must be provided", required=False) input_parser.add_reference_mask(default=None) input_parser.add_study_name() input_parser.add_reconstruction_type(default="TK1L2") input_parser.add_measures(default=["PSNR", "RMSE", "SSIM", "NCC", "NMI"]) input_parser.add_tv_solver(default="PD") input_parser.add_iterations(default=50) input_parser.add_rho(default=0.1) input_parser.add_iter_max(default=10) input_parser.add_minimizer(default="lsmr") input_parser.add_alpha(default=0.01) input_parser.add_data_loss(default="linear") input_parser.add_data_loss_scale(default=1) input_parser.add_log_script_execution(default=1) input_parser.add_verbose(default=1) # Range for parameter sweeps input_parser.add_alpha_range(default=[0.001, 0.05, 20]) # TK1L2 # input_parser.add_alpha_range(default=[0.001, 0.003, 10]) # TVL2, HuberL2 input_parser.add_data_losses( # default=["linear", "arctan"] ) input_parser.add_data_loss_scale_range( # default=[0.1, 1.5, 2] ) args = input_parser.parse_args() input_parser.print_arguments(args) if args.reference is None and args.reconstruction_space is None: raise IOError("Either reference (--reference) or reconstruction space " "(--reconstruction-space) must be provided.") # Write script execution call if args.log_script_execution: input_parser.write_performed_script_execution( os.path.abspath(__file__)) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") # Neither '--dir-input' nor '--filenames' was specified if args.filenames is not None and args.dir_input is not None: raise IOError( "Provide input by either '--dir-input' or '--filenames' " "but not both together") # '--dir-input' specified elif args.dir_input is not None: data_reader = dr.ImageSlicesDirectoryReader( path_to_directory=args.dir_input, suffix_mask=args.suffix_mask, image_selection=args.image_selection) # '--filenames' specified elif args.filenames is not None: data_reader = dr.MultipleImagesReader( args.filenames, suffix_mask=args.suffix_mask) else: raise IOError( "Provide input by either '--dir-input' or '--filenames'") data_reader.read_data() stacks = data_reader.get_data() ph.print_info("%d input stacks read for further processing" % len(stacks)) if args.reference is not None: reference = st.Stack.from_filename( file_path=args.reference, file_path_mask=args.reference_mask, extract_slices=False) reconstruction_space = stacks[0].get_resampled_stack(reference.sitk) reconstruction_space = \ reconstruction_space.get_stack_multiplied_with_mask() x_ref = sitk.GetArrayFromImage(reference.sitk).flatten() x_ref_mask = sitk.GetArrayFromImage(reference.sitk_mask).flatten() else: reconstruction_space = st.Stack.from_filename( file_path=args.reconstruction_space, extract_slices=False) reconstruction_space = stacks[0].get_resampled_stack( reconstruction_space.sitk) reconstruction_space = \ reconstruction_space.get_stack_multiplied_with_mask() x_ref = None x_ref_mask = None # ----------------------------Set Up Parameters---------------------------- parameters = {} parameters["alpha"] = np.linspace( args.alpha_range[0], args.alpha_range[1], int(args.alpha_range[2])) if args.data_losses is not None: parameters["data_loss"] = args.data_losses if args.data_loss_scale_range is not None: parameters["data_loss_scale"] = np.linspace( args.data_loss_scale_range[0], args.data_loss_scale_range[1], int(args.data_loss_scale_range[2])) # --------------------------Set Up Parameter Study------------------------- if args.study_name is None: name = args.reconstruction_type else: name = args.study_name reconstruction_info = { "shape": reconstruction_space.sitk.GetSize()[::-1], "origin": reconstruction_space.sitk.GetOrigin(), "spacing": reconstruction_space.sitk.GetSpacing(), "direction": reconstruction_space.sitk.GetDirection(), } # Create Tikhonov solver from which all information can be extracted # (also for other reconstruction types) tmp = tk.TikhonovSolver( stacks=stacks, reconstruction=reconstruction_space, alpha=args.alpha, iter_max=args.iter_max, data_loss=args.data_loss, data_loss_scale=args.data_loss_scale, reg_type="TK1", minimizer=args.minimizer, verbose=args.verbose, ) solver = tmp.get_solver() parameter_study_interface = \ deconv_interface.DeconvolutionParameterStudyInterface( A=solver.get_A(), A_adj=solver.get_A_adj(), D=solver.get_B(), D_adj=solver.get_B_adj(), b=solver.get_b(), x0=solver.get_x0(), alpha=solver.get_alpha(), x_scale=solver.get_x_scale(), data_loss=solver.get_data_loss(), data_loss_scale=solver.get_data_loss_scale(), iter_max=solver.get_iter_max(), minimizer=solver.get_minimizer(), iterations=args.iterations, measures=args.measures, dimension=3, L2=16./reconstruction_space.sitk.GetSpacing()[0]**2, reconstruction_type=args.reconstruction_type, rho=args.rho, dir_output=args.dir_output, parameters=parameters, name=name, reconstruction_info=reconstruction_info, x_ref=x_ref, x_ref_mask=x_ref_mask, tv_solver=args.tv_solver, verbose=args.verbose, ) parameter_study_interface.set_up_parameter_study() parameter_study = parameter_study_interface.get_parameter_study() # Run parameter study parameter_study.run() print("\nComputational time for Deconvolution Parameter Study %s: %s" % (name, parameter_study.get_computational_time())) return 0
def from_motion_score(cls, file_paths, file_paths_masks): if len(file_paths) != len(file_paths_masks): raise ValueError("Number of provided images and masks must match") t0 = ph.start_timing() tse = cls() volumes = np.array([ TargetStackEstimator._compute_volume(f) for f in file_paths_masks ]) # only allow stacks with minimum volume, i.e. anatomical/brain coverage vol_min = 0.7 * np.median(volumes) indices = [i for i in range(len(volumes)) if volumes[i] > vol_min] # read all eligible stacks stacks = [ st.Stack.from_filename( file_path=file_paths[i], file_path_mask=file_paths_masks[i], extract_slices=False, ) for i in indices ] # crop stack to bounding box of mask stacks = [s.get_cropped_stack_based_on_mask() for s in stacks] # debug # for i_stack, stack in enumerate(stacks): # stack.show(label=str(indices[i_stack])) # compute motion scores of eligible stacks motion_scores = [None] * len(indices) for i_stack in range(len(indices)): # compute singular values s = TargetStackEstimator._compute_singular_values(stacks[i_stack]) # compute motion score motion_scores[i_stack] = tse._compute_motion_score[tse._mode](s) # select stack with minimum motion (score) selection_best = np.argmin(motion_scores) # reference back to input file_paths index target_stack_index = indices[selection_best] tse._target_stack_index = target_stack_index # computational time tse._computational_time = ph.stop_timing(t0) # debug # print(indices, len(indices), len(file_paths)) # print("Best: %d" % target_stack_index) # print(motion_scores) # print(tse.get_computational_time()) # ph.killall_itksnap() return tse
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform Bias Field correction using N4ITK.", ) input_parser.add_filename(required=True) input_parser.add_output(required=True) input_parser.add_filename_mask() input_parser.add_option( option_string="--convergence-threshold", type=float, help="Specify the convergence threshold.", default=1e-6, ) input_parser.add_option( option_string="--spline-order", type=int, help="Specify the spline order defining the bias field estimate.", default=3, ) input_parser.add_option( option_string="--wiener-filter-noise", type=float, help="Specify the noise estimate defining the Wiener filter.", default=0.11, ) input_parser.add_option( option_string="--bias-field-fwhm", type=float, help="Specify the full width at half maximum parameter characterizing " "the width of the Gaussian deconvolution.", default=0.15, ) input_parser.add_log_config(default=1) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]): raise ValueError( "output filename invalid; allowed extensions are: %s" % ", ".join(ALLOWED_EXTENSIONS)) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) # Read data stack = st.Stack.from_filename( file_path=args.filename, file_path_mask=args.filename_mask, extract_slices=False, ) # Perform Bias Field Correction # ph.print_title("Perform Bias Field Correction") bias_field_corrector = n4itk.N4BiasFieldCorrection( stack=stack, use_mask=True if args.filename_mask is not None else False, convergence_threshold=args.convergence_threshold, spline_order=args.spline_order, wiener_filter_noise=args.wiener_filter_noise, bias_field_fwhm=args.bias_field_fwhm, ) ph.print_info("N4ITK Bias Field Correction ... ", newline=False) bias_field_corrector.run_bias_field_correction() stack_corrected = bias_field_corrector.get_bias_field_corrected_stack() print("done") dw.DataWriter.write_image(stack_corrected.sitk, args.output) elapsed_time = ph.stop_timing(time_start) if args.verbose: ph.show_niftis([args.filename, args.output]) ph.print_title("Summary") exe_file_info = os.path.basename(os.path.abspath(__file__)).split(".")[0] print("%s | Computational Time for Bias Field Correction: %s" % (exe_file_info, elapsed_time)) return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) # Read input input_parser = InputArgparser( description="Volumetric MRI reconstruction framework to reconstruct " "an isotropic, high-resolution 3D volume from multiple " "motion-corrected (or static) stacks of low-resolution slices.", ) input_parser.add_dir_input() input_parser.add_filenames() input_parser.add_image_selection() input_parser.add_dir_output(required=True) input_parser.add_prefix_output(default="SRR_") input_parser.add_suffix_mask(default="_mask") input_parser.add_target_stack_index(default=0) input_parser.add_extra_frame_target(default=10) input_parser.add_isotropic_resolution(default=None) input_parser.add_reconstruction_space(default=None) input_parser.add_minimizer(default="lsmr") input_parser.add_iter_max(default=10) input_parser.add_reconstruction_type(default="TK1L2") input_parser.add_data_loss(default="linear") input_parser.add_data_loss_scale(default=1) input_parser.add_alpha(default=0.02 # TK1L2 # default=0.006 #TVL2, HuberL2 ) input_parser.add_rho(default=0.5) input_parser.add_tv_solver(default="PD") input_parser.add_pd_alg_type(default="ALG2") input_parser.add_iterations(default=15) input_parser.add_subfolder_comparison() input_parser.add_provide_comparison(default=0) input_parser.add_log_script_execution(default=1) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) # Write script execution call if args.log_script_execution: input_parser.write_performed_script_execution( os.path.abspath(__file__)) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") # Neither '--dir-input' nor '--filenames' was specified if args.filenames is not None and args.dir_input is not None: raise IOError("Provide input by either '--dir-input' or '--filenames' " "but not both together") # '--dir-input' specified elif args.dir_input is not None: data_reader = dr.ImageSlicesDirectoryReader( path_to_directory=args.dir_input, suffix_mask=args.suffix_mask, image_selection=args.image_selection) # '--filenames' specified elif args.filenames is not None: data_reader = dr.MultipleImagesReader(args.filenames, suffix_mask=args.suffix_mask) else: raise IOError("Provide input by either '--dir-input' or '--filenames'") if args.reconstruction_type not in ["TK1L2", "TVL2", "HuberL2"]: raise IOError("Reconstruction type unknown") data_reader.read_data() stacks = data_reader.get_data() ph.print_info("%d input stacks read for further processing" % len(stacks)) # Reconstruction space is given isotropically resampled target stack if args.reconstruction_space is None: recon0 = \ stacks[args.target_stack_index].get_isotropically_resampled_stack( resolution=args.isotropic_resolution, extra_frame=args.extra_frame_target) # Reconstruction space was provided by user else: recon0 = st.Stack.from_filename(args.reconstruction_space, extract_slices=False) # Change resolution for isotropic resolution if provided by user if args.isotropic_resolution is not None: recon0 = recon0.get_isotropically_resampled_stack( args.isotropic_resolution) # Use image information of selected target stack as recon0 serves # as initial value for reconstruction recon0 = \ stacks[args.target_stack_index].get_resampled_stack(recon0.sitk) recon0 = recon0.get_stack_multiplied_with_mask() if args.reconstruction_type in ["TVL2", "HuberL2"]: ph.print_title("Compute Initial value for %s" % args.reconstruction_type) SRR0 = tk.TikhonovSolver( stacks=stacks, reconstruction=recon0, alpha=args.alpha, iter_max=args.iter_max, reg_type="TK1", minimizer=args.minimizer, data_loss=args.data_loss, data_loss_scale=args.data_loss_scale, # verbose=args.verbose, ) SRR0.run() recon = SRR0.get_reconstruction() recon.set_filename(SRR0.get_setting_specific_filename(args.prefix_output)) recon.write(args.dir_output) # List to store SRRs recons = [] for i in range(0, len(stacks)): recons.append(stacks[i]) recons.insert(0, recon) if args.reconstruction_type in ["TVL2", "HuberL2"]: ph.print_title("Compute %s reconstruction" % args.reconstruction_type) if args.tv_solver == "ADMM": SRR = admm.ADMMSolver( stacks=stacks, reconstruction=st.Stack.from_stack(SRR0.get_reconstruction()), minimizer=args.minimizer, alpha=args.alpha, iter_max=args.iter_max, rho=args.rho, data_loss=args.data_loss, iterations=args.iterations, verbose=args.verbose, ) SRR.run() recon = SRR.get_reconstruction() recon.set_filename( SRR.get_setting_specific_filename(args.prefix_output)) recons.insert(0, recon) recon.write(args.dir_output) else: SRR = pd.PrimalDualSolver( stacks=stacks, reconstruction=st.Stack.from_stack(SRR0.get_reconstruction()), minimizer=args.minimizer, alpha=args.alpha, iter_max=args.iter_max, iterations=args.iterations, alg_type=args.pd_alg_type, reg_type="TV" if args.reconstruction_type == "TVL2" else "huber", data_loss=args.data_loss, verbose=args.verbose, ) SRR.run() recon = SRR.get_reconstruction() recon.set_filename( SRR.get_setting_specific_filename(args.prefix_output)) recons.insert(0, recon) recon.write(args.dir_output) if args.verbose and not args.provide_comparison: sitkh.show_stacks(recons) # Show SRR together with linearly resampled input data. # Additionally, a script is generated to open files if args.provide_comparison: sitkh.show_stacks( recons, show_comparison_file=args.provide_comparison, dir_output=os.path.join(args.dir_output, args.subfolder_comparison), ) ph.print_line_separator() elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Volumetric Reconstruction: %s" % (elapsed_time)) return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform automatic brain masking using " "fetal_brain_seg, part of the MONAIfbs package " "(https://github.com/gift-surg/MONAIfbs). ", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks(required=False) input_parser.add_dir_output(required=False) input_parser.add_verbose(default=0) input_parser.add_log_config(default=0) input_parser.add_option( option_string="--neuroimage-legacy-seg", type=int, required=False, default=0, help="If set to 1, use the legacy method for fetal brain segmentation " "i.e. the two-step approach proposed in Ebner, Wang et al " "NeuroImage (2020)" ) args = input_parser.parse_args() input_parser.print_arguments(args) if args.neuroimage_legacy_seg: try: DIR_FETAL_BRAIN_SEG = os.environ["FETAL_BRAIN_SEG"] except KeyError as e: raise RuntimeError( "Environment variable FETAL_BRAIN_SEG is not specified. " "Specify the root directory of fetal_brain_seg " "(https://github.com/gift-surg/fetal_brain_seg) " "using " "'export FETAL_BRAIN_SEG=path_to_fetal_brain_seg_dir' " "(in bashrc).") else: try: import monaifbs DIR_FETAL_BRAIN_SEG = os.path.dirname(monaifbs.__file__) except ImportError as e: raise RuntimeError( "monaifbs not correctly installed. " "Please check its installation running " "pip install -e MONAIfbs/ " ) print("Using executable from {}".format(DIR_FETAL_BRAIN_SEG)) if args.filenames_masks is None and args.dir_output is None: raise IOError("Either --filenames-masks or --dir-output must be set") if args.dir_output is not None: args.filenames_masks = [ os.path.join(args.dir_output, os.path.basename(f)) for f in args.filenames ] if len(args.filenames) != len(args.filenames_masks): raise IOError("Number of filenames and filenames-masks must match") if args.log_config: input_parser.log_config(os.path.abspath(__file__)) cd_fetal_brain_seg = "cd %s" % DIR_FETAL_BRAIN_SEG for f, m in zip(args.filenames, args.filenames_masks): if not ph.file_exists(f): raise IOError("File '%s' does not exist" % f) # use absolute path for input image f = os.path.abspath(f) # use absolute path for output image dir_output = os.path.dirname(m) if not os.path.isabs(dir_output): dir_output = os.path.realpath( os.path.join(os.getcwd(), dir_output)) m = os.path.join(dir_output, os.path.basename(m)) ph.create_directory(dir_output) # Change to root directory of fetal_brain_seg cmds = [cd_fetal_brain_seg] # Run masking independently (Takes longer but ensures that it does # not terminate because of provided 'non-brain images') cmd_args = ["python fetal_brain_seg.py"] cmd_args.append("--input_names '%s'" % f) cmd_args.append("--segment_output_names '%s'" % m) cmds.append(" ".join(cmd_args)) # Execute both steps cmd = " && ".join(cmds) flag = ph.execute_command(cmd) if flag != 0: ph.print_warning( "Error using fetal_brain_seg. \n" "Execute '%s' for further investigation" % cmd) ph.print_info("Fetal brain segmentation written to '%s'" % m) if args.verbose: ph.show_nifti(f, segmentation=m) elapsed_time_total = ph.stop_timing(time_start) ph.print_title("Summary") exe_file_info = os.path.basename(os.path.abspath(__file__)).split(".")[0] print("%s | Computational Time: %s" % (exe_file_info, elapsed_time_total)) return 0
def run(self): print_precisicion = 3 print_suppress = True if self._optimizer_method in ["lm"]: verbose = 1 if self._optimizer_loss not in ["linear"]: self._optimizer_loss = "linear" print( "Optimizer method 'lm' only supports 'linear' loss function. " ) else: verbose = 2 jac = '2-point' # jac = '3-point' x_scale = 1.0 # or array # x_scale = 'jac' #or array # Initialize registration pipeline self._run_registration_pipeline_initialization() if self._use_verbose: print("Initial values = ") ph.print_numpy_array(self._parameters, precision=print_precisicion, suppress=print_suppress) # Parameter normalization if self._use_parameter_normalization: parameter_normalization = pn.ParameterNormalization( self._parameters) parameter_normalization.compute_normalization_coefficients() coefficients = parameter_normalization.get_normalization_coefficients( ) # Use absolute mean for normalization scale = abs(np.array(coefficients[0])) # scale could be zero (like for rotation) scale[np.where(scale == 0)] = 1 if self._use_verbose: print("Normalization parameters:") ph.print_numpy_array(scale, precision=print_precisicion, suppress=print_suppress) # Each slice with the same scaling x_scale = np.tile(scale, self._parameters.shape[0]) # HACK self._transforms_2D_itk = [None] * self._N_slices for i in range(0, self._N_slices): self._transforms_2D_itk[i] = self._new_transform_itk[ self._transform_type]() self._transforms_2D_itk[i].SetParameters( itk.OptimizerParameters[itk.D]( self._transforms_2D_sitk[i].GetParameters())) self._transforms_2D_itk[i].SetFixedParameters( itk.OptimizerParameters[itk.D]( self._transforms_2D_sitk[i].GetFixedParameters())) # Get cost function and its Jacobian w.r.t. the parameters fun = self._get_residual_call() jac = self._get_jacobian_residual_call() x0 = self._parameters0_vec.flatten() time_start = ph.start_timing() if self._optimizer == "least_squares": self._print_info_text_least_squares() res = self._run_optimizer_least_squares( fun=fun, jac=jac, x0=x0, method=self._optimizer_method, loss=self._optimizer_loss, iter_max=self._optimizer_iter_max, verbose=verbose, x_scale=x_scale) else: self._print_info_text_minimize() res = self._run_optimizer_minimize( fun=fun, jac=jac, x0=x0, method=self._optimizer, loss=self._optimizer_loss, iter_max=self._optimizer_iter_max, verbose=verbose, x_scale=x_scale) self._elapsed_time = ph.stop_timing(time_start) # Get and reshape final transform parameters for each slice self._parameters = res.reshape(self._parameters.shape) # Denormalize parameters # self._parameters = self._parameter_normalizer.denormalize_parameters(self._parameters) if self._use_verbose: print("Final values = ") ph.print_numpy_array(self._parameters, precision=print_precisicion, suppress=print_suppress) # if self._use_verbose: # print("Final values = ") # print(self._parameters) # Apply motion correction and compute slice transforms self._apply_motion_correction()
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Register an obtained reconstruction (moving) " "to a template image/space (fixed) using rigid registration. " "The resulting registration can optionally be applied to previously " "obtained motion correction slice transforms so that a volumetric " "reconstruction is possible in the (standard anatomical) space " "defined by the fixed.", ) input_parser.add_fixed(required=True) input_parser.add_moving(required=True) input_parser.add_output(help="Path to registration transform (.txt)", required=True) input_parser.add_fixed_mask() input_parser.add_moving_mask() input_parser.add_dir_input_mc() input_parser.add_search_angle(default=180) input_parser.add_option(option_string="--initial-transform", type=str, help="Path to initial transform.", default=None) input_parser.add_option( option_string="--test-ap-flip", type=int, help="Turn on/off functionality to run an additional registration " "after an AP-flip. Seems to be more robust to find a better " "registration outcome in general.", default=1) input_parser.add_option( option_string="--use-flirt", type=int, help="Turn on/off functionality to use FLIRT for the registration.", default=1) input_parser.add_option( option_string="--use-regaladin", type=int, help="Turn on/off functionality to use RegAladin for the " "registration.", default=1) input_parser.add_verbose(default=0) input_parser.add_log_config(default=1) args = input_parser.parse_args() input_parser.print_arguments(args) debug = 0 if args.log_config: input_parser.log_config(os.path.abspath(__file__)) if not args.use_regaladin and not args.use_flirt: raise IOError("Either RegAladin or FLIRT must be activated.") if not args.output.endswith(".txt"): raise IOError("output transformation path must end in '.txt'") dir_output = os.path.dirname(args.output) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") fixed = st.Stack.from_filename(file_path=args.fixed, file_path_mask=args.fixed_mask, extract_slices=False) moving = st.Stack.from_filename(file_path=args.moving, file_path_mask=args.moving_mask, extract_slices=False) if args.initial_transform is not None: transform_sitk = sitkh.read_transform_sitk(args.initial_transform) else: transform_sitk = sitk.AffineTransform(fixed.sitk.GetDimension()) sitk.WriteTransform(transform_sitk, args.output) path_to_tmp_output = os.path.join( DIR_TMP, ph.append_to_filename(os.path.basename(args.moving), "_warped")) # -------------------Register Reconstruction to Template------------------- ph.print_title("Register Reconstruction to Template") if args.use_flirt: path_to_transform_flirt = os.path.join(DIR_TMP, "transform_flirt.txt") # Convert SimpleITK into FLIRT transform cmd = "simplereg_transform -sitk2flirt %s %s %s %s" % ( args.output, args.fixed, args.moving, path_to_transform_flirt) ph.execute_command(cmd, verbose=False) # Define search angle ranges for FLIRT in all three dimensions search_angles = [ "-searchr%s -%d %d" % (x, args.search_angle, args.search_angle) for x in ["x", "y", "z"] ] # flt = nipype.interfaces.fsl.FLIRT() # flt.inputs.in_file = args.moving # flt.inputs.reference = args.fixed # if args.initial_transform is not None: # flt.inputs.in_matrix_file = path_to_transform_flirt # flt.inputs.out_matrix_file = path_to_transform_flirt # # flt.inputs.output_type = "NIFTI_GZ" # flt.inputs.out_file = path_to_tmp_output # flt.inputs.args = "-dof 6" # flt.inputs.args += " %s" % " ".join(search_angles) # if args.moving_mask is not None: # flt.inputs.in_weight = args.moving_mask # if args.fixed_mask is not None: # flt.inputs.ref_weight = args.fixed_mask # ph.print_info("Run Registration (FLIRT) ... ", newline=False) # flt.run() # print("done") cmd_args = ["flirt"] cmd_args.append("-in %s" % args.moving) cmd_args.append("-ref %s" % args.fixed) if args.initial_transform is not None: cmd_args.append("-init %s" % path_to_transform_flirt) cmd_args.append("-omat %s" % path_to_transform_flirt) cmd_args.append("-out %s" % path_to_tmp_output) cmd_args.append("-dof 6") cmd_args.append((" ").join(search_angles)) if args.moving_mask is not None: cmd_args.append("-inweight %s" % args.moving_mask) if args.fixed_mask is not None: cmd_args.append("-refweight %s" % args.fixed_mask) ph.print_info("Run Registration (FLIRT) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=False) print("done") # Convert FLIRT to SimpleITK transform cmd = "simplereg_transform -flirt2sitk %s %s %s %s" % ( path_to_transform_flirt, args.fixed, args.moving, args.output) ph.execute_command(cmd, verbose=False) if debug: ph.show_niftis([args.fixed, path_to_tmp_output]) # Additionally, use RegAladin for more accurate alignment # Rationale: FLIRT has better capture range, but RegAladin seems to # find better alignment once it is within its capture range. if args.use_regaladin: path_to_transform_regaladin = os.path.join(DIR_TMP, "transform_regaladin.txt") # Convert SimpleITK to RegAladin transform cmd = "simplereg_transform -sitk2nreg %s %s" % ( args.output, path_to_transform_regaladin) ph.execute_command(cmd, verbose=False) # nreg = nipype.interfaces.niftyreg.RegAladin() # nreg.inputs.ref_file = args.fixed # nreg.inputs.flo_file = args.moving # nreg.inputs.res_file = path_to_tmp_output # nreg.inputs.in_aff_file = path_to_transform_regaladin # nreg.inputs.aff_file = path_to_transform_regaladin # nreg.inputs.args = "-rigOnly -voff" # if args.moving_mask is not None: # nreg.inputs.fmask_file = args.moving_mask # if args.fixed_mask is not None: # nreg.inputs.rmask_file = args.fixed_mask # ph.print_info("Run Registration (RegAladin) ... ", newline=False) # nreg.run() # print("done") cmd_args = ["reg_aladin"] cmd_args.append("-ref %s" % args.fixed) cmd_args.append("-flo %s" % args.moving) cmd_args.append("-res %s" % path_to_tmp_output) if args.initial_transform is not None or args.use_flirt == 1: cmd_args.append("-inaff %s" % path_to_transform_regaladin) cmd_args.append("-aff %s" % path_to_transform_regaladin) # cmd_args.append("-cog") # cmd_args.append("-ln 2") cmd_args.append("-rigOnly") cmd_args.append("-voff") if args.moving_mask is not None: cmd_args.append("-fmask %s" % args.moving_mask) if args.fixed_mask is not None: cmd_args.append("-rmask %s" % args.fixed_mask) ph.print_info("Run Registration (RegAladin) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=False) print("done") # Convert RegAladin to SimpleITK transform cmd = "simplereg_transform -nreg2sitk %s %s" % ( path_to_transform_regaladin, args.output) ph.execute_command(cmd, verbose=False) if debug: ph.show_niftis([args.fixed, path_to_tmp_output]) if args.test_ap_flip: path_to_transform_flip = os.path.join(DIR_TMP, "transform_flip.txt") path_to_tmp_output_flip = os.path.join(DIR_TMP, "output_flip.nii.gz") # Get AP-flip transform transform_ap_flip_sitk = get_ap_flip_transform(args.fixed) path_to_transform_flip_regaladin = os.path.join( DIR_TMP, "transform_flip_regaladin.txt") sitk.WriteTransform(transform_ap_flip_sitk, path_to_transform_flip) # Compose current transform with AP flip transform cmd = "simplereg_transform -c %s %s %s" % ( args.output, path_to_transform_flip, path_to_transform_flip) ph.execute_command(cmd, verbose=False) # Convert SimpleITK to RegAladin transform cmd = "simplereg_transform -sitk2nreg %s %s" % ( path_to_transform_flip, path_to_transform_flip_regaladin) ph.execute_command(cmd, verbose=False) # nreg = nipype.interfaces.niftyreg.RegAladin() # nreg.inputs.ref_file = args.fixed # nreg.inputs.flo_file = args.moving # nreg.inputs.res_file = path_to_tmp_output_flip # nreg.inputs.in_aff_file = path_to_transform_flip_regaladin # nreg.inputs.aff_file = path_to_transform_flip_regaladin # nreg.inputs.args = "-rigOnly -voff" # if args.moving_mask is not None: # nreg.inputs.fmask_file = args.moving_mask # if args.fixed_mask is not None: # nreg.inputs.rmask_file = args.fixed_mask # ph.print_info("Run Registration AP-flipped (RegAladin) ... ", # newline=False) # nreg.run() # print("done") cmd_args = ["reg_aladin"] cmd_args.append("-ref %s" % args.fixed) cmd_args.append("-flo %s" % args.moving) cmd_args.append("-res %s" % path_to_tmp_output_flip) cmd_args.append("-inaff %s" % path_to_transform_flip_regaladin) cmd_args.append("-aff %s" % path_to_transform_flip_regaladin) cmd_args.append("-rigOnly") # cmd_args.append("-ln 2") cmd_args.append("-voff") if args.moving_mask is not None: cmd_args.append("-fmask %s" % args.moving_mask) if args.fixed_mask is not None: cmd_args.append("-rmask %s" % args.fixed_mask) ph.print_info("Run Registration AP-flipped (RegAladin) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=False) print("done") if debug: ph.show_niftis( [args.fixed, path_to_tmp_output, path_to_tmp_output_flip]) warped_moving = st.Stack.from_filename(path_to_tmp_output, extract_slices=False) warped_moving_flip = st.Stack.from_filename(path_to_tmp_output_flip, extract_slices=False) fixed = st.Stack.from_filename(args.fixed, args.fixed_mask) stacks = [warped_moving, warped_moving_flip] image_similarity_evaluator = ise.ImageSimilarityEvaluator( stacks=stacks, reference=fixed) image_similarity_evaluator.compute_similarities() similarities = image_similarity_evaluator.get_similarities() if similarities["NMI"][1] > similarities["NMI"][0]: ph.print_info("AP-flipped outcome better") # Convert RegAladin to SimpleITK transform cmd = "simplereg_transform -nreg2sitk %s %s" % ( path_to_transform_flip_regaladin, args.output) ph.execute_command(cmd, verbose=False) # Copy better outcome cmd = "cp -p %s %s" % (path_to_tmp_output_flip, path_to_tmp_output) ph.execute_command(cmd, verbose=False) else: ph.print_info("AP-flip does not improve outcome") if args.dir_input_mc is not None: transform_sitk = sitkh.read_transform_sitk(args.output, inverse=1) if args.dir_input_mc.endswith("/"): subdir_mc = args.dir_input_mc.split("/")[-2] else: subdir_mc = args.dir_input_mc.split("/")[-1] dir_output_mc = os.path.join(dir_output, subdir_mc) ph.create_directory(dir_output_mc, delete_files=True) pattern = REGEX_FILENAMES + "[.]tfm" p = re.compile(pattern) trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)] for t in trafos: path_to_input_transform = os.path.join(args.dir_input_mc, t) path_to_output_transform = os.path.join(dir_output_mc, t) t_sitk = sitkh.read_transform_sitk(path_to_input_transform) t_sitk = sitkh.get_composite_sitk_affine_transform( transform_sitk, t_sitk) sitk.WriteTransform(t_sitk, path_to_output_transform) if args.verbose: ph.show_niftis([args.fixed, path_to_tmp_output]) elapsed_time_total = ph.stop_timing(time_start) # Summary ph.print_title("Summary") print("Computational Time: %s" % (elapsed_time_total)) return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Run reconstruction pipeline including " "(i) bias field correction, " "(ii) volumetric reconstruction in subject space, " "(iii) volumetric reconstruction in template space, " "and (iv) some diagnostics to assess the obtained reconstruction.", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks(required=True) input_parser.add_target_stack(required=False) input_parser.add_suffix_mask(default="") input_parser.add_dir_output(required=True) input_parser.add_alpha(default=0.01) input_parser.add_verbose(default=0) input_parser.add_gestational_age(required=False) input_parser.add_prefix_output(default="") input_parser.add_search_angle(default=180) input_parser.add_multiresolution(default=0) input_parser.add_log_config(default=1) input_parser.add_isotropic_resolution() input_parser.add_reference() input_parser.add_reference_mask() input_parser.add_bias_field_correction(default=1) input_parser.add_intensity_correction(default=1) input_parser.add_iter_max(default=10) input_parser.add_two_step_cycles(default=3) input_parser.add_slice_thicknesses(default=None) input_parser.add_option( option_string="--run-bias-field-correction", type=int, help="Turn on/off bias field correction. " "If off, it is assumed that this step was already performed " "if --bias-field-correction is active.", default=1) input_parser.add_option( option_string="--run-recon-subject-space", type=int, help="Turn on/off reconstruction in subject space. " "If off, it is assumed that this step was already performed.", default=1) input_parser.add_option( option_string="--run-recon-template-space", type=int, help="Turn on/off reconstruction in template space. " "If off, it is assumed that this step was already performed.", default=1) input_parser.add_option( option_string="--run-diagnostics", type=int, help="Turn on/off diagnostics of the obtained volumetric " "reconstruction. ", default=0) input_parser.add_option( option_string="--initial-transform", type=str, help="Set initial transform to be used for register_image.", default=None) input_parser.add_outlier_rejection(default=1) input_parser.add_threshold_first(default=0.5) input_parser.add_threshold(default=0.8) input_parser.add_argument( "--sda", "-sda", action='store_true', help="If given, the volume is reconstructed using " "Scattered Data Approximation (Vercauteren et al., 2006). " "--alpha is considered the value for the standard deviation then. " "Recommended value is, e.g., --alpha 0.8") input_parser.add_argument( "--v2v-robust", "-v2v-robust", action='store_true', help="If given, a more robust volume-to-volume registration step is " "performed, i.e. four rigid registrations are performed using four " "rigid transform initializations based on " "principal component alignment of associated masks.") input_parser.add_interleave(default=3) input_parser.add_argument( "--s2v-hierarchical", "-s2v-hierarchical", action='store_true', help="If given, a hierarchical approach for the first slice-to-volume " "registration cycle is used, i.e. sub-packages defined by the " "specified interleave (--interleave) are registered until each " "slice is registered independently.") args = input_parser.parse_args() input_parser.print_arguments(args) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) filename_srr = "srr" dir_output_preprocessing = os.path.join(args.dir_output, "preprocessing_n4itk") dir_output_recon_subject_space = os.path.join(args.dir_output, "recon_subject_space") dir_output_recon_template_space = os.path.join(args.dir_output, "recon_template_space") dir_output_diagnostics = os.path.join(args.dir_output, "diagnostics") srr_subject = os.path.join(dir_output_recon_subject_space, "%s_subject.nii.gz" % filename_srr) srr_subject_mask = ph.append_to_filename(srr_subject, "_mask") srr_template = os.path.join(dir_output_recon_template_space, "%s_template.nii.gz" % filename_srr) srr_template_mask = ph.append_to_filename(srr_template, "_mask") trafo_template = os.path.join(dir_output_recon_template_space, "registration_transform_sitk.txt") srr_slice_coverage = os.path.join( dir_output_diagnostics, "%s_template_slicecoverage.nii.gz" % filename_srr) if args.bias_field_correction and args.run_bias_field_correction: for i, f in enumerate(args.filenames): output = os.path.join(dir_output_preprocessing, os.path.basename(f)) cmd_args = [] cmd_args.append("--filename '%s'" % f) cmd_args.append("--filename-mask '%s'" % args.filenames_masks[i]) cmd_args.append("--output '%s'" % output) # cmd_args.append("--verbose %d" % args.verbose) cmd_args.append("--log-config %d" % args.log_config) cmd = "niftymic_correct_bias_field %s" % (" ").join(cmd_args) time_start_bias = ph.start_timing() exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Bias field correction failed") elapsed_time_bias = ph.stop_timing(time_start_bias) filenames = [ os.path.join(dir_output_preprocessing, os.path.basename(f)) for f in args.filenames ] elif args.bias_field_correction and not args.run_bias_field_correction: elapsed_time_bias = ph.get_zero_time() filenames = [ os.path.join(dir_output_preprocessing, os.path.basename(f)) for f in args.filenames ] else: elapsed_time_bias = ph.get_zero_time() filenames = args.filenames # Specify target stack for intensity correction and reconstruction space if args.target_stack is None: target_stack = filenames[0] else: try: target_stack_index = args.filenames.index(args.target_stack) except ValueError as e: raise ValueError( "--target-stack must correspond to an image as provided by " "--filenames") target_stack = filenames[target_stack_index] # Add single quotes around individual filenames to account for whitespaces filenames = ["'" + f + "'" for f in filenames] filenames_masks = ["'" + f + "'" for f in args.filenames_masks] if args.run_recon_subject_space: cmd_args = ["niftymic_reconstruct_volume"] cmd_args.append("--filenames %s" % (" ").join(filenames)) cmd_args.append("--filenames-masks %s" % (" ").join(filenames_masks)) cmd_args.append("--multiresolution %d" % args.multiresolution) cmd_args.append("--target-stack '%s'" % target_stack) cmd_args.append("--output '%s'" % srr_subject) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--intensity-correction %d" % args.intensity_correction) cmd_args.append("--alpha %s" % args.alpha) cmd_args.append("--iter-max %d" % args.iter_max) cmd_args.append("--two-step-cycles %d" % args.two_step_cycles) cmd_args.append("--outlier-rejection %d" % args.outlier_rejection) cmd_args.append("--threshold-first %f" % args.threshold_first) cmd_args.append("--threshold %f" % args.threshold) if args.slice_thicknesses is not None: cmd_args.append("--slice-thicknesses %s" % " ".join(map(str, args.slice_thicknesses))) cmd_args.append("--verbose %d" % args.verbose) cmd_args.append("--log-config %d" % args.log_config) if args.isotropic_resolution is not None: cmd_args.append("--isotropic-resolution %f" % args.isotropic_resolution) if args.reference is not None: cmd_args.append("--reference %s" % args.reference) if args.reference_mask is not None: cmd_args.append("--reference-mask %s" % args.reference_mask) if args.sda: cmd_args.append("--sda") if args.v2v_robust: cmd_args.append("--v2v-robust") if args.s2v_hierarchical: cmd_args.append("--s2v-hierarchical") cmd = (" ").join(cmd_args) time_start_volrec = ph.start_timing() exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Reconstruction in subject space failed") # Compute SRR mask in subject space # (Approximated using SDA within reconstruct_volume) if 0: dir_motion_correction = os.path.join( dir_output_recon_subject_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % " ".join(filenames_masks)) cmd_args.append("--dir-input-mc '%s'" % dir_motion_correction) cmd_args.append("--output '%s'" % srr_subject_mask) cmd_args.append("--reconstruction-space '%s'" % srr_subject) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--mask") cmd_args.append("--log-config %d" % args.log_config) if args.slice_thicknesses is not None: cmd_args.append("--slice-thicknesses %s" % " ".join(map(str, args.slice_thicknesses))) if args.sda: cmd_args.append("--sda") cmd_args.append("--alpha 1") else: cmd_args.append("--alpha 0.1") cmd_args.append("--iter-max 5") cmd = (" ").join(cmd_args) ph.execute_command(cmd) elapsed_time_volrec = ph.stop_timing(time_start_volrec) else: elapsed_time_volrec = ph.get_zero_time() if args.run_recon_template_space: if args.gestational_age is None: template_stack_estimator = \ tse.TemplateStackEstimator.from_mask(srr_subject_mask) gestational_age = template_stack_estimator.get_estimated_gw() ph.print_info("Estimated gestational age: %d" % gestational_age) else: gestational_age = args.gestational_age template = os.path.join(DIR_TEMPLATES, "STA%d.nii.gz" % gestational_age) template_mask = os.path.join(DIR_TEMPLATES, "STA%d_mask.nii.gz" % gestational_age) # Register SRR to template space cmd_args = ["niftymic_register_image"] cmd_args.append("--fixed '%s'" % template) cmd_args.append("--moving '%s'" % srr_subject) cmd_args.append("--fixed-mask '%s'" % template_mask) cmd_args.append("--moving-mask '%s'" % srr_subject_mask) cmd_args.append( "--dir-input-mc '%s'" % os.path.join(dir_output_recon_subject_space, "motion_correction")) cmd_args.append("--output '%s'" % trafo_template) cmd_args.append("--verbose %s" % args.verbose) cmd_args.append("--log-config %d" % args.log_config) cmd_args.append("--refine-pca") if args.initial_transform is not None: cmd_args.append("--initial-transform '%s'" % args.initial_transform) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Registration to template space failed") # Compute SRR in template space dir_input_mc = os.path.join(dir_output_recon_template_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % (" ").join(filenames)) cmd_args.append("--filenames-masks %s" % (" ").join(filenames_masks)) cmd_args.append("--dir-input-mc '%s'" % dir_input_mc) cmd_args.append("--output '%s'" % srr_template) cmd_args.append("--reconstruction-space '%s'" % template) cmd_args.append("--target-stack '%s'" % target_stack) cmd_args.append("--iter-max %d" % args.iter_max) cmd_args.append("--alpha %s" % args.alpha) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--verbose %s" % args.verbose) cmd_args.append("--log-config %d" % args.log_config) if args.slice_thicknesses is not None: cmd_args.append("--slice-thicknesses %s" % " ".join(map(str, args.slice_thicknesses))) if args.sda: cmd_args.append("--sda") cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Reconstruction in template space failed") # Compute SRR mask in template space if 1: dir_motion_correction = os.path.join( dir_output_recon_template_space, "motion_correction") cmd_args = ["niftymic_reconstruct_volume_from_slices"] cmd_args.append("--filenames %s" % " ".join(filenames_masks)) cmd_args.append("--dir-input-mc '%s'" % dir_motion_correction) cmd_args.append("--output '%s'" % srr_template_mask) cmd_args.append("--reconstruction-space '%s'" % srr_template) cmd_args.append("--suffix-mask '%s'" % args.suffix_mask) cmd_args.append("--log-config %d" % args.log_config) cmd_args.append("--mask") if args.slice_thicknesses is not None: cmd_args.append("--slice-thicknesses %s" % " ".join(map(str, args.slice_thicknesses))) if args.sda: cmd_args.append("--sda") cmd_args.append("--alpha 1") else: cmd_args.append("--alpha 0.1") cmd_args.append("--iter-max 5") cmd = (" ").join(cmd_args) ph.execute_command(cmd) # Copy SRR to output directory if 0: output = "%sSRR_Stacks%d.nii.gz" % (args.prefix_output, len(args.filenames)) path_to_output = os.path.join(args.dir_output, output) cmd = "cp -p '%s' '%s'" % (srr_template, path_to_output) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Copy of SRR to output directory failed") # Multiply template mask with reconstruction if 0: cmd_args = ["niftymic_multiply"] fnames = [ srr_template, srr_template_mask, ] output_masked = "Masked_%s" % output path_to_output_masked = os.path.join(args.dir_output, output_masked) cmd_args.append("--filenames %s" % " ".join(fnames)) cmd_args.append("--output '%s'" % path_to_output_masked) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("SRR brain masking failed") else: elapsed_time_template = ph.get_zero_time() if args.run_diagnostics: dir_input_mc = os.path.join(dir_output_recon_template_space, "motion_correction") dir_output_orig_vs_proj = os.path.join(dir_output_diagnostics, "original_vs_projected") dir_output_selfsimilarity = os.path.join(dir_output_diagnostics, "selfsimilarity") dir_output_orig_vs_proj_pdf = os.path.join(dir_output_orig_vs_proj, "pdf") # Show slice coverage over reconstruction space exe = os.path.abspath(show_slice_coverage.__file__) cmd_args = ["python %s" % exe] cmd_args.append("--filenames %s" % (" ").join(filenames)) cmd_args.append("--dir-input-mc '%s'" % dir_input_mc) cmd_args.append("--reconstruction-space '%s'" % srr_template) cmd_args.append("--output '%s'" % srr_slice_coverage) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Slice coverage visualization failed") # Get simulated/projected slices exe = os.path.abspath(simulate_stacks_from_reconstruction.__file__) cmd_args = ["python %s" % exe] cmd_args.append("--filenames %s" % (" ").join(filenames)) if args.filenames_masks is not None: cmd_args.append("--filenames-masks %s" % (" ").join(filenames_masks)) cmd_args.append("--dir-input-mc '%s'" % dir_input_mc) cmd_args.append("--dir-output '%s'" % dir_output_orig_vs_proj) cmd_args.append("--reconstruction '%s'" % srr_template) cmd_args.append("--copy-data 1") if args.slice_thicknesses is not None: cmd_args.append("--slice-thicknesses %s" % " ".join(map(str, args.slice_thicknesses))) # cmd_args.append("--verbose %s" % args.verbose) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("SRR slice projections failed") filenames_simulated = [ "'%s" % os.path.join(dir_output_orig_vs_proj, os.path.basename(f)) for f in filenames ] # Evaluate slice similarities to ground truth exe = os.path.abspath(evaluate_simulated_stack_similarity.__file__) cmd_args = ["python %s" % exe] cmd_args.append("--filenames %s" % (" ").join(filenames_simulated)) if args.filenames_masks is not None: cmd_args.append("--filenames-masks %s" % (" ").join(filenames_masks)) cmd_args.append("--measures NCC SSIM") cmd_args.append("--dir-output '%s'" % dir_output_selfsimilarity) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Evaluation of stack similarities failed") # Generate figures showing the quantitative comparison exe = os.path.abspath( show_evaluated_simulated_stack_similarity.__file__) cmd_args = ["python %s" % exe] cmd_args.append("--dir-input '%s'" % dir_output_selfsimilarity) cmd_args.append("--dir-output '%s'" % dir_output_selfsimilarity) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: ph.print_warning("Visualization of stack similarities failed") # Generate pdfs showing all the side-by-side comparisons if 0: exe = os.path.abspath( export_side_by_side_simulated_vs_original_slice_comparison. __file__) cmd_args = ["python %s" % exe] cmd_args.append("--filenames %s" % (" ").join(filenames_simulated)) cmd_args.append("--dir-output '%s'" % dir_output_orig_vs_proj_pdf) cmd = "python %s %s" % (exe, (" ").join(cmd_args)) cmd = (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Generation of PDF overview failed") ph.print_title("Summary") print("Computational Time for Bias Field Correction: %s" % elapsed_time_bias) print("Computational Time for Volumetric Reconstruction: %s" % elapsed_time_volrec) print("Computational Time for Pipeline: %s" % ph.stop_timing(time_start)) return 0
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform automatic brain masking using " "fetal_brain_seg (https://github.com/gift-surg/fetal_brain_seg). ", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks(required=False) input_parser.add_dir_output(required=False) input_parser.add_verbose(default=0) input_parser.add_log_config(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) try: DIR_FETAL_BRAIN_SEG = os.environ["FETAL_BRAIN_SEG"] except KeyError as e: raise RuntimeError( "Environment variable FETAL_BRAIN_SEG is not specified. " "Specify the root directory of fetal_brain_seg " "(https://github.com/gift-surg/fetal_brain_seg) " "using " "'export FETAL_BRAIN_SEG=path_to_fetal_brain_seg_dir' " "(in bashrc).") if args.filenames_masks is None and args.dir_output is None: raise IOError("Either --filenames-masks or --dir-output must be set") if args.dir_output is not None: args.filenames_masks = [ os.path.join(args.dir_output, os.path.basename(f)) for f in args.filenames ] if len(args.filenames) != len(args.filenames_masks): raise IOError("Number of filenames and filenames-masks must match") if args.log_config: input_parser.log_config(os.path.abspath(__file__)) cd_fetal_brain_seg = "cd %s" % DIR_FETAL_BRAIN_SEG for f, m in zip(args.filenames, args.filenames_masks): if not ph.file_exists(f): raise IOError("File '%s' does not exist" % f) # use absolute path for input image f = os.path.abspath(f) # use absolute path for output image dir_output = os.path.dirname(m) if not os.path.isabs(dir_output): dir_output = os.path.realpath(os.path.join(os.getcwd(), dir_output)) m = os.path.join(dir_output, os.path.basename(m)) ph.create_directory(dir_output) # Change to root directory of fetal_brain_seg cmds = [cd_fetal_brain_seg] # Run masking independently (Takes longer but ensures that it does # not terminate because of provided 'non-brain images') cmd_args = ["python fetal_brain_seg.py"] cmd_args.append("--input_names '%s'" % f) cmd_args.append("--segment_output_names '%s'" % m) cmds.append(" ".join(cmd_args)) # Execute both steps cmd = " && ".join(cmds) flag = ph.execute_command(cmd) if flag != 0: ph.print_warning("Error using fetal_brain_seg. \n" "Execute '%s' for further investigation" % cmd) ph.print_info("Fetal brain segmentation written to '%s'" % m) if args.verbose: ph.show_nifti(f, segmentation=m) return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) # Read input input_parser = InputArgparser( description="Script to study reconstruction parameters and their " "impact on the volumetric reconstruction quality. " "This script can only be used to sweep through one single parameter, " "e.g. the regularization parameter 'alpha'. ") input_parser.add_filenames(required=True) input_parser.add_filenames_masks() input_parser.add_suffix_mask(default="_mask") input_parser.add_dir_input_mc() input_parser.add_dir_output(required=True) input_parser.add_reconstruction_space() input_parser.add_reference( help="Path to reference NIfTI image file. If given the volumetric " "reconstructed is performed in this physical space. " "Either a reconstruction space or a reference must be provided", required=False) input_parser.add_reference_mask(default=None) input_parser.add_study_name() input_parser.add_reconstruction_type(default="TK1L2") input_parser.add_measures( default=["PSNR", "MAE", "RMSE", "SSIM", "NCC", "NMI"]) input_parser.add_tv_solver(default="PD") input_parser.add_iterations(default=50) input_parser.add_rho(default=0.1) input_parser.add_iter_max(default=10) input_parser.add_minimizer(default="lsmr") input_parser.add_log_config(default=1) input_parser.add_use_masks_srr(default=0) input_parser.add_verbose(default=1) input_parser.add_slice_thicknesses(default=None) input_parser.add_argument( "--append", "-append", action='store_true', help="If given, results are appended to previously executed parameter " "study with identical parameters and study name store in the output " "directory.") # Range for parameter sweeps input_parser.add_alphas(default=list(np.linspace(0.01, 0.5, 5))) input_parser.add_data_losses(default=["linear"] # default=["linear", "arctan"] ) input_parser.add_data_loss_scales(default=[1] # default=[0.1, 0.5, 1.5] ) args = input_parser.parse_args() input_parser.print_arguments(args) if args.reference is None and args.reconstruction_space is None: raise IOError("Either reference (--reference) or reconstruction space " "(--reconstruction-space) must be provided.") if args.log_config: input_parser.log_config(os.path.abspath(__file__)) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") data_reader = dr.MultipleImagesReader( file_paths=args.filenames, file_paths_masks=args.filenames_masks, suffix_mask=args.suffix_mask, dir_motion_correction=args.dir_input_mc, stacks_slice_thicknesses=args.slice_thicknesses, ) data_reader.read_data() stacks = data_reader.get_data() ph.print_info("%d input stacks read for further processing" % len(stacks)) if args.reference is not None: reference = st.Stack.from_filename(file_path=args.reference, file_path_mask=args.reference_mask, extract_slices=False) reconstruction_space = stacks[0].get_resampled_stack(reference.sitk) reconstruction_space = \ reconstruction_space.get_stack_multiplied_with_mask() x_ref = sitk.GetArrayFromImage(reference.sitk).flatten() x_ref_mask = sitk.GetArrayFromImage(reference.sitk_mask).flatten() else: reconstruction_space = st.Stack.from_filename( file_path=args.reconstruction_space, extract_slices=False) reconstruction_space = stacks[0].get_resampled_stack( reconstruction_space.sitk) reconstruction_space = \ reconstruction_space.get_stack_multiplied_with_mask() x_ref = None x_ref_mask = None # ----------------------------Set Up Parameters---------------------------- parameters = {} parameters["alpha"] = args.alphas if len(args.data_losses) > 1: parameters["data_loss"] = args.data_losses if len(args.data_loss_scales) > 1: parameters["data_loss_scale"] = args.data_loss_scales # --------------------------Set Up Parameter Study------------------------- ph.print_title("Run Parameter Study") if args.study_name is None: name = args.reconstruction_type else: name = args.study_name reconstruction_info = { "shape": reconstruction_space.sitk.GetSize()[::-1], "origin": reconstruction_space.sitk.GetOrigin(), "spacing": reconstruction_space.sitk.GetSpacing(), "direction": reconstruction_space.sitk.GetDirection(), } # Create Tikhonov solver from which all information can be extracted # (also for other reconstruction types) tmp = tk.TikhonovSolver( stacks=stacks, reconstruction=reconstruction_space, alpha=args.alphas[0], iter_max=args.iter_max, data_loss=args.data_losses[0], data_loss_scale=args.data_loss_scales[0], reg_type="TK1", minimizer=args.minimizer, verbose=args.verbose, use_masks=args.use_masks_srr, ) solver = tmp.get_solver() parameter_study_interface = \ deconv_interface.DeconvolutionParameterStudyInterface( A=solver.get_A(), A_adj=solver.get_A_adj(), D=solver.get_B(), D_adj=solver.get_B_adj(), b=solver.get_b(), x0=solver.get_x0(), alpha=solver.get_alpha(), x_scale=solver.get_x_scale(), data_loss=solver.get_data_loss(), data_loss_scale=solver.get_data_loss_scale(), iter_max=solver.get_iter_max(), minimizer=solver.get_minimizer(), iterations=args.iterations, measures=args.measures, dimension=3, L2=16. / reconstruction_space.sitk.GetSpacing()[0]**2, reconstruction_type=args.reconstruction_type, rho=args.rho, dir_output=args.dir_output, parameters=parameters, name=name, reconstruction_info=reconstruction_info, x_ref=x_ref, x_ref_mask=x_ref_mask, tv_solver=args.tv_solver, verbose=args.verbose, append=args.append, ) parameter_study_interface.set_up_parameter_study() parameter_study = parameter_study_interface.get_parameter_study() # Run parameter study parameter_study.run() print("\nComputational time for Deconvolution Parameter Study %s: %s" % (name, parameter_study.get_computational_time())) return 0
def main(): time_start = ph.start_timing() # Set print options for numpy np.set_printoptions(precision=3) input_parser = InputArgparser( description="Propagate image mask using rigid registration.", ) input_parser.add_moving(required=True) input_parser.add_moving_mask(required=True) input_parser.add_fixed(required=True) input_parser.add_output(required=True) input_parser.add_v2v_method( option_string="--method", help="Registration method used for the registration (%s)." % (", or ".join(V2V_METHOD_OPTIONS)), default="RegAladin", ) input_parser.add_option( option_string="--use-moving-mask", type=int, help="Turn on/off use of moving mask to constrain the registration.", default=0, ) input_parser.add_dilation_radius(default=1) input_parser.add_verbose(default=0) input_parser.add_log_config(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]): raise ValueError( "output filename invalid; allowed extensions are: %s" % ", ".join(ALLOWED_EXTENSIONS)) if args.method not in V2V_METHOD_OPTIONS: raise ValueError("method must be in {%s}" % (", ".join(V2V_METHOD_OPTIONS))) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) stack = st.Stack.from_filename( file_path=args.fixed, extract_slices=False, ) template = st.Stack.from_filename( file_path=args.moving, file_path_mask=args.moving_mask, extract_slices=False, ) if args.method == "FLIRT": # Define search angle ranges for FLIRT in all three dimensions # search_angles = ["-searchr%s -%d %d" % # (x, args.search_angle, args.search_angle) # for x in ["x", "y", "z"]] # options = (" ").join(search_angles) # options += " -noresample" registration = regflirt.FLIRT( registration_type="Rigid", fixed=stack, moving=template, use_fixed_mask=False, use_moving_mask=args.use_moving_mask, # options=options, use_verbose=False, ) else: registration = niftyreg.RegAladin( registration_type="Rigid", fixed=stack, moving=template, use_fixed_mask=False, use_moving_mask=args.use_moving_mask, # options="-ln 2", use_verbose=False, ) try: registration.run() except RuntimeError as e: raise RuntimeError( "%s\n\n" "Have you tried running the script with '--use-moving-mask 0'?" % e) transform_sitk = registration.get_registration_transform_sitk() stack.sitk_mask = sitk.Resample(template.sitk_mask, stack.sitk_mask, transform_sitk, sitk.sitkNearestNeighbor, 0, template.sitk_mask.GetPixelIDValue()) if args.dilation_radius > 0: stack_mask_morpher = stmorph.StackMaskMorphologicalOperations.from_sitk_mask( mask_sitk=stack.sitk_mask, dilation_radius=args.dilation_radius, dilation_kernel="Ball", use_dilation_in_plane_only=True, ) stack_mask_morpher.run_dilation() stack.sitk_mask = stack_mask_morpher.get_processed_mask_sitk() dw.DataWriter.write_mask(stack.sitk_mask, args.output) elapsed_time = ph.stop_timing(time_start) if args.verbose: ph.show_nifti(args.fixed, segmentation=args.output) ph.print_title("Summary") exe_file_info = os.path.basename(os.path.abspath(__file__)).split(".")[0] print("%s | Computational Time for Segmentation Propagation: %s" % (exe_file_info, elapsed_time)) return 0