def main(): input_parser = InputArgparser( description="Create and write random motion transforms.", ) input_parser.add_dir_output(required=True) input_parser.add_option( option_string="--simulations", type=int, required=True) input_parser.add_option(option_string="--angle-max", default=10) input_parser.add_option(option_string="--translation-max", default=10) input_parser.add_option(option_string="--seed", type=int, default=1) input_parser.add_option(option_string="--dimension", type=int, default=3) input_parser.add_option( option_string="--write-settings", type=int, default=1) input_parser.add_prefix_output(default="Euler3DTransform_") input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) motion_simulator = ms.RandomRigidMotionSimulator( dimension=args.dimension, angle_max_deg=args.angle_max, translation_max=args.translation_max, verbose=args.verbose) motion_simulator.simulate_motion( seed=args.seed, simulations=args.simulations) prefix = "%sAngle%gTranslation%gSeed%d_" % ( args.prefix_output, args.angle_max, args.translation_max, args.seed) prefix = prefix.replace(".", "p") motion_simulator.write_transforms_sitk( directory=args.dir_output, prefix_filename=prefix) return 0
def main(): input_parser = InputArgparser( description="Create and write random rigid motion transformations. " "Simulated transformations are exported as (Simple)ITK transforms. ", ) input_parser.add_dir_output(required=True) input_parser.add_option( option_string="--simulations", type=int, required=True, help="Number of simulated motion transformations." ) input_parser.add_option( option_string="--angle-max", default=10, help="random angles (in degree) are drawn such " "that |angle| <= angle_max." ) input_parser.add_option( option_string="--translation-max", default=10, help="random translations (in millimetre) are drawn such " "that |translation| <= translation_max." ) input_parser.add_option( option_string="--seed", type=int, default=1, help="Seed for pseudo-random data generation" ) input_parser.add_option( option_string="--dimension", type=int, default=3, help="Spatial dimension for transformations." ) input_parser.add_prefix_output(default="EulerTransform_slice") input_parser.add_verbose(default=1) args = input_parser.parse_args() input_parser.print_arguments(args) motion_simulator = ms.RandomRigidMotionSimulator( dimension=args.dimension, angle_max_deg=args.angle_max, translation_max=args.translation_max, verbose=args.verbose) motion_simulator.simulate_motion( seed=args.seed, simulations=args.simulations, ) motion_simulator.write_transforms_sitk( directory=args.dir_output, prefix_filename=args.prefix_output, ) 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 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(): # Set print options np.set_printoptions(precision=3) pd.set_option('display.width', 1000) input_parser = InputArgparser(description=".", ) input_parser.add_filenames(required=True) input_parser.add_reference(required=True) input_parser.add_reference_mask() input_parser.add_dir_output(required=False) input_parser.add_measures( default=["PSNR", "RMSE", "MAE", "SSIM", "NCC", "NMI"]) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) ph.print_title("Image similarity") data_reader = dr.MultipleImagesReader(args.filenames) data_reader.read_data() stacks = data_reader.get_data() reference = st.Stack.from_filename(args.reference, args.reference_mask) for stack in stacks: try: stack.sitk - reference.sitk except RuntimeError as e: raise IOError( "All provided images must be at the same image space") x_ref = sitk.GetArrayFromImage(reference.sitk) if args.reference_mask is None: indices = np.where(x_ref != np.inf) else: x_ref_mask = sitk.GetArrayFromImage(reference.sitk_mask) indices = np.where(x_ref_mask > 0) measures_dic = { m: lambda x, m=m: SimilarityMeasures.similarity_measures[m] (x[indices], x_ref[indices]) # SimilarityMeasures.similarity_measures[m](x, x_ref) for m in args.measures } observer = obs.Observer() observer.set_measures(measures_dic) for stack in stacks: nda = sitk.GetArrayFromImage(stack.sitk) observer.add_x(nda) if args.verbose: stacks_comparison = [s for s in stacks] stacks_comparison.insert(0, reference) sitkh.show_stacks( stacks_comparison, segmentation=reference, ) observer.compute_measures() measures = observer.get_measures() # Store information in array error = np.zeros((len(stacks), len(measures))) cols = measures rows = [] for i_stack, stack in enumerate(stacks): error[i_stack, :] = np.array([measures[m][i_stack] for m in measures]) rows.append(stack.get_filename()) header = "# Ref: %s, Ref-Mask: %d, %s \n" % ( reference.get_filename(), args.reference_mask is None, ph.get_time_stamp(), ) header += "# %s\n" % ("\t").join(measures) path_to_file_filenames = os.path.join(args.dir_output, "filenames.txt") path_to_file_similarities = os.path.join(args.dir_output, "similarities.txt") # Write to files ph.write_to_file(path_to_file_similarities, header) ph.write_array_to_file(path_to_file_similarities, error, verbose=False) text = header text += "%s\n" % "\n".join(rows) ph.write_to_file(path_to_file_filenames, text) # Print to screen ph.print_subtitle("Computed Similarities") df = pd.DataFrame(error, rows, cols) print(df) return 0
def main(): input_parser = InputArgparser( description="Script to export a side-by-side comparison of originally " "acquired and simulated/projected slice given the estimated " "volumetric reconstruction." "This function takes the result of " "simulate_stacks_from_reconstruction.py as input.", ) input_parser.add_filenames(required=True) input_parser.add_dir_output(required=True) input_parser.add_option( option_string="--prefix-simulated", type=str, help="Specify the prefix of the simulated stacks to distinguish them " "from the original data.", default="Simulated_", ) input_parser.add_option( option_string="--dir-input-simulated", type=str, help="Specify the directory where the simulated stacks are. " "If not given, it is assumed that they are in the same directory " "as the original ones.", default=None) input_parser.add_option( option_string="--resize", type=float, help="Factor to resize images (otherwise they might be very small " "depending on the FOV)", default=3) args = input_parser.parse_args() input_parser.print_arguments(args) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") # Read original data filenames_original = args.filenames data_reader = dr.MultipleImagesReader(filenames_original) data_reader.read_data() stacks_original = data_reader.get_data() # Read data simulated from obtained reconstruction if args.dir_input_simulated is None: dir_input_simulated = os.path.dirname(filenames_original[0]) else: dir_input_simulated = args.dir_input_simulated filenames_simulated = [ os.path.join("%s", "%s%s") % (dir_input_simulated, args.prefix_simulated, os.path.basename(f)) for f in filenames_original ] data_reader = dr.MultipleImagesReader(filenames_simulated) data_reader.read_data() stacks_simulated = data_reader.get_data() ph.create_directory(args.dir_output) for i in range(len(stacks_original)): try: stacks_original[i].sitk - stacks_simulated[i].sitk except: raise IOError( "Images '%s' and '%s' do not occupy the same space!" % (filenames_original[i], filenames_simulated[i])) # ---------------------Create side-by-side comparisons--------------------- ph.print_title("Create side-by-side comparisons") intensity_max = 255 intensity_min = 0 for i in range(len(stacks_original)): ph.print_subtitle("Stack %d/%d" % (i + 1, len(stacks_original))) nda_3D_original = sitk.GetArrayFromImage(stacks_original[i].sitk) nda_3D_simulated = sitk.GetArrayFromImage(stacks_simulated[i].sitk) # Scale uniformly between 0 and 255 according to the simulated stack # for export to png scale = np.max(nda_3D_simulated) nda_3D_original = intensity_max * nda_3D_original / scale nda_3D_simulated = intensity_max * nda_3D_simulated / scale nda_3D_simulated = np.clip(nda_3D_simulated, intensity_min, intensity_max) nda_3D_original = np.clip(nda_3D_original, intensity_min, intensity_max) filename = stacks_original[i].get_filename() path_to_file = os.path.join(args.dir_output, "%s.pdf" % filename) # Export side-by-side comparison of each stack to a pdf file export_comparison_to_file(nda_3D_original, nda_3D_simulated, path_to_file, resize=args.resize)
def main(): input_parser = InputArgparser(description="Convert NIfTI to DICOM image", ) input_parser.add_filename(required=True) input_parser.add_option( option_string="--template", type=str, required=True, help="Template DICOM to extract relevant DICOM tags.", ) input_parser.add_dir_output(required=True) input_parser.add_label( help="Label used for series description of DICOM output.", default="SRR_NiftyMIC") input_parser.add_argument( "--volume", "-volume", action='store_true', help="If given, the output DICOM file is combined as 3D volume") args = input_parser.parse_args() input_parser.print_arguments(args) # Prepare for final DICOM output ph.create_directory(args.dir_output) if args.volume: dir_output_2d_slices = os.path.join(DIR_TMP, "dicom_slices") else: dir_output_2d_slices = os.path.join(args.dir_output, args.label) ph.create_directory(dir_output_2d_slices, delete_files=True) # read NiftyMIC version (if available) data_reader = dr.ImageHeaderReader(args.filename) data_reader.read_data() niftymic_version = data_reader.get_niftymic_version() if niftymic_version is None: niftymic_version = "NiftyMIC" else: niftymic_version = "NiftyMIC-v%s" % niftymic_version # Create set of 2D DICOM slices from 3D NIfTI image # (correct image orientation!) ph.print_title("Create set of 2D DICOM slices from 3D NIfTI image") cmd_args = ["nifti2dicom"] cmd_args.append("-i '%s'" % args.filename) cmd_args.append("-o '%s'" % dir_output_2d_slices) cmd_args.append("-d '%s'" % args.template) cmd_args.append("--prefix ''") cmd_args.append("--seriesdescription '%s'" % args.label) cmd_args.append("--accessionnumber '%s'" % ACCESSION_NUMBER) cmd_args.append("--seriesnumber '%s'" % SERIES_NUMBER) cmd_args.append("--institutionname '%s'" % IMAGE_COMMENTS) # Overwrite default "nifti2dicom" tags which would be added otherwise # (no deletion/update with empty '' sufficient to overwrite them) cmd_args.append("--manufacturersmodelname '%s'" % "NiftyMIC") cmd_args.append("--protocolname '%s'" % niftymic_version) cmd_args.append("-y") ph.execute_command(" ".join(cmd_args)) if args.volume: path_to_output = os.path.join(args.dir_output, "%s.dcm" % args.label) # Combine set of 2D DICOM slices to form 3D DICOM image # (image orientation stays correct) ph.print_title("Combine set of 2D DICOM slices to form 3D DICOM image") cmd_args = ["medcon"] cmd_args.append("-f '%s'/*.dcm" % dir_output_2d_slices) cmd_args.append("-o '%s'" % path_to_output) cmd_args.append("-c dicom") cmd_args.append("-stack3d") cmd_args.append("-n") cmd_args.append("-qc") cmd_args.append("-w") ph.execute_command(" ".join(cmd_args)) # Update all relevant DICOM tags accordingly ph.print_title("Update all relevant DICOM tags accordingly") print("") dataset_template = pydicom.dcmread(args.template) dataset = pydicom.dcmread(path_to_output) # Copy tags from template (to guarantee grouping with original data) update_dicom_tags = {} for tag in COPY_DICOM_TAGS: try: update_dicom_tags[tag] = getattr(dataset_template, tag) except: update_dicom_tags[tag] = "" # Additional tags update_dicom_tags["SeriesDescription"] = args.label update_dicom_tags["InstitutionName"] = institution_name update_dicom_tags["ImageComments"] = IMAGE_COMMENTS update_dicom_tags["AccessionNumber"] = ACCESSION_NUMBER update_dicom_tags["SeriesNumber"] = SERIES_NUMBER for tag in sorted(update_dicom_tags.keys()): value = update_dicom_tags[tag] setattr(dataset, tag, value) ph.print_info("%s: '%s'" % (tag, value)) dataset.save_as(path_to_output) print("") ph.print_info("3D DICOM image written to '%s'" % path_to_output) else: ph.print_info("DICOM images written to '%s'" % dir_output_2d_slices) return 0
def main(): input_parser = InputArgparser( description="Script to show the evaluated similarity between " "simulated stack from obtained reconstruction and original stack. " "This function takes the result of " "evaluate_simulated_stack_similarity.py as input. " "Provide --dir-output in order to save the results." ) input_parser.add_dir_input(required=True) input_parser.add_dir_output(required=False) args = input_parser.parse_args() input_parser.print_arguments(args) if not ph.directory_exists(args.dir_input): raise exceptions.DirectoryNotExistent(args.dir_input) # --------------------------------Read Data-------------------------------- pattern = "Similarity_(" + REGEX_FILENAMES + ")[.]txt" p = re.compile(pattern) dic_filenames = { p.match(f).group(1): p.match(f).group(0) for f in os.listdir(args.dir_input) if p.match(f) } dic_stacks = {} for filename in dic_filenames.keys(): path_to_file = os.path.join(args.dir_input, dic_filenames[filename]) # Extract evaluated measures written as header in second line measures = open(path_to_file).readlines()[1] measures = re.sub("#\t", "", measures) measures = re.sub("\n", "", measures) measures = measures.split("\t") # Extract errors similarities = np.loadtxt(path_to_file, skiprows=2) # Build dictionary holding all similarity information for stack dic_stack_similarity = { measures[i]: similarities[:, i] for i in range(len(measures)) } # dic_stack_similarity["measures"] = measures # Store information of to dictionary dic_stacks[filename] = dic_stack_similarity # -----------Visualize stacks individually per similarity measure---------- ctr = [0] N_stacks = len(dic_stacks) N_measures = len(measures) rows = 2 if N_measures < 6 else 3 filenames = natsorted(dic_stacks.keys(), key=lambda y: y.lower()) for i, filename in enumerate(filenames): fig = plt.figure(ph.add_one(ctr)) fig.clf() for m, measure in enumerate(measures): ax = plt.subplot(rows, np.ceil(N_measures/float(rows)), m+1) y = dic_stacks[filename][measure] x = range(1, y.size+1) lines = plt.plot(x, y) line = lines[0] line.set_linestyle("") line.set_marker(ph.MARKERS[0]) # line.set_markerfacecolor("w") plt.xlabel("Slice") plt.ylabel(measure) ax.set_xticks(x) if measure in ["SSIM", "NCC"]: ax.set_ylim([0, 1]) plt.suptitle(filename) try: # Open windows (and also save them) in full screen manager = plt.get_current_fig_manager() manager.full_screen_toggle() except: pass plt.show(block=False) if args.dir_output is not None: filename = "Similarity_%s.pdf" % filename ph.save_fig(fig, args.dir_output, filename) # -----------All in one (meaningful in case of similar scaling)---------- fig = plt.figure(ph.add_one(ctr)) fig.clf() data = {} for m, measure in enumerate(measures): for i, filename in enumerate(filenames): similarities = dic_stacks[filename][measure] labels = [filename] * similarities.size if m == 0: if "Stack" not in data.keys(): data["Stack"] = labels else: data["Stack"] = np.concatenate((data["Stack"], labels)) if measure not in data.keys(): data[measure] = similarities else: data[measure] = np.concatenate( (data[measure], similarities)) df_melt = pd.DataFrame(data).melt( id_vars="Stack", var_name="", value_name=" ", value_vars=measures, ) ax = plt.subplot(1, 1, 1) b = sns.boxplot( data=df_melt, hue="Stack", # different colors for different "Stack" x="", y=" ", order=measures, ) ax.set_axisbelow(True) try: # Open windows (and also save them) in full screen manager = plt.get_current_fig_manager() manager.full_screen_toggle() except: pass plt.show(block=False) if args.dir_output is not None: filename = "Boxplot.pdf" ph.save_fig(fig, args.dir_output, filename) # # -------------Boxplot: Plot individual similarity measures v1---------- # for m, measure in enumerate(measures): # fig = plt.figure(ph.add_one(ctr)) # fig.clf() # data = {} # for i, filename in enumerate(filenames): # similarities = dic_stacks[filename][measure] # labels = [filename] * similarities.size # if "Stack" not in data.keys(): # data["Stack"] = labels # else: # data["Stack"] = np.concatenate((data["Stack"], labels)) # if measure not in data.keys(): # data[measure] = similarities # else: # data[measure] = np.concatenate( # (data[measure], similarities)) # df_melt = pd.DataFrame(data).melt( # id_vars="Stack", # var_name="", # value_name=measure, # ) # ax = plt.subplot(1, 1, 1) # b = sns.boxplot( # data=df_melt, # hue="Stack", # different colors for different "Stack" # x="", # y=measure, # ) # ax.set_axisbelow(True) # plt.show(block=False) # # -------------Boxplot: Plot individual similarity measures v2---------- # for m, measure in enumerate(measures): # fig = plt.figure(ph.add_one(ctr)) # fig.clf() # data = {} # for i, filename in enumerate(filenames): # similarities = dic_stacks[filename][measure] # labels = [filename] * len(filenames) # if filename not in data.keys(): # data[filename] = similarities # else: # data[filename] = np.concatenate( # (data[filename], similarities)) # for filename in filenames: # data[filename] = pd.Series(data[filename]) # df = pd.DataFrame(data) # df_melt = df.melt( # var_name="", # value_name=measure, # value_vars=filenames, # ) # ax = plt.subplot(1, 1, 1) # b = sns.boxplot( # data=df_melt, # x="", # y=measure, # order=filenames, # ) # ax.set_axisbelow(True) # plt.show(block=False) 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() 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, nargs="+", help="Specify moving image to be warped to fixed space. " "If multiple images are provided, all images will be transformed " "uniformly according to the registration obtained for the first one.") input_parser.add_dir_output(required=True) input_parser.add_dir_input() input_parser.add_suffix_mask(default="_mask") input_parser.add_search_angle(default=180) input_parser.add_option( option_string="--transform-only", type=int, help="Turn on/off functionality to transform moving image(s) to fixed " "image only, i.e. no resampling to fixed image space", default=0) input_parser.add_option( option_string="--write-transform", type=int, help="Turn on/off functionality to write registration transform", default=0) input_parser.add_verbose(default=0) args = input_parser.parse_args() input_parser.print_arguments(args) use_reg_aladin_for_refinement = True # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") data_reader = dr.MultipleImagesReader(args.moving, suffix_mask="_mask") data_reader.read_data() moving = data_reader.get_data() data_reader = dr.MultipleImagesReader([args.fixed], suffix_mask="_mask") data_reader.read_data() fixed = data_reader.get_data()[0] # -------------------Register Reconstruction to Template------------------- ph.print_title("Register Reconstruction to Template") # 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) options_args = [] options_args.append(search_angles) # cost = "mutualinfo" # options_args.append("-searchcost %s -cost %s" % (cost, cost)) registration = regflirt.FLIRT( fixed=moving[0], moving=fixed, # use_fixed_mask=True, # use_moving_mask=True, # moving mask only seems to work for SB cases registration_type="Rigid", use_verbose=False, options=(" ").join(options_args), ) ph.print_info("Run Registration (FLIRT) ... ", newline=False) registration.run() print("done") transform_sitk = registration.get_registration_transform_sitk() if args.write_transform: path_to_transform = os.path.join(args.dir_output, "registration_transform_sitk.txt") sitk.WriteTransform(transform_sitk, path_to_transform) # Apply rigidly transform to align reconstruction (moving) with template # (fixed) for m in moving: m.update_motion_correction(transform_sitk) # 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 use_reg_aladin_for_refinement: registration = niftyreg.RegAladin( fixed=m, use_fixed_mask=True, moving=fixed, registration_type="Rigid", use_verbose=False, ) ph.print_info("Run Registration (RegAladin) ... ", newline=False) registration.run() print("done") transform2_sitk = registration.get_registration_transform_sitk() m.update_motion_correction(transform2_sitk) transform_sitk = sitkh.get_composite_sitk_affine_transform( transform2_sitk, transform_sitk) if args.transform_only: for m in moving: m.write(args.dir_output, write_mask=False) ph.exit() # Resample reconstruction (moving) to template space (fixed) warped_moving = [ m.get_resampled_stack(fixed.sitk, interpolator="Linear") for m in moving ] for wm in warped_moving: wm.set_filename(wm.get_filename() + "ResamplingToTemplateSpace") if args.verbose: sitkh.show_stacks([fixed, wm], segmentation=fixed) # Write resampled reconstruction (moving) wm.write(args.dir_output, write_mask=False) if args.dir_input is not None: data_reader = dr.ImageSlicesDirectoryReader( path_to_directory=args.dir_input, suffix_mask=args.suffix_mask) data_reader.read_data() stacks = data_reader.get_data() for i, stack in enumerate(stacks): stack.update_motion_correction(transform_sitk) ph.print_info("Stack %d/%d: All slice transforms updated" % (i + 1, len(stacks))) # Write transformed slices stack.write( os.path.join(args.dir_output, "motion_correction"), write_mask=True, write_slices=True, write_transforms=True, suffix_mask=args.suffix_mask, ) 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(): # Read input input_parser = InputArgparser( description="Script to evaluate the similarity of simulated stack " "from obtained reconstruction against the original stack. " "This function takes the result of " "simulate_stacks_from_reconstruction.py as input.", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks() input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_measures(default=["NCC", "SSIM"]) input_parser.add_option( option_string="--prefix-simulated", type=str, help="Specify the prefix of the simulated stacks to distinguish them " "from the original data.", default="Simulated_", ) input_parser.add_option( option_string="--dir-input-simulated", type=str, help="Specify the directory where the simulated stacks are. " "If not given, it is assumed that they are in the same directory " "as the original ones.", default=None) input_parser.add_slice_thicknesses(default=None) args = input_parser.parse_args() input_parser.print_arguments(args) # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") # Read original data filenames_original = args.filenames data_reader = dr.MultipleImagesReader( file_paths=filenames_original, file_paths_masks=args.filenames_masks, suffix_mask=args.suffix_mask, stacks_slice_thicknesses=args.slice_thicknesses, ) data_reader.read_data() stacks_original = data_reader.get_data() # Read data simulated from obtained reconstruction if args.dir_input_simulated is None: dir_input_simulated = os.path.dirname(filenames_original[0]) else: dir_input_simulated = args.dir_input_simulated filenames_simulated = [ os.path.join("%s", "%s%s") % (dir_input_simulated, args.prefix_simulated, os.path.basename(f)) for f in filenames_original ] data_reader = dr.MultipleImagesReader(filenames_simulated, suffix_mask=args.suffix_mask) data_reader.read_data() stacks_simulated = data_reader.get_data() for i in range(len(stacks_original)): try: stacks_original[i].sitk - stacks_simulated[i].sitk except: raise IOError( "Images '%s' and '%s' do not occupy the same space!" % (filenames_original[i], filenames_simulated[i])) similarity_measures = { m: SimilarityMeasures.similarity_measures[m] for m in args.measures } similarities = np.zeros(len(args.measures)) for i in range(len(stacks_original)): nda_3D_original = sitk.GetArrayFromImage(stacks_original[i].sitk) nda_3D_simulated = sitk.GetArrayFromImage(stacks_simulated[i].sitk) nda_3D_mask = sitk.GetArrayFromImage(stacks_original[i].sitk_mask) path_to_file = os.path.join( args.dir_output, "Similarity_%s.txt" % stacks_original[i].get_filename()) text = "# Similarity: %s vs %s (%s)." % (os.path.basename( filenames_original[i]), os.path.basename( filenames_simulated[i]), ph.get_time_stamp()) text += "\n#\t" + ("\t").join(args.measures) text += "\n" ph.write_to_file(path_to_file, text, "w") for k in range(nda_3D_original.shape[0]): x_2D_original = nda_3D_original[k, :, :] x_2D_simulated = nda_3D_simulated[k, :, :] # zero slice, i.e. rejected during motion correction if np.abs(x_2D_simulated).sum() < 1e-6: x_2D_simulated[:] = np.nan x_2D_mask = nda_3D_mask[k, :, :] indices = np.where(x_2D_mask > 0) for m, measure in enumerate(args.measures): if len(indices[0]) > 0: similarities[m] = similarity_measures[measure]( x_2D_original[indices], x_2D_simulated[indices]) else: similarities[m] = np.nan ph.write_array_to_file(path_to_file, similarities.reshape(1, -1)) return 0
def main(): time_start = ph.start_timing() # Set print options np.set_printoptions(precision=3) pd.set_option('display.width', 1000) input_parser = InputArgparser(description=".", ) input_parser.add_filenames() input_parser.add_filenames_masks() input_parser.add_dir_input_mc() input_parser.add_suffix_mask(default="_mask") input_parser.add_reference(required=True) input_parser.add_reference_mask() input_parser.add_dir_output(required=False) input_parser.add_log_config(default=1) input_parser.add_measures(default=["PSNR", "RMSE", "SSIM", "NCC", "NMI"]) input_parser.add_verbose(default=0) input_parser.add_slice_thicknesses(default=None) input_parser.add_option(option_string="--use-reference-mask", type=int, default=1) input_parser.add_option(option_string="--use-slice-masks", type=int, default=1) args = input_parser.parse_args() input_parser.print_arguments(args) 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)) reference = st.Stack.from_filename(args.reference, args.reference_mask) ph.print_title("Slice Residual Similarity") residual_evaluator = res_ev.ResidualEvaluator( stacks=stacks, reference=reference, measures=args.measures, use_reference_mask=args.use_reference_mask, use_slice_masks=args.use_slice_masks, ) residual_evaluator.compute_slice_projections() residual_evaluator.evaluate_slice_similarities() residual_evaluator.write_slice_similarities(args.dir_output) elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Slice Residual Evaluation: %s" % (elapsed_time)) return 0
def main(): time_start = ph.start_timing() # Set print options np.set_printoptions(precision=3) pd.set_option('display.width', 1000) input_parser = InputArgparser( description=".", ) input_parser.add_filenames() input_parser.add_filenames_masks() input_parser.add_dir_input_mc() input_parser.add_suffix_mask(default="_mask") input_parser.add_reference(required=True) input_parser.add_reference_mask() input_parser.add_dir_output(required=False) input_parser.add_log_config(default=1) input_parser.add_measures( default=["PSNR", "MAE", "RMSE", "SSIM", "NCC", "NMI"]) input_parser.add_verbose(default=0) input_parser.add_target_stack(default=None) input_parser.add_intensity_correction(default=1) input_parser.add_slice_thicknesses(default=None) input_parser.add_option( option_string="--use-reference-mask", type=int, default=1) input_parser.add_option( option_string="--use-slice-masks", type=int, default=1) args = input_parser.parse_args() input_parser.print_arguments(args) 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)) # Specify target stack for intensity correction and reconstruction space if args.target_stack is None: target_stack_index = 0 else: filenames = ["%s.nii.gz" % s.get_filename() for s in stacks] filename_target_stack = os.path.basename(args.target_stack) try: target_stack_index = filenames.index(filename_target_stack) except ValueError as e: raise ValueError( "--target-stack must correspond to an image as provided by " "--filenames") # ---------------------------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_stack_mask(True) intensity_corrector.use_reference_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()) # ----------------------- Slice Residual Similarity ----------------------- reference = st.Stack.from_filename(args.reference, args.reference_mask) ph.print_title("Slice Residual Similarity") residual_evaluator = res_ev.ResidualEvaluator( stacks=stacks, reference=reference, measures=args.measures, use_reference_mask=args.use_reference_mask, use_slice_masks=args.use_slice_masks, ) residual_evaluator.compute_slice_projections() residual_evaluator.evaluate_slice_similarities() residual_evaluator.write_slice_similarities(args.dir_output) elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Slice Residual Evaluation: %s" % (elapsed_time)) return 0
def main(): input_parser = InputArgparser( description="Simulate stacks from obtained reconstruction. " "Script simulates/projects the slices at estimated positions " "within reconstructed volume. Ideally, if motion correction was " "correct, the resulting stack of such obtained projected slices, " "corresponds to the originally acquired (motion corrupted) data.", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks() input_parser.add_dir_input_mc(required=True) input_parser.add_reconstruction(required=True) input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_prefix_output(default="Simulated_") input_parser.add_option( option_string="--copy-data", type=int, help="Turn on/off copying of original data (including masks) to " "output folder.", default=0) input_parser.add_option( option_string="--reconstruction-mask", type=str, help="If given, reconstruction image mask is propagated to " "simulated stack(s) of slices as well", default=None) input_parser.add_interpolator( option_string="--interpolator-mask", help="Choose the interpolator type to propagate the reconstruction " "mask (%s)." % (INTERPOLATOR_TYPES), default="NearestNeighbor") input_parser.add_log_config(default=0) input_parser.add_verbose(default=0) input_parser.add_slice_thicknesses(default=None) args = input_parser.parse_args() input_parser.print_arguments(args) if args.interpolator_mask not in ALLOWED_INTERPOLATORS: raise IOError( "Unknown interpolator provided. Possible choices are %s" % ( INTERPOLATOR_TYPES)) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) # Read motion corrected 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() reconstruction = st.Stack.from_filename( args.reconstruction, args.reconstruction_mask, extract_slices=False) linear_operators = lin_op.LinearOperators() for i, stack in enumerate(stacks): # initialize image data array(s) nda = np.zeros_like(sitk.GetArrayFromImage(stack.sitk)) nda[:] = np.nan if args.reconstruction_mask: nda_mask = np.zeros_like(sitk.GetArrayFromImage(stack.sitk_mask)) slices = stack.get_slices() kept_indices = [s.get_slice_number() for s in slices] # Fill stack information "as if slice was acquired consecutively" # Therefore, simulated stack slices correspond to acquired slices # (in case motion correction was correct) for j in range(nda.shape[0]): if j in kept_indices: index = kept_indices.index(j) simulated_slice = linear_operators.A( reconstruction, slices[index], interpolator_mask=args.interpolator_mask ) nda[j, :, :] = sitk.GetArrayFromImage(simulated_slice.sitk) if args.reconstruction_mask: nda_mask[j, :, :] = sitk.GetArrayFromImage( simulated_slice.sitk_mask) # Create nifti image with same image header as original stack simulated_stack_sitk = sitk.GetImageFromArray(nda) simulated_stack_sitk.CopyInformation(stack.sitk) if args.reconstruction_mask: simulated_stack_sitk_mask = sitk.GetImageFromArray(nda_mask) simulated_stack_sitk_mask.CopyInformation(stack.sitk_mask) else: simulated_stack_sitk_mask = None simulated_stack = st.Stack.from_sitk_image( image_sitk=simulated_stack_sitk, image_sitk_mask=simulated_stack_sitk_mask, filename=args.prefix_output + stack.get_filename(), extract_slices=False, slice_thickness=stack.get_slice_thickness(), ) if args.verbose: sitkh.show_stacks([ stack, simulated_stack], segmentation=stack) simulated_stack.write( args.dir_output, write_mask=False, write_slices=False, suffix_mask=args.suffix_mask) if args.copy_data: stack.write( args.dir_output, write_mask=True, write_slices=False, suffix_mask="_mask") 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 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 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="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() # 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() # 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() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Run reconstruction pipeline including " "(i) preprocessing (bias field correction + intensity correction), " "(ii) volumetric reconstruction in subject space, " "and (iii) volumetric reconstruction in template space.", ) input_parser.add_dir_input(required=True) input_parser.add_dir_mask(required=True) input_parser.add_dir_output(required=True) input_parser.add_suffix_mask(default="_mask") input_parser.add_target_stack(required=False) 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_script_execution(default=1) input_parser.add_dir_input_templates(default=DIR_TEMPLATES) 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-recon-subject-space", type=int, help="Turn on/off reconstruction in subject space", default=1) input_parser.add_option( option_string="--run-recon-template-space", type=int, help="Turn on/off reconstruction in template space", 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", default=1) input_parser.add_outlier_rejection(default=0) input_parser.add_use_robust_registration(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__)) 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") # if args.run_recon_template_space and args.gestational_age is None: # raise IOError("Gestational age must be set in order to pick the " # "right template") # get input stack names files = os.listdir(args.dir_input) input_files = [] mask_files = [] for file in files: if (".nii" in file): input_files.append("{0:}/{1:}".format(args.dir_input, file)) file_prefix = file[:-7] if (".nii.gz" in file) else file[:-4] mask_name = "{0:}/{1:}.nii.gz".format(args.dir_mask, file_prefix) if(not os.path.isfile(mask_name)): mask_name = "{0:}/{1:}.nii".format(args.dir_mask, file_prefix) assert(os.path.isfile(mask_name)) mask_files.append(mask_name) if args.target_stack is None: target_stack = input_files[0] else: target_stack = input_files if args.run_recon_subject_space: target_stack_index = input_files.index(target_stack) cmd_args = [] cmd_args.append("--filenames %s" % (" ").join(input_files)) cmd_args.append("--filenames-masks %s" % (" ").join(mask_files)) cmd_args.append("--multiresolution %d" % args.multiresolution) cmd_args.append("--target-stack-index %d" % target_stack_index) cmd_args.append("--dir-output %s" % dir_output_recon_subject_space) # 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("--use-robust-registration %d" % args.use_robust_registration) 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) 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") elapsed_time_volrec = ph.stop_timing(time_start_volrec) else: elapsed_time_volrec = ph.get_zero_time() if args.run_recon_template_space: # register recon to template space pattern = "[a-zA-Z0-9_]+(stacks)[a-zA-Z0-9_]+(.nii.gz)" p = re.compile(pattern) reconstruction = [ os.path.join( dir_output_recon_subject_space, p.match(f).group(0)) for f in os.listdir(dir_output_recon_subject_space) if p.match(f)][0] if('mask_manual' in args.dir_output): # find the corresponding template by volume matching reconstruction_mask = reconstruction if(not ("_mask" in reconstruction)): reconstruction_mask = ph.append_to_filename(reconstruction, "_mask") template_stack_estimator = \ tse.TemplateStackEstimator.from_mask( reconstruction_mask, args.dir_input_templates) template_mask = template_stack_estimator.get_path_to_template() template = template_mask.replace('_mask_dil.nii.gz', '.nii.gz') print('template name', template) # template = os.path.join( # args.dir_input_templates, # "STA%d.nii.gz" % args.gestational_age) # template_mask = os.path.join( # args.dir_input_templates, # "STA%d_mask.nii.gz" % args.gestational_age) else: template_folder = args.dir_output + "/../../mask_manual/reconstruct_outlier_gpr/" file_names = os.listdir(template_folder) template_names = [item for item in file_names if ("nii.gz" in item) and ("Masked" not in item)] mask_names = [item for item in file_names if ("nii.gz" in item) and ("Masked" in item)] template = os.path.join(template_folder, template_names[0]) template_mask = os.path.join(template_folder, mask_names[0]) cmd_args = [] cmd_args.append("--moving %s" % reconstruction) cmd_args.append("--fixed %s" % template) # if(use_spatiotemporal_template is False): # cmd_args.append("--use-fixed-mask 1") # added by Guotai # cmd_args.append("--template-mask %s" % template_mask) # micheal's code cmd_args.append("--dir-input %s" % os.path.join( dir_output_recon_subject_space, "motion_correction")) cmd_args.append("--dir-output %s" % dir_output_recon_template_space) cmd_args.append("--suffix-mask %s" % args.suffix_mask) cmd_args.append("--verbose %s" % args.verbose) 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 = os.path.join( dir_output_recon_template_space, "motion_correction") cmd_args = [] cmd_args.append("--dir-input %s" % dir_input) cmd_args.append("--dir-output %s" % dir_output_recon_template_space) 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 = "niftymic_reconstruct_volume_from_slices %s" % \ (" ").join(cmd_args) exit_code = ph.execute_command(cmd) if exit_code != 0: raise RuntimeError("Reconstruction in template space failed") pattern = "[a-zA-Z0-9_.]+(stacks[0-9]+).*(.nii.gz)" p = re.compile(pattern) reconstruction = { p.match(f).group(1): 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) and not p.match(f).group(0).endswith( "ResamplingToTemplateSpace.nii.gz")} key = reconstruction.keys()[0] path_to_recon = reconstruction[key] # Copy SRR to output directory output = "%sSRR_%s_GW%d.nii.gz" % ( args.prefix_output, key, args.gestational_age) path_to_output = os.path.join(args.dir_output, output) cmd = "cp -p %s %s" % (path_to_recon, 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 = [] cmd_args.append("--filename %s" % path_to_output) cmd_args.append("--gestational-age %s" % args.gestational_age) cmd_args.append("--verbose %s" % args.verbose) cmd_args.append("--dir-input-templates %s " % args.dir_input_templates) cmd = "niftymic_multiply_stack_with_mask %s" % ( " ").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 = os.path.join( dir_output_recon_template_space, "motion_correction") pattern = "[a-zA-Z0-9_.]+(stacks[0-9]+).*(.nii.gz)" # pattern = "Masked_[a-zA-Z0-9_.]+(stacks[0-9]+).*(.nii.gz)" p = re.compile(pattern) reconstruction = { p.match(f).group(1): 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) and not p.match(f).group(0).endswith( "ResamplingToTemplateSpace.nii.gz")} key = reconstruction.keys()[0] path_to_recon = reconstruction[key] # Get simulated/projected slices cmd_args = [] cmd_args.append("--dir-input %s" % dir_input) cmd_args.append("--dir-output %s" % dir_output_data_vs_simulatd_data) cmd_args.append("--reconstruction %s" % path_to_recon) 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 input_files] 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)) 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 Volumetric Reconstruction: %s" % elapsed_time_volrec) print("Computational Time for Pipeline: %s" % ph.stop_timing(time_start)) return 0