def from_filename(cls, file_path, slice_number, slice_thickness, file_path_mask=None, verbose=False, ): slice = cls() if not ph.file_exists(file_path): raise exceptions.FileNotExistent(file_path) slice._dir_input = os.path.dirname(file_path) slice._filename = os.path.basename(file_path).split(".")[0] slice._slice_number = slice_number slice._slice_thickness = slice_thickness # Append stacks as SimpleITK and ITK Image objects slice.sitk = sitkh.read_nifti_image_sitk(file_path, sitk.sitkFloat64) slice.itk = sitkh.get_itk_from_sitk_image(slice.sitk) # Append masks (if provided) if file_path_mask is None: slice.sitk_mask = slice._generate_identity_mask() if verbose: ph.print_info( "Identity mask created for '%s'." % (file_path)) else: if not ph.file_exists(file_path_mask): raise exceptions.FileNotExistent(file_path_mask) slice.sitk_mask = sitkh.read_nifti_image_sitk( file_path_mask, sitk.sitkUInt8) try: # ensure mask occupies the same physical space slice.sitk_mask.CopyInformation(slice.sitk) except RuntimeError as e: raise IOError( "Given image and its mask do not occupy the same space: %s" % e.message) slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask) # Store current affine transform of image slice._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image( slice.sitk) # Prepare history of affine transforms, i.e. encoded spatial # position+orientation of slice, and motion estimates of slice # obtained in the course of the registration/reconstruction process slice._history_affine_transforms = [] slice._history_affine_transforms.append(slice._affine_transform_sitk) slice._history_motion_corrections = [] slice._history_motion_corrections.append(sitk.Euler3DTransform()) return slice
def read_transform(path_to_file, inverse=0, nii_as_nib=0, as_itk=0): if not ph.file_exists(path_to_file): raise IOError("Transform file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_TRANSFORMS and \ extension not in ALLOWED_TRANSFORMS_DISPLACEMENTS: raise IOError("Transform file extension must be of type " "%s (transformation) or %s (displacements)" % (", ".join(ALLOWED_TRANSFORMS), ", ".join(ALLOWED_TRANSFORMS_DISPLACEMENTS))) if extension in ALLOWED_TRANSFORMS: if as_itk: tranform_sitk = sitk.read_transform_itk(path_to_file, inverse=inverse) else: transform_sitk = sitkh.read_transform_sitk(path_to_file, inverse=inverse) else: # Used for sitk_to_nreg conversion only if nii_as_nib: displacement_sitk = nib.load(path_to_file) return displacement_sitk else: displacement_sitk = sitk.ReadImage(path_to_file, sitk.sitkVectorFloat64) transform_sitk = sitk.DisplacementFieldTransform( sitk.Image(displacement_sitk)) if inverse: # May throw RuntimeError transform_sitk = transform_sitk.GetInverse() return transform_sitk
def read_similarities(self, directory): if not ph.directory_exists(directory): raise IOError("Directory '%s' does not exist." % directory) # Get filename paths path_to_file_filenames, path_to_file_similarities = self._get_filename_paths( directory) for f in [path_to_file_filenames, path_to_file_similarities]: if not ph.file_exists(path_to_file_filenames): raise IOError("File '%s' does not exist" % f) lines = ph.read_file_line_by_line(path_to_file_filenames) # Get image filenames image_names = [re.sub("\n", "", f) for f in lines[2:]] # Get computed measures measures = lines[1] measures = re.sub("# ", "", measures) measures = re.sub("\n", "", measures) self._measures = measures.split("\t") # Get computed similarities similarities_nda = np.loadtxt(path_to_file_similarities, skiprows=2) # Reconstruct similarity dictionary self._similarities = {} self._similarities["filenames"] = image_names for i_m, m in enumerate(self._measures): self._similarities[m] = similarities_nda[:, i_m]
def read_transform_nreg(path_to_file): if not ph.file_exists(path_to_file): raise IOError("NiftyReg transform file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_TRANSFORMS and \ extension not in ALLOWED_TRANSFORMS_DISPLACEMENTS: raise IOError("NiftyReg transform file extension must be of type " "%s (reg_aladin) or %s (reg_f3d displacement)" % (", ".join(ALLOWED_TRANSFORMS), ", ".join(ALLOWED_TRANSFORMS_DISPLACEMENTS))) if extension in ALLOWED_TRANSFORMS: transform_nreg = np.loadtxt(path_to_file) else: transform_nreg = nib.load(path_to_file) # check that image is a NiftyReg displacement field header = transform_nreg.get_header() if int(header['intent_p1']) != 1 or \ int(header['intent_p2']) != 0 or \ int(header['intent_p3']) != 0 or \ int(header['intent_code']) != 1007: raise IOError("Provided image must represent a NiftyReg " "displacement field") return transform_nreg
def from_filename(cls, file_path, slice_number, file_path_mask=None, verbose=False): slice = cls() if not ph.file_exists(file_path): raise exceptions.FileNotExistent(file_path) slice._dir_input = os.path.dirname(file_path) slice._filename = os.path.basename(file_path).split(".")[0] slice._slice_number = slice_number # Append stacks as SimpleITK and ITK Image objects slice.sitk = sitk.ReadImage(file_path, sitk.sitkFloat64) slice.itk = sitkh.get_itk_from_sitk_image(slice.sitk) # Append masks (if provided) if file_path_mask is None: slice.sitk_mask = slice._generate_identity_mask() if verbose: ph.print_info("Identity mask created for '%s'." % (file_path)) else: if not ph.file_exists(file_path_mask): raise exceptions.FileNotExistent(file_path_mask) slice.sitk_mask = sitk.ReadImage(file_path_mask, sitk.sitkUInt8) slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask) # Store current affine transform of image slice._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image( slice.sitk) # Prepare history of affine transforms, i.e. encoded spatial # position+orientation of slice, and motion estimates of slice # obtained in the course of the registration/reconstruction process slice._history_affine_transforms = [] slice._history_affine_transforms.append(slice._affine_transform_sitk) slice._history_motion_corrections = [] slice._history_motion_corrections.append(sitk.Euler3DTransform()) return slice
def read_landmarks(path_to_file): if not ph.file_exists(path_to_file): raise IOError("Landmark file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_LANDMARKS: raise IOError("Landmark file extension must be of type %s " % ", or ".join(ALLOWED_LANDMARKS)) return np.loadtxt(path_to_file)
def read_transform_flirt(path_to_file): if not ph.file_exists(path_to_file): raise IOError("FLIRT transform file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_TRANSFORMS: raise IOError("FLIRT transform file extension must be of type %s" % ", or ".join(ALLOWED_TRANSFORMS)) return np.loadtxt(path_to_file)
def read_landmarks(path_to_file): if not ph.file_exists(path_to_file): raise IOError("Landmark file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_LANDMARKS: raise IOError("Landmark file extension must be of type %s " % ", or ".join(ALLOWED_LANDMARKS)) nda = np.loadtxt(path_to_file) if nda.shape[1] not in [2, 3]: raise IOError("Landmark array file must be of shape N x dim, " "with dim either 2 or 3.") return nda
def read_image(path_to_file, as_itk=0): if not ph.file_exists(path_to_file): raise IOError("Image file '%s' not found" % path_to_file) extension = ph.strip_filename_extension(path_to_file)[1] if extension not in ALLOWED_IMAGES: raise IOError("Image file extension must be of type %s " % ", or ".join(ALLOWED_IMAGES)) # Read as itk.Image object if as_itk: image = itk.imread(path_to_file) # Read as sitk.Image object else: image = sitk.ReadImage(path_to_file) return image
def main(): time_start = ph.start_timing() np.set_printoptions(precision=3) input_parser = InputArgparser( description="Perform automatic brain masking using " "fetal_brain_seg, part of the MONAIfbs package " "(https://github.com/gift-surg/MONAIfbs). ", ) input_parser.add_filenames(required=True) input_parser.add_filenames_masks(required=False) input_parser.add_dir_output(required=False) input_parser.add_verbose(default=0) input_parser.add_log_config(default=0) input_parser.add_option( option_string="--neuroimage-legacy-seg", type=int, required=False, default=0, help="If set to 1, use the legacy method for fetal brain segmentation " "i.e. the two-step approach proposed in Ebner, Wang et al " "NeuroImage (2020)" ) args = input_parser.parse_args() input_parser.print_arguments(args) if args.neuroimage_legacy_seg: try: DIR_FETAL_BRAIN_SEG = os.environ["FETAL_BRAIN_SEG"] except KeyError as e: raise RuntimeError( "Environment variable FETAL_BRAIN_SEG is not specified. " "Specify the root directory of fetal_brain_seg " "(https://github.com/gift-surg/fetal_brain_seg) " "using " "'export FETAL_BRAIN_SEG=path_to_fetal_brain_seg_dir' " "(in bashrc).") else: try: import monaifbs DIR_FETAL_BRAIN_SEG = os.path.dirname(monaifbs.__file__) except ImportError as e: raise RuntimeError( "monaifbs not correctly installed. " "Please check its installation running " "pip install -e MONAIfbs/ " ) print("Using executable from {}".format(DIR_FETAL_BRAIN_SEG)) if args.filenames_masks is None and args.dir_output is None: raise IOError("Either --filenames-masks or --dir-output must be set") if args.dir_output is not None: args.filenames_masks = [ os.path.join(args.dir_output, os.path.basename(f)) for f in args.filenames ] if len(args.filenames) != len(args.filenames_masks): raise IOError("Number of filenames and filenames-masks must match") if args.log_config: input_parser.log_config(os.path.abspath(__file__)) cd_fetal_brain_seg = "cd %s" % DIR_FETAL_BRAIN_SEG for f, m in zip(args.filenames, args.filenames_masks): if not ph.file_exists(f): raise IOError("File '%s' does not exist" % f) # use absolute path for input image f = os.path.abspath(f) # use absolute path for output image dir_output = os.path.dirname(m) if not os.path.isabs(dir_output): dir_output = os.path.realpath( os.path.join(os.getcwd(), dir_output)) m = os.path.join(dir_output, os.path.basename(m)) ph.create_directory(dir_output) # Change to root directory of fetal_brain_seg cmds = [cd_fetal_brain_seg] # Run masking independently (Takes longer but ensures that it does # not terminate because of provided 'non-brain images') cmd_args = ["python fetal_brain_seg.py"] cmd_args.append("--input_names '%s'" % f) cmd_args.append("--segment_output_names '%s'" % m) cmds.append(" ".join(cmd_args)) # Execute both steps cmd = " && ".join(cmds) flag = ph.execute_command(cmd) if flag != 0: ph.print_warning( "Error using fetal_brain_seg. \n" "Execute '%s' for further investigation" % cmd) ph.print_info("Fetal brain segmentation written to '%s'" % m) if args.verbose: ph.show_nifti(f, segmentation=m) elapsed_time_total = ph.stop_timing(time_start) ph.print_title("Summary") exe_file_info = os.path.basename(os.path.abspath(__file__)).split(".")[0] print("%s | Computational Time: %s" % (exe_file_info, elapsed_time_total)) return 0
def run(self): if not ph.directory_exists(self._dir_motion_correction): raise exceptions.DirectoryNotExistent(self._dir_motion_correction) abs_path_to_directory = os.path.abspath(self._dir_motion_correction) for i in range(len(self._stacks)): stack_name = self._stacks[i].get_filename() # update stack position path_to_stack_transform = os.path.join(abs_path_to_directory, "%s.tfm" % stack_name) if ph.file_exists(path_to_stack_transform): transform_stack_sitk = sitkh.read_transform_sitk( path_to_stack_transform) transform_stack_sitk_inv = sitkh.read_transform_sitk( path_to_stack_transform, inverse=True) self._stacks[i].update_motion_correction(transform_stack_sitk) ph.print_info("Stack '%s': Stack position updated" % stack_name) else: transform_stack_sitk_inv = sitk.Euler3DTransform() # update slice positions pattern_trafo_slices = stack_name + self._prefix_slice + \ "([0-9]+)[.]tfm" p = re.compile(pattern_trafo_slices) dic_slice_transforms = { int(p.match(f).group(1)): os.path.join(abs_path_to_directory, p.match(f).group(0)) for f in os.listdir(abs_path_to_directory) if p.match(f) } slices = self._stacks[i].get_slices() for i_slice in range(self._stacks[i].get_number_of_slices()): if i_slice in dic_slice_transforms.keys(): transform_slice_sitk = sitkh.read_transform_sitk( dic_slice_transforms[i_slice]) transform_slice_sitk = \ sitkh.get_composite_sitk_affine_transform( transform_slice_sitk, transform_stack_sitk_inv) slices[i_slice].update_motion_correction( transform_slice_sitk) # # ------------------------- HACK ------------------------- # # 18 Jan 2019 # # HACK to use results of a previous version where image # # slices were still exported # # (Bug was that after stack intensity correction, the # # previous v2v-reg was not passed on to the final # # registration transform): # import niftymic.base.slice as sl # path_to_slice = re.sub( # ".tfm", ".nii.gz", dic_slice_transforms[i_slice]) # path_to_slice_mask = re.sub( # ".tfm", "_mask.nii.gz", dic_slice_transforms[i_slice]) # slice_sitk = sitk.ReadImage(path_to_slice) # slice_sitk_mask = sitk.ReadImage(path_to_slice_mask) # hack = sl.Slice.from_sitk_image( # # slice_sitk=slice_sitk, # slice_sitk=slice_sitk_mask, # mask for Mask-SRR! # slice_sitk_mask=slice_sitk_mask, # slice_number=slices[i_slice].get_slice_number(), # slice_thickness=slices[i_slice].get_slice_thickness(), # ) # self._stacks[i]._slices[i_slice] = hack # # -------------------------------------------------------- else: self._stacks[i].delete_slice(slices[i_slice]) # print update information ph.print_info("Stack '%s': Slice positions updated " "(%d/%d slices deleted)" % ( stack_name, len(self._stacks[i].get_deleted_slice_numbers()), self._stacks[i].sitk.GetSize()[-1], )) # delete entire stack if all slices were rejected if self._stacks[i].get_number_of_slices() == 0: ph.print_info("Stack '%s' removed as all slices were deleted" % stack_name) self._stacks[i] = None # only return maintained stacks self._stacks = [s for s in self._stacks if s is not None] if len(self._stacks) == 0: raise RuntimeError( "All stacks removed. " "Did you check that the correct motion-correction directory " "was provided?")
def from_filename(cls, file_path, file_path_mask=None, extract_slices=True, verbose=False): stack = cls() if not ph.file_exists(file_path): raise exceptions.FileNotExistent(file_path) path_to_directory = os.path.dirname(file_path) # Strip extension from filename and remove potentially included "." filename = [re.sub("." + ext, "", os.path.basename(file_path)) for ext in ALLOWED_EXTENSIONS if file_path.endswith(ext)][0] # filename = filename.replace(".", "p") stack._dir = os.path.dirname(file_path) stack._filename = filename # Append stacks as SimpleITK and ITK Image objects stack.sitk = sitk.ReadImage(file_path, sitk.sitkFloat64) stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk) # Append masks (either provided or binary mask) if file_path_mask is None: stack.sitk_mask = stack._generate_identity_mask() if verbose: ph.print_info( "Identity mask created for '%s'." % (file_path)) else: if not ph.file_exists(file_path_mask): raise exceptions.FileNotExistent(file_path_mask) stack.sitk_mask = sitk.ReadImage(file_path_mask, sitk.sitkUInt8) stack._is_unity_mask = False # Append itk object stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) # Extract all slices and their masks from the stack and store them if extract_slices: dimenson = stack.sitk.GetDimension() if dimenson == 3: stack._N_slices = stack.sitk.GetSize()[-1] stack._slices = stack._extract_slices() elif dimenson == 2: stack._N_slices = 1 stack._slices = [stack.sitk[:, :]] else: stack._N_slices = 0 stack._slices = None if verbose: ph.print_info( "Stack (image + mask) associated to '%s' successfully read." % (file_path)) return stack
def run(self, older_than_v3=False): if not ph.directory_exists(self._dir_motion_correction): raise exceptions.DirectoryNotExistent( self._dir_motion_correction) abs_path_to_directory = os.path.abspath( self._dir_motion_correction) path_to_rejected_slices = os.path.join( abs_path_to_directory, "rejected_slices.json") if ph.file_exists(path_to_rejected_slices): self._rejected_slices = ph.read_dictionary_from_json( path_to_rejected_slices) bool_check = True else: self._rejected_slices = None bool_check = False for i in range(len(self._stacks)): stack_name = self._stacks[i].get_filename() if not older_than_v3: # update stack position path_to_stack_transform = os.path.join( abs_path_to_directory, "%s.tfm" % stack_name) if ph.file_exists(path_to_stack_transform): transform_stack_sitk = sitkh.read_transform_sitk( path_to_stack_transform) transform_stack_sitk_inv = sitkh.read_transform_sitk( path_to_stack_transform, inverse=True) self._stacks[i].update_motion_correction( transform_stack_sitk) ph.print_info( "Stack '%s': Stack position updated" % stack_name) else: transform_stack_sitk_inv = sitk.Euler3DTransform() if self._volume_motion_only: continue # update slice positions pattern_trafo_slices = stack_name + self._prefix_slice + \ "([0-9]+)[.]tfm" p = re.compile(pattern_trafo_slices) dic_slice_transforms = { int(p.match(f).group(1)): os.path.join( abs_path_to_directory, p.match(f).group(0)) for f in os.listdir(abs_path_to_directory) if p.match(f) } slices = self._stacks[i].get_slices() for i_slice in range(self._stacks[i].get_number_of_slices()): if i_slice in dic_slice_transforms.keys(): transform_slice_sitk = sitkh.read_transform_sitk( dic_slice_transforms[i_slice]) transform_slice_sitk = \ sitkh.get_composite_sitk_affine_transform( transform_slice_sitk, transform_stack_sitk_inv) slices[i_slice].update_motion_correction( transform_slice_sitk) else: self._stacks[i].delete_slice(slices[i_slice]) # ----------------------------- HACK ----------------------------- # 18 Jan 2019 # HACK to use results of a previous version where image slices were # still exported. # (There was a bug after stack intensity correction, which resulted # in v2v-reg transforms not being part of in the final registration # transforms; Thus, slice transformations (tfm's) were flawed and # could not be used): else: # Recover suffix for mask pattern = stack_name + self._prefix_slice + \ "[0-9]+[_]([a-zA-Z]+)[.]nii.gz" pm = re.compile(pattern) matches = list(set([pm.match(f).group(1) for f in os.listdir( abs_path_to_directory) if pm.match(f)])) if len(matches) > 1: raise RuntimeError("Suffix mask cannot be determined") suffix_mask = "_%s" % matches[0] # Recover stack path_to_stack = os.path.join( abs_path_to_directory, "%s.nii.gz" % stack_name) path_to_stack_mask = os.path.join( abs_path_to_directory, "%s%s.nii.gz" % ( stack_name, suffix_mask)) stack = st.Stack.from_filename( path_to_stack, path_to_stack_mask) # Recover slices pattern_trafo_slices = stack_name + self._prefix_slice + \ "([0-9]+)[.]tfm" p = re.compile(pattern_trafo_slices) dic_slice_transforms = { int(p.match(f).group(1)): os.path.join( abs_path_to_directory, p.match(f).group(0)) for f in os.listdir(abs_path_to_directory) if p.match(f) } slices = self._stacks[i].get_slices() for i_slice in range(self._stacks[i].get_number_of_slices()): if i_slice in dic_slice_transforms.keys(): path_to_slice = re.sub( ".tfm", ".nii.gz", dic_slice_transforms[i_slice]) path_to_slice_mask = re.sub( ".tfm", "%s.nii.gz" % suffix_mask, dic_slice_transforms[i_slice]) slice_sitk = sitk.ReadImage(path_to_slice) slice_sitk_mask = sitk.ReadImage(path_to_slice_mask) hack = sl.Slice.from_sitk_image( slice_sitk=slice_sitk, # slice_sitk=slice_sitk_mask, # mask for Mask-SRR! slice_sitk_mask=slice_sitk_mask, slice_number=slices[i_slice].get_slice_number(), slice_thickness=slices[ i_slice].get_slice_thickness(), ) self._stacks[i]._slices[i_slice] = hack else: self._stacks[i].delete_slice(slices[i_slice]) self._stacks[i].sitk = stack.sitk self._stacks[i].sitk_mask = stack.sitk_mask self._stacks[i].itk = stack.itk self._stacks[i].itk_mask = stack.itk_mask # ----------------------------------------------------------------- # print update information ph.print_info( "Stack '%s': Slice positions updated " "(%d/%d slices deleted)" % ( stack_name, len(self._stacks[i].get_deleted_slice_numbers()), self._stacks[i].sitk.GetSize()[-1], ) ) # delete entire stack if all slices were rejected if self._stacks[i].get_number_of_slices() == 0: ph.print_info( "Stack '%s' removed as all slices were deleted" % stack_name) self._stacks[i] = None # only return maintained stacks self._stacks = [s for s in self._stacks if s is not None] if len(self._stacks) == 0: raise RuntimeError( "All stacks removed. " "Did you check that the correct motion-correction directory " "was provided?")
def from_filename( cls, file_path, file_path_mask=None, extract_slices=True, verbose=False, slice_thickness=None, ): stack = cls() if not ph.file_exists(file_path): raise exceptions.FileNotExistent(file_path) path_to_directory = os.path.dirname(file_path) # Strip extension from filename and remove potentially included "." filename = [ re.sub("." + ext, "", os.path.basename(file_path)) for ext in ALLOWED_EXTENSIONS if file_path.endswith(ext) ][0] # filename = filename.replace(".", "p") stack._dir = os.path.dirname(file_path) stack._filename = filename # Append stacks as SimpleITK and ITK Image objects stack.sitk = sitkh.read_nifti_image_sitk(file_path, sitk.sitkFloat64) stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk) # Set slice thickness of acquisition if slice_thickness is None: stack._slice_thickness = stack.sitk.GetSpacing()[-1] else: stack._slice_thickness = slice_thickness # Append masks (either provided or binary mask) if file_path_mask is None: stack.sitk_mask = stack._generate_identity_mask() if verbose: ph.print_info("Identity mask created for '%s'." % (file_path)) else: if not ph.file_exists(file_path_mask): raise exceptions.FileNotExistent(file_path_mask) stack.sitk_mask = sitkh.read_nifti_image_sitk( file_path_mask, sitk.sitkUInt8) try: # ensure masks occupy same physical space stack.sitk_mask.CopyInformation(stack.sitk) except RuntimeError as e: raise IOError( "Given image and its mask do not occupy the same space: %s" % e.message) stack._is_unity_mask = False # Append itk object stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) # Store current affine transform of image stack._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image( stack.sitk) # Prepare history of affine transforms, i.e. encoded spatial # position+orientation of stack, and motion estimates of stack # obtained in the course of the registration/reconstruction process stack._history_affine_transforms = [] stack._history_affine_transforms.append(stack._affine_transform_sitk) stack._history_motion_corrections = [] stack._history_motion_corrections.append(sitk.Euler3DTransform()) # Extract all slices and their masks from the stack and store them if extract_slices: dimenson = stack.sitk.GetDimension() if dimenson == 3: stack._N_slices = stack.sitk.GetSize()[-1] stack._slices = stack._extract_slices( slice_thickness=stack.get_slice_thickness()) elif dimenson == 2: stack._N_slices = 1 stack._slices = [stack.sitk[:, :]] else: stack._N_slices = 0 stack._slices = None if verbose: ph.print_info( "Stack (image + mask) associated to '%s' successfully read." % (file_path)) return stack
def from_slice_filenames( cls, dir_input, prefix_stack, suffix_mask=None, dic_slice_filenames=None, prefix_slice="_slice", slice_thickness=None, ): stack = cls() if dir_input[-1] is not "/": dir_input += "/" stack._dir = dir_input stack._filename = prefix_stack # Get 3D images stack.sitk = sitkh.read_nifti_image_sitk( dir_input + prefix_stack + ".nii.gz", sitk.sitkFloat64) stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk) # Store current affine transform of image stack._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image( stack.sitk) # Prepare history of affine transforms, i.e. encoded spatial # position+orientation of stack, and motion estimates of stack # obtained in the course of the registration/reconstruction process stack._history_affine_transforms = [] stack._history_affine_transforms.append(stack._affine_transform_sitk) stack._history_motion_corrections = [] stack._history_motion_corrections.append(sitk.Euler3DTransform()) # Set slice thickness of acquisition if slice_thickness is None: stack._slice_thickness = float(stack.sitk.GetSpacing()[-1]) else: stack._slice_thickness = float(slice_thickness) # Append masks (either provided or binary mask) if suffix_mask is not None and \ os.path.isfile(dir_input + prefix_stack + suffix_mask + ".nii.gz"): stack.sitk_mask = sitkh.read_nifti_image_sitk( dir_input + prefix_stack + suffix_mask + ".nii.gz", sitk.sitkUInt8) stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) stack._is_unity_mask = False else: stack.sitk_mask = stack._generate_identity_mask() stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) stack._is_unity_mask = True # Get slices if dic_slice_filenames is None: stack._N_slices = stack.sitk.GetDepth() stack._slices = [None] * stack._N_slices # Append slices as Slice objects for i in range(0, stack._N_slices): path_to_slice = os.path.join( dir_input, prefix_stack + prefix_slice + str(i) + ".nii.gz") path_to_slice_mask = os.path.join( dir_input, prefix_stack + prefix_slice + str(i) + suffix_mask + ".nii.gz") if ph.file_exists(path_to_slice_mask): stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=i, file_path_mask=path_to_slice_mask) else: stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=i) else: slice_numbers = sorted(dic_slice_filenames.keys()) stack._N_slices = len(slice_numbers) stack._slices = [None] * stack._N_slices for i, slice_number in enumerate(slice_numbers): path_to_slice = os.path.join( dir_input, dic_slice_filenames[slice_number] + ".nii.gz") path_to_slice_mask = os.path.join( dir_input, dic_slice_filenames[slice_number] + suffix_mask + ".nii.gz") if ph.file_exists(path_to_slice_mask): stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=slice_number, file_path_mask=path_to_slice_mask, slice_thickness=stack.get_slice_thickness(), ) else: stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=slice_number, slice_thickness=stack.get_slice_thickness(), ) return stack
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 from_slice_filenames( cls, dir_input, prefix_stack, suffix_mask=None, dic_slice_filenames=None, prefix_slice="_slice", slice_thickness=None, ): stack = cls() if dir_input[-1] is not "/": dir_input += "/" stack._dir = dir_input stack._filename = prefix_stack # Get 3D images stack.sitk = sitkh.read_nifti_image_sitk( dir_input + prefix_stack + ".nii.gz", sitk.sitkFloat64) stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk) # Set slice thickness of acquisition if slice_thickness is None: stack._slice_thickness = stack.sitk.GetSpacing()[-1] else: stack._slice_thickness = slice_thickness # Append masks (either provided or binary mask) if suffix_mask is not None and \ os.path.isfile(dir_input + prefix_stack + suffix_mask + ".nii.gz"): stack.sitk_mask = sitkh.read_nifti_image_sitk( dir_input + prefix_stack + suffix_mask + ".nii.gz", sitk.sitkUInt8) stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) stack._is_unity_mask = False else: stack.sitk_mask = stack._generate_identity_mask() stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask) stack._is_unity_mask = True # Get slices if dic_slice_filenames is None: stack._N_slices = stack.sitk.GetDepth() stack._slices = [None] * stack._N_slices # Append slices as Slice objects for i in range(0, stack._N_slices): path_to_slice = os.path.join( dir_input, prefix_stack + prefix_slice + str(i) + ".nii.gz") path_to_slice_mask = os.path.join( dir_input, prefix_stack + prefix_slice + str(i) + suffix_mask + ".nii.gz") if ph.file_exists(path_to_slice_mask): stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=i, file_path_mask=path_to_slice_mask) else: stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=i) else: slice_numbers = sorted(dic_slice_filenames.keys()) stack._N_slices = len(slice_numbers) stack._slices = [None] * stack._N_slices for i, slice_number in enumerate(slice_numbers): path_to_slice = os.path.join( dir_input, dic_slice_filenames[slice_number] + ".nii.gz") path_to_slice_mask = os.path.join( dir_input, dic_slice_filenames[slice_number] + suffix_mask + ".nii.gz") if ph.file_exists(path_to_slice_mask): stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=slice_number, file_path_mask=path_to_slice_mask, slice_thickness=stack.get_slice_thickness(), ) else: stack._slices[i] = sl.Slice.from_filename( file_path=path_to_slice, slice_number=slice_number, slice_thickness=stack.get_slice_thickness(), ) return stack
def main(): time_start = ph.start_timing() flag_individual_cases_only = 1 flag_batch_script = 0 batch_ctr = [32] flag_correct_bias_field = 0 # flag_correct_intensities = 0 flag_collect_segmentations = 0 flag_select_images_segmentations = 0 flag_reconstruct_volume_subject_space = 0 flag_reconstruct_volume_subject_space_irtk = 0 flag_reconstruct_volume_subject_space_show_comparison = 0 flag_register_to_template = 0 flag_register_to_template_irtk = 0 flag_show_srr_template_space = 0 flag_reconstruct_volume_template_space = 0 flag_collect_volumetric_reconstruction_results = 0 flag_show_volumetric_reconstruction_results = 0 flag_rsync_stuff = 0 # Analysis flag_remove_failed_cases_for_analysis = 1 flag_postop = 2 # 0... preop, 1...postop, 2... pre+postop flag_evaluate_image_similarities = 0 flag_analyse_image_similarities = 1 flag_evaluate_slice_residual_similarities = 0 flag_analyse_slice_residual_similarities = 0 flag_analyse_stacks = 0 flag_analyse_qualitative_assessment = 0 flag_collect_data_blinded_analysis = 0 flag_anonymize_data_blinded_analysis = 0 provide_comparison = 0 intensity_correction = 1 isotropic_resolution = 0.75 alpha = 0.02 outlier_rejection = 1 threshold = 0.7 threshold_first = 0.6 # metric = "ANTSNeighborhoodCorrelation" # metric_radius = 5 # multiresolution = 0 prefix_srr = "srr_" prefix_srr_qa = "masked_" # ----------------------------------Set Up--------------------------------- if flag_correct_bias_field: dir_batch = os.path.join(utils.DIR_BATCH_ROOT, "BiasFieldCorrection") elif flag_reconstruct_volume_subject_space: dir_batch = os.path.join(utils.DIR_BATCH_ROOT, "VolumetricReconstructionSubjectSpace") elif flag_register_to_template: dir_batch = os.path.join(utils.DIR_BATCH_ROOT, "VolumetricReconstructionRegisterToTemplate") elif flag_reconstruct_volume_template_space: dir_batch = os.path.join(utils.DIR_BATCH_ROOT, "VolumetricReconstructionTemplateSpace") else: dir_batch = os.path.join(utils.DIR_BATCH_ROOT, "foo") file_prefix_batch = os.path.join(dir_batch, "command") if flag_batch_script: verbose = 0 else: verbose = 1 data_reader = dr.ExcelSheetDataReader(utils.EXCEL_FILE) data_reader.read_data() cases = data_reader.get_data() if flag_analyse_qualitative_assessment: data_reader = dr.ExcelSheetQualitativeAssessmentReader(utils.QA_FILE) data_reader.read_data() qualitative_assessment = data_reader.get_data() statistical_evaluation = se.StatisticalEvaluation( qualitative_assessment) statistical_evaluation.run_tests(ref="seg_manual") ph.exit() cases_similarities = [] cases_stacks = [] if flag_individual_cases_only: N_cases = len(INDIVIDUAL_CASE_IDS) else: N_cases = len(cases.keys()) i_case = 0 for case_id in sorted(cases.keys()): if flag_individual_cases_only and case_id not in INDIVIDUAL_CASE_IDS: continue if not flag_analyse_image_similarities and \ not flag_analyse_slice_residual_similarities: i_case += 1 ph.print_title("%d/%d: %s" % (i_case, N_cases, case_id)) if flag_rsync_stuff: dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="") dir_input = re.sub("Volumes/spina/", "Volumes/medic-volumetric_res/SpinaBifida/", dir_output) cmd = "rsync -avuhn --exclude 'motion_correction' %sseg_manual %s" % ( dir_input, dir_output) ph.print_execution(cmd) # ph.execute_command(cmd) # -------------------------Correct Bias Field-------------------------- if flag_correct_bias_field: filenames = utils.get_filenames_preprocessing_bias_field(case_id) paths_to_filenames = [ os.path.join(utils.get_directory_case_original(case_id), f) for f in filenames ] dir_output = utils.get_directory_case_preprocessing( case_id, stage="01_N4ITK") # no image found matching the pattern if len(paths_to_filenames) == 0: continue cmd_args = [] cmd_args.append("--filenames %s" % " ".join(paths_to_filenames)) cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--prefix-output ''") cmd = "niftymic_correct_bias_field %s" % (" ").join(cmd_args) ph.execute_command(cmd, flag_print_to_file=flag_batch_script, path_to_file="%s%d.txt" % (file_prefix_batch, ph.add_one(batch_ctr))) # # Skip case in case segmentations have not been provided yet # if not ph.directory_exists(utils.get_directory_case_segmentation( # case_id, utils.SEGMENTATION_INIT, SEG_MODES[0])): # continue # ------------------------Collect Segmentations------------------------ if flag_collect_segmentations: # Skip case in case segmentations have been collected already if ph.directory_exists( utils.get_directory_case_segmentation( case_id, utils.SEGMENTATION_SELECTED, SEG_MODES[0])): ph.print_info("skipped") continue filenames = utils.get_segmented_image_filenames( case_id, subfolder=utils.SEGMENTATION_INIT) for i_seg_mode, seg_mode in enumerate(SEG_MODES): directory_selected = utils.get_directory_case_segmentation( case_id, utils.SEGMENTATION_SELECTED, seg_mode) ph.create_directory(directory_selected) paths_to_filenames_init = [ os.path.join( utils.get_directory_case_segmentation( case_id, utils.SEGMENTATION_INIT, seg_mode), f) for f in filenames ] paths_to_filenames_selected = [ os.path.join(directory_selected, f) for f in filenames ] for i in range(len(filenames)): cmd = "cp -p %s %s" % (paths_to_filenames_init[i], paths_to_filenames_selected[i]) # ph.print_execution(cmd) ph.execute_command(cmd) if flag_select_images_segmentations: filenames = utils.get_segmented_image_filenames( case_id, subfolder=utils.SEGMENTATION_SELECTED) paths_to_filenames = [ os.path.join( utils.get_directory_case_preprocessing(case_id, stage="01_N4ITK"), f) for f in filenames ] paths_to_filenames_masks = [ os.path.join( utils.get_directory_case_segmentation( case_id, utils.SEGMENTATION_SELECTED, "seg_manual"), f) for f in filenames ] for i in range(len(filenames)): ph.show_niftis( [paths_to_filenames[i]], segmentation=paths_to_filenames_masks[i], # viewer="fsleyes", ) ph.pause() ph.killall_itksnap() # # -------------------------Correct Intensities----------------------- # if flag_correct_intensities: # filenames = utils.get_segmented_image_filenames(case_id) # paths_to_filenames_bias = [os.path.join( # utils.get_directory_case_preprocessing( # case_id, stage="01_N4ITK"), f) for f in filenames] # print paths_to_filenames_bias # -----------------Reconstruct Volume in Subject Space----------------- if flag_reconstruct_volume_subject_space: filenames = utils.get_segmented_image_filenames( case_id, subfolder=utils.SEGMENTATION_SELECTED) # filenames = filenames[0:2] paths_to_filenames = [ os.path.join( utils.get_directory_case_preprocessing(case_id, stage="01_N4ITK"), f) for f in filenames ] # Estimate target stack target_stack_index = utils.get_target_stack_index( case_id, utils.SEGMENTATION_SELECTED, "seg_auto", filenames) for i, seg_mode in enumerate(SEG_MODES): # Get mask filenames paths_to_filenames_masks = [ os.path.join( utils.get_directory_case_segmentation( case_id, utils.SEGMENTATION_SELECTED, seg_mode), f) for f in filenames ] if flag_reconstruct_volume_subject_space_irtk: if seg_mode != "seg_manual": continue utils.export_irtk_call_to_workstation( case_id=case_id, filenames=filenames, seg_mode=seg_mode, isotropic_resolution=isotropic_resolution, target_stack_index=target_stack_index, kernel_mask_dilation=(15, 15, 4)) else: dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode) # dir_output = "/tmp/foo" cmd_args = [] cmd_args.append("--filenames %s" % " ".join(paths_to_filenames)) cmd_args.append("--filenames-masks %s" % " ".join(paths_to_filenames_masks)) cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--use-masks-srr 0") cmd_args.append("--isotropic-resolution %f" % isotropic_resolution) cmd_args.append("--target-stack-index %d" % target_stack_index) cmd_args.append("--intensity-correction %d" % intensity_correction) cmd_args.append("--outlier-rejection %d" % outlier_rejection) cmd_args.append("--threshold-first %f" % threshold_first) cmd_args.append("--threshold %f" % threshold) # cmd_args.append("--metric %s" % metric) # cmd_args.append("--multiresolution %d" % multiresolution) # cmd_args.append("--metric-radius %s" % metric_radius) # if i > 0: # cmd_args.append("--reconstruction-space %s" % ( # utils.get_path_to_recon( # utils.get_directory_case_recon_seg_mode( # case_id, "seg_manual")))) # cmd_args.append("--two-step-cycles 0") cmd_args.append("--verbose %d" % verbose) cmd_args.append("--provide-comparison %d" % provide_comparison) # cmd_args.append("--iter-max 1") cmd = "niftymic_reconstruct_volume %s" % ( " ").join(cmd_args) ph.execute_command( cmd, flag_print_to_file=flag_batch_script, path_to_file="%s%d.txt" % (file_prefix_batch, ph.add_one(batch_ctr))) if flag_reconstruct_volume_subject_space_show_comparison: recon_paths = [] for seg_mode in SEG_MODES: path_to_recon = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode)) recon_paths.append(path_to_recon) recon_path_irtk = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode="IRTK"), "IRTK_SRR.nii.gz") show_modes = list(SEG_MODES) if ph.file_exists(recon_path_irtk): recon_paths.append(recon_path_irtk) show_modes.append("irtk") ph.show_niftis(recon_paths) ph.print_info("Sequence: %s" % (" -- ").join(show_modes)) ph.pause() ph.killall_itksnap() # -------------------------Register to template------------------------ if flag_register_to_template: for seg_mode in SEG_MODES: cmd_args = [] # register seg_auto-recon to template space if seg_mode == "seg_auto": path_to_recon = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode)) template_stack_estimator = \ tse.TemplateStackEstimator.from_mask( ph.append_to_filename(path_to_recon, "_mask")) path_to_reference = \ template_stack_estimator.get_path_to_template() dir_input_motion_correction = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode), "motion_correction") dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode) # dir_output = "/home/mebner/tmp" # # ------- DELETE ----- # dir_output = re.sub("data", "foo+1", dir_output) # dir_output = re.sub( # "volumetric_reconstruction/20180126/template_space/seg_auto", # "", dir_output) # # ------- # cmd_args.append("--use-fixed-mask 1") cmd_args.append("--use-moving-mask 1") # HACK path_to_initial_transform = os.path.join( utils.DIR_INPUT_ROOT_DATA, case_id, "volumetric_reconstruction", "20180126", "template_space", "seg_manual", "registration_transform_sitk.txt") cmd_args.append("--initial-transform %s" % path_to_initial_transform) cmd_args.append("--use-flirt 0") cmd_args.append("--use-regaladin 1") cmd_args.append("--test-ap-flip 0") # register remaining recons to registered seg_auto-recon else: path_to_reference = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="seg_auto"), suffix="ResamplingToTemplateSpace", ) path_to_initial_transform = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="seg_auto"), "registration_transform_sitk.txt") path_to_recon = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode)) dir_input_motion_correction = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode=seg_mode), "motion_correction") dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode) cmd_args.append("--use-fixed-mask 0") cmd_args.append("--use-moving-mask 0") cmd_args.append("--initial-transform %s" % path_to_initial_transform) cmd_args.append("--use-flirt 0") cmd_args.append("--use-regaladin 1") cmd_args.append("--test-ap-flip 0") cmd_args.append("--moving %s" % path_to_recon) cmd_args.append("--fixed %s" % path_to_reference) cmd_args.append("--dir-input %s" % dir_input_motion_correction) cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--write-transform 1") cmd_args.append("--verbose %d" % verbose) cmd = "niftymic_register_image %s" % (" ").join(cmd_args) ph.execute_command(cmd, flag_print_to_file=flag_batch_script, path_to_file="%s%d.txt" % (file_prefix_batch, ph.add_one(batch_ctr))) if flag_register_to_template_irtk: dir_input = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode="IRTK") dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="IRTK") path_to_recon = os.path.join(dir_input, "IRTK_SRR.nii.gz") path_to_reference = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="seg_manual"), suffix="ResamplingToTemplateSpace", ) path_to_initial_transform = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="seg_manual"), "registration_transform_sitk.txt") cmd_args = [] cmd_args.append("--fixed %s" % path_to_reference) cmd_args.append("--moving %s" % path_to_recon) cmd_args.append("--initial-transform %s" % path_to_initial_transform) cmd_args.append("--use-fixed-mask 0") cmd_args.append("--use-moving-mask 0") cmd_args.append("--use-flirt 0") cmd_args.append("--use-regaladin 1") cmd_args.append("--test-ap-flip 0") cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--verbose %d" % verbose) cmd = "niftymic_register_image %s" % (" ").join(cmd_args) ph.execute_command(cmd) if flag_show_srr_template_space: recon_paths = [] show_modes = list(SEG_MODES) # show_modes.append("IRTK") for seg_mode in show_modes: dir_input = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode) # # ------- DELETE ----- # dir_input = re.sub("data", "foo+1", dir_input) # dir_input = re.sub( # "volumetric_reconstruction/20180126/template_space/seg_auto", # "", dir_input) # # ------- path_to_recon_space = utils.get_path_to_recon( dir_input, suffix="ResamplingToTemplateSpace", ) recon_paths.append(path_to_recon_space) ph.show_niftis(recon_paths) ph.print_info("Sequence: %s" % (" -- ").join(show_modes)) ph.pause() ph.killall_itksnap() # -----------------Reconstruct Volume in Template Space---------------- if flag_reconstruct_volume_template_space: for seg_mode in SEG_MODES: path_to_recon_space = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode), suffix="ResamplingToTemplateSpace", ) dir_input = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode), "motion_correction") dir_output = utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode) # dir_output = os.path.join("/tmp/spina/template_space/%s-%s" % ( # case_id, seg_mode)) cmd_args = [] cmd_args.append("--dir-input %s" % dir_input) cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--reconstruction-space %s" % path_to_recon_space) cmd_args.append("--alpha %s" % alpha) cmd_args.append("--verbose %s" % verbose) cmd_args.append("--use-masks-srr 0") # cmd_args.append("--minimizer L-BFGS-B") # cmd_args.append("--alpha 0.006") # cmd_args.append("--reconstruction-type HuberL2") # cmd_args.append("--data-loss arctan") # cmd_args.append("--iterations 5") # cmd_args.append("--data-loss-scale 0.7") cmd = "niftymic_reconstruct_volume_from_slices %s" % \ (" ").join(cmd_args) ph.execute_command(cmd, flag_print_to_file=flag_batch_script, path_to_file="%s%d.txt" % (file_prefix_batch, ph.add_one(batch_ctr))) # ----------------Collect SRR results in Template Space---------------- if flag_collect_volumetric_reconstruction_results: directory = utils.get_directory_case_recon_summary(case_id) ph.create_directory(directory) # clear potentially existing files cmd = "rm -f %s/*.nii.gz" % (directory) ph.execute_command(cmd) # Collect SRRs for seg_mode in SEG_MODES: path_to_recon_src = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode), ) path_to_recon = os.path.join( directory, "%s%s.nii.gz" % (prefix_srr, seg_mode)) cmd = "cp -p %s %s" % (path_to_recon_src, path_to_recon) ph.execute_command(cmd) # Collect IRTK recon path_to_recon_src = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode="IRTK"), "IRTK_SRR_LinearResamplingToTemplateSpace.nii.gz") path_to_recon = os.path.join(directory, "%s%s.nii.gz" % (prefix_srr, "irtk")) cmd = "cp -p %s %s" % (path_to_recon_src, path_to_recon) ph.execute_command(cmd) # Collect evaluation mask path_to_recon = utils.get_path_to_recon( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="subject_space", seg_mode="seg_auto")) template_stack_estimator = \ tse.TemplateStackEstimator.from_mask( ph.append_to_filename(path_to_recon, "_mask")) path_to_template = \ template_stack_estimator.get_path_to_template() path_to_template_mask_src = ph.append_to_filename( path_to_template, "_mask_dil") path_to_template_mask = "%s/" % directory cmd = "cp -p %s %s" % (path_to_template_mask_src, path_to_template_mask) ph.execute_command(cmd) if flag_show_volumetric_reconstruction_results: dir_output = utils.get_directory_case_recon_summary(case_id) paths_to_recons = [] for seg_mode in RECON_MODES: path_to_recon = os.path.join( dir_output, "%s%s.nii.gz" % (prefix_srr, seg_mode)) paths_to_recons.append(path_to_recon) path_to_mask = "%s/STA*.nii.gz" % dir_output cmd = ph.show_niftis(paths_to_recons, segmentation=path_to_mask) sitkh.write_executable_file([cmd], dir_output=dir_output) ph.pause() ph.killall_itksnap() # ---------------------Evaluate Image Similarities--------------------- if flag_evaluate_image_similarities: dir_input = utils.get_directory_case_recon_summary(case_id) dir_output = utils.get_directory_case_recon_similarities(case_id) paths_to_recons = [] for seg_mode in ["seg_auto", "detect", "irtk"]: path_to_recon = os.path.join( dir_input, "%s%s.nii.gz" % (prefix_srr, seg_mode)) paths_to_recons.append(path_to_recon) path_to_reference = os.path.join( dir_input, "%s%s.nii.gz" % (prefix_srr, "seg_manual")) path_to_reference_mask = utils.get_path_to_mask(dir_input) cmd_args = [] cmd_args.append("--filenames %s" % " ".join(paths_to_recons)) cmd_args.append("--reference %s" % path_to_reference) cmd_args.append("--reference-mask %s" % path_to_reference_mask) # cmd_args.append("--verbose 1") cmd_args.append("--dir-output %s" % dir_output) exe = re.sub("pyc", "py", os.path.abspath(evaluate_image_similarity.__file__)) cmd_args.insert(0, exe) # clear potentially existing files cmd = "rm -f %s/*.txt" % (dir_output) ph.execute_command(cmd) cmd = "python %s" % " ".join(cmd_args) ph.execute_command(cmd) # -----------------Evaluate Slice Residual Similarities---------------- if flag_evaluate_slice_residual_similarities: path_to_reference_mask = utils.get_path_to_mask( utils.get_directory_case_recon_summary(case_id)) dir_output_root = \ utils.get_directory_case_slice_residual_similarities(case_id) # clear potentially existing files # cmd = "rm -f %s/*.txt" % (dir_output_root) # ph.execute_command(cmd) for seg_mode in SEG_MODES: dir_input = os.path.join( utils.get_directory_case_recon_seg_mode( case_id=case_id, recon_space="template_space", seg_mode=seg_mode, ), "motion_correction") path_to_reference = os.path.join( utils.get_directory_case_recon_summary(case_id), "%s%s.nii.gz" % (prefix_srr, seg_mode)) dir_output = os.path.join(dir_output_root, seg_mode) cmd_args = [] cmd_args.append("--dir-input %s" % dir_input) cmd_args.append("--reference %s" % path_to_reference) cmd_args.append("--reference-mask %s" % path_to_reference_mask) cmd_args.append("--use-reference-mask 1") cmd_args.append("--use-slice-masks 0") # cmd_args.append("--verbose 1") cmd_args.append("--dir-output %s" % dir_output) exe = re.sub("pyc", "py", os.path.abspath(esrs.__file__)) cmd_args.insert(0, exe) cmd = "python %s" % " ".join(cmd_args) ph.execute_command(cmd) # Collect data for blinded analysis if flag_collect_data_blinded_analysis: if flag_remove_failed_cases_for_analysis and case_id in RECON_FAILED_CASE_IDS: continue dir_input = utils.get_directory_case_recon_summary(case_id) # pattern = "STA([0-9]+)[_]mask.nii.gz" pattern = "STA([0-9]+)[_]mask_dil.nii.gz" p = re.compile(pattern) gw = [ p.match(f).group(1) for f in os.listdir(dir_input) if p.match(f) ][0] dir_output = os.path.join( utils.get_directory_blinded_analysis(case_id, "open"), case_id) exe = re.sub("pyc", "py", os.path.abspath(mswm.__file__)) recons = [] for seg_mode in RECON_MODES: path_to_recon = os.path.join( dir_input, "%s%s.nii.gz" % (prefix_srr, seg_mode)) cmd_args = [] cmd_args.append("--filename %s" % path_to_recon) cmd_args.append("--gestational-age %s" % gw) cmd_args.append("--dir-output %s" % dir_output) cmd_args.append("--prefix-output %s" % prefix_srr_qa) cmd_args.append("--verbose 0") cmd_args.insert(0, exe) cmd = "python %s" % " ".join(cmd_args) # ph.execute_command(cmd) recon = "%s%s" % (prefix_srr_qa, os.path.basename(path_to_recon)) recons.append(recon) ph.write_show_niftis_exe(recons, dir_output) if flag_anonymize_data_blinded_analysis: dir_input = os.path.join( utils.get_directory_blinded_analysis(case_id, "open"), case_id) dir_output_dictionaries = utils.get_directory_anonymized_dictionares( case_id) dir_output_anonymized_images = os.path.join( utils.get_directory_blinded_analysis(case_id, "anonymized"), case_id) if not ph.directory_exists(dir_input): continue ph.create_directory(dir_output_dictionaries) ph.create_directory(dir_output_anonymized_images) data_anonymizer = da.DataAnonymizer() # Create random dictionary (only required once) # data_anonymizer.set_prefix_identifiers("%s_" % case_id) # data_anonymizer.read_nifti_filenames_from_directory(dir_input) # data_anonymizer.generate_identifiers() # data_anonymizer.generate_randomized_dictionary() # data_anonymizer.write_dictionary( # dir_output_dictionaries, "dictionary_%s" % case_id) # Read dictionary data_anonymizer.read_dictionary(dir_output_dictionaries, "dictionary_%s" % case_id) # Anonymize files if 0: ph.clear_directory(dir_output_anonymized_images) data_anonymizer.anonymize_files(dir_input, dir_output_anonymized_images) # Write executable script filenames = [ "%s.nii.gz" % f for f in sorted(data_anonymizer.get_identifiers()) ] ph.write_show_niftis_exe(filenames, dir_output_anonymized_images) # Reveal anonymized files if 1: filenames = data_anonymizer.reveal_anonymized_files( dir_output_anonymized_images) filenames = sorted(["%s" % f for f in filenames]) ph.write_show_niftis_exe(filenames, dir_output_anonymized_images) # Reveal additional, original files # data_anonymizer.reveal_original_files(dir_output) # relative_directory = re.sub( # utils.get_directory_blinded_analysis(case_id, "anonymized"), # ".", # dir_output_anonymized_images) # paths_to_filenames = [os.path.join( # relative_directory, f) for f in filenames] # ---------------------Analyse Image Similarities--------------------- if flag_analyse_image_similarities or \ flag_analyse_slice_residual_similarities or \ flag_analyse_stacks: if flag_remove_failed_cases_for_analysis: if case_id in RECON_FAILED_CASE_IDS: continue if cases[case_id]['postrep'] == flag_postop or flag_postop == 2: cases_similarities.append(case_id) cases_stacks.append( utils.get_segmented_image_filenames( case_id, # subfolder=utils.SEGMENTATION_INIT, subfolder=utils.SEGMENTATION_SELECTED, )) dir_output_analysis = os.path.join( # "/Users/mebner/UCL/UCL/Publications", "/home/mebner/Dropbox/UCL/Publications", "2018_MICCAI/brain_reconstruction_paper") if flag_analyse_image_similarities: dir_inputs = [] filename = "image_similarities_postop%d.txt" % flag_postop for case_id in cases_similarities: dir_inputs.append( utils.get_directory_case_recon_similarities(case_id)) cmd_args = [] cmd_args.append("--dir-inputs %s" % " ".join(dir_inputs)) cmd_args.append("--dir-output %s" % dir_output_analysis) cmd_args.append("--filename %s" % filename) exe = re.sub("pyc", "py", os.path.abspath(src.analyse_image_similarities.__file__)) cmd_args.insert(0, exe) cmd = "python %s" % " ".join(cmd_args) ph.execute_command(cmd) if flag_analyse_slice_residual_similarities: dir_inputs = [] filename = "slice_residuals_postop%d.txt" % flag_postop for case_id in cases_similarities: dir_inputs.append( utils.get_directory_case_slice_residual_similarities(case_id)) cmd_args = [] cmd_args.append("--dir-inputs %s" % " ".join(dir_inputs)) cmd_args.append("--subfolder %s" % " ".join(SEG_MODES)) cmd_args.append("--dir-output %s" % dir_output_analysis) cmd_args.append("--filename %s" % filename) exe = re.sub( "pyc", "py", os.path.abspath(src.analyse_slice_residual_similarities.__file__)) cmd_args.insert(0, exe) cmd = "python %s" % " ".join(cmd_args) # print len(cases_similarities) # print cases_similarities ph.execute_command(cmd) if flag_analyse_stacks: cases_stacks_N = [len(s) for s in cases_stacks] ph.print_subtitle("%d cases -- Number of stacks" % len(cases_stacks)) ph.print_info("min: %g" % np.min(cases_stacks_N)) ph.print_info("mean: %g" % np.mean(cases_stacks_N)) ph.print_info("median: %g" % np.median(cases_stacks_N)) ph.print_info("max: %g" % np.max(cases_stacks_N)) elapsed_time = ph.stop_timing(time_start) ph.print_title("Summary") print("Computational Time for Pipeline: %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) input_parser.add_output(help="Path to registration transform (.txt)", required=True) input_parser.add_fixed_mask(required=False) input_parser.add_moving_mask(required=False) input_parser.add_option( option_string="--initial-transform", type=str, help="Path to initial transform. " "If not provided, registration will be initialized based on " "rigid alignment of eigenbasis of the fixed/moving image masks " "using principal component analysis", default=None) input_parser.add_v2v_method( option_string="--method", help="Registration method used for the registration.", default="RegAladin", ) input_parser.add_argument( "--init-pca", "-init-pca", action='store_true', help="If given, PCA-based initializations will be refined using " "RegAladin registrations.") input_parser.add_dir_input_mc() input_parser.add_verbose(default=0) input_parser.add_log_config(default=1) args = input_parser.parse_args() input_parser.print_arguments(args) if args.log_config: input_parser.log_config(os.path.abspath(__file__)) if not args.output.endswith(".txt"): raise IOError("output filename '%s' invalid; " "allowed transformation extensions are: '.txt'" % (args.output)) if args.initial_transform is not None and args.init_pca: raise IOError( "Both --initial-transform and --init-pca cannot be activated. " "Choose one.") dir_output = os.path.dirname(args.output) ph.create_directory(dir_output) debug = False # --------------------------------Read Data-------------------------------- ph.print_title("Read Data") fixed = st.Stack.from_filename(file_path=args.fixed, file_path_mask=args.fixed_mask, extract_slices=False) moving = st.Stack.from_filename(file_path=args.moving, file_path_mask=args.moving_mask, extract_slices=False) path_to_tmp_output = os.path.join( DIR_TMP, ph.append_to_filename(os.path.basename(args.moving), "_warped")) # ---------------------------- Initialization ---------------------------- if args.initial_transform is None and args.init_pca: ph.print_title("Estimate (initial) transformation using PCA") if args.moving_mask is None or args.fixed_mask is None: ph.print_warning("Fixed and moving masks are strongly recommended") transform_initializer = tinit.TransformInitializer( fixed=fixed, moving=moving, similarity_measure="NMI", refine_pca_initializations=True, ) transform_initializer.run() transform_init_sitk = transform_initializer.get_transform_sitk() elif args.initial_transform is not None: transform_init_sitk = sitkh.read_transform_sitk(args.initial_transform) else: transform_init_sitk = None if transform_init_sitk is not None: sitk.WriteTransform(transform_init_sitk, args.output) # -------------------Register Reconstruction to Template------------------- ph.print_title("Registration") # If --init-pca given, RegAladin run already performed if args.method == "RegAladin" and not args.init_pca: path_to_transform_regaladin = os.path.join(DIR_TMP, "transform_regaladin.txt") # Convert SimpleITK to RegAladin transform if transform_init_sitk is not None: cmd = "simplereg_transform -sitk2nreg %s %s" % ( args.output, path_to_transform_regaladin) ph.execute_command(cmd, verbose=False) # Run NiftyReg cmd_args = ["reg_aladin"] cmd_args.append("-ref '%s'" % args.fixed) cmd_args.append("-flo '%s'" % args.moving) cmd_args.append("-res '%s'" % path_to_tmp_output) if transform_init_sitk is not None: cmd_args.append("-inaff '%s'" % path_to_transform_regaladin) cmd_args.append("-aff '%s'" % path_to_transform_regaladin) cmd_args.append("-rigOnly") cmd_args.append("-ln 2") # seems to perform better for spina bifida cmd_args.append("-voff") if args.fixed_mask is not None: cmd_args.append("-rmask '%s'" % args.fixed_mask) # To avoid error "0 correspondences between blocks were found" that can # occur for some cases. Also, disable moving mask, as this would be ignored # anyway cmd_args.append("-noSym") # if args.moving_mask is not None: # cmd_args.append("-fmask '%s'" % args.moving_mask) ph.print_info("Run Registration (RegAladin) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=debug) print("done") # Convert RegAladin to SimpleITK transform cmd = "simplereg_transform -nreg2sitk '%s' '%s'" % ( path_to_transform_regaladin, args.output) ph.execute_command(cmd, verbose=False) elif args.method == "FLIRT": path_to_transform_flirt = os.path.join(DIR_TMP, "transform_flirt.txt") # Convert SimpleITK into FLIRT transform if transform_init_sitk is not None: cmd = "simplereg_transform -sitk2flirt '%s' '%s' '%s' '%s'" % ( args.output, args.fixed, args.moving, path_to_transform_flirt) ph.execute_command(cmd, verbose=False) # Define search angle ranges for FLIRT in all three dimensions # search_angles = ["-searchr%s -%d %d" % (x, 180, 180) # for x in ["x", "y", "z"]] cmd_args = ["flirt"] cmd_args.append("-in '%s'" % args.moving) cmd_args.append("-ref '%s'" % args.fixed) if transform_init_sitk is not None: cmd_args.append("-init '%s'" % path_to_transform_flirt) cmd_args.append("-omat '%s'" % path_to_transform_flirt) cmd_args.append("-out '%s'" % path_to_tmp_output) cmd_args.append("-dof 6") # cmd_args.append((" ").join(search_angles)) if args.moving_mask is not None: cmd_args.append("-inweight '%s'" % args.moving_mask) if args.fixed_mask is not None: cmd_args.append("-refweight '%s'" % args.fixed_mask) ph.print_info("Run Registration (FLIRT) ... ", newline=False) ph.execute_command(" ".join(cmd_args), verbose=debug) print("done") # Convert FLIRT to SimpleITK transform cmd = "simplereg_transform -flirt2sitk '%s' '%s' '%s' '%s'" % ( path_to_transform_flirt, args.fixed, args.moving, args.output) ph.execute_command(cmd, verbose=False) ph.print_info("Registration transformation written to '%s'" % args.output) if args.dir_input_mc is not None: ph.print_title("Update Motion-Correction Transformations") transform_sitk = sitkh.read_transform_sitk(args.output, inverse=1) if args.dir_input_mc.endswith("/"): subdir_mc = args.dir_input_mc.split("/")[-2] else: subdir_mc = args.dir_input_mc.split("/")[-1] dir_output_mc = os.path.join(dir_output, subdir_mc) ph.create_directory(dir_output_mc, delete_files=True) pattern = REGEX_FILENAMES + "[.]tfm" p = re.compile(pattern) trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)] for t in trafos: path_to_input_transform = os.path.join(args.dir_input_mc, t) path_to_output_transform = os.path.join(dir_output_mc, t) t_sitk = sitkh.read_transform_sitk(path_to_input_transform) t_sitk = sitkh.get_composite_sitk_affine_transform( transform_sitk, t_sitk) sitk.WriteTransform(t_sitk, path_to_output_transform) ph.print_info("%d transformations written to '%s'" % (len(trafos), dir_output_mc)) # Copy rejected_slices.json file path_to_rejected_slices = os.path.join(args.dir_input_mc, "rejected_slices.json") if ph.file_exists(path_to_rejected_slices): ph.copy_file(path_to_rejected_slices, dir_output_mc) if args.verbose: cmd_args = ["simplereg_resample"] cmd_args.append("-f %s" % args.fixed) cmd_args.append("-m %s" % args.moving) cmd_args.append("-t %s" % args.output) cmd_args.append("-o %s" % path_to_tmp_output) ph.execute_command(" ".join(cmd_args)) ph.show_niftis([args.fixed, path_to_tmp_output]) elapsed_time_total = ph.stop_timing(time_start) # Summary 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 test_register_image(self): filename = "registration_transform_sitk.txt" gestational_age = 28 path_to_recon = os.path.join( self.dir_data, "register_image", "SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz") dir_input_mc = os.path.join(self.dir_data, "register_image", "motion_correction") path_to_transform_res = os.path.join(self.dir_output, filename) path_to_transform_ref = os.path.join(self.dir_data, "register_image", filename) dir_ref_mc = os.path.join(self.dir_data, "register_image", "motion_correction_ref") path_to_rejected_slices_ref = os.path.join(dir_ref_mc, "rejected_slices.json") 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 = ["niftymic_register_image"] cmd_args.append("--fixed %s" % template) cmd_args.append("--moving %s" % path_to_recon) cmd_args.append("--fixed-mask %s" % template_mask) cmd_args.append("--moving-mask %s" % ph.append_to_filename(path_to_recon, self.suffix_mask)) cmd_args.append("--dir-input-mc %s" % dir_input_mc) cmd_args.append("--output %s" % path_to_transform_res) cmd_args.append("--init-pca") # cmd_args.append("--verbose 1") self.assertEqual(ph.execute_command(" ".join(cmd_args)), 0) # Check registration transform res_sitk = sitkh.read_transform_sitk(path_to_transform_res) ref_sitk = sitkh.read_transform_sitk(path_to_transform_ref) res_nda = res_sitk.GetParameters() ref_nda = ref_sitk.GetParameters() diff_nda = np.array(res_nda) - ref_nda self.assertAlmostEqual(np.linalg.norm(diff_nda), 0, places=self.precision) # Check individual slice transforms pattern = REGEX_FILENAMES + "[.]tfm" p = re.compile(pattern) dir_res_mc = os.path.join(self.dir_output, "motion_correction") trafos_res = sorted([ os.path.join(dir_res_mc, t) for t in os.listdir(dir_res_mc) if p.match(t) ]) trafos_ref = sorted([ os.path.join(dir_ref_mc, t) for t in os.listdir(dir_res_mc) if p.match(t) ]) self.assertEqual(len(trafos_res), len(trafos_ref)) for i in range(len(trafos_ref)): nda_res = sitkh.read_transform_sitk(trafos_res[i]).GetParameters() nda_ref = sitkh.read_transform_sitk(trafos_ref[i]).GetParameters() nda_diff = np.linalg.norm(np.array(nda_res) - nda_ref) self.assertAlmostEqual(nda_diff, 0, places=self.precision) # Check rejected_slices.json path_to_rejected_slices_res = os.path.join(dir_res_mc, "rejected_slices.json") self.assertEqual(ph.file_exists(path_to_rejected_slices_res), True) rejected_slices_res = ph.read_dictionary_from_json( path_to_rejected_slices_res) rejected_slices_ref = ph.read_dictionary_from_json( path_to_rejected_slices_ref) self.assertEqual(rejected_slices_res == rejected_slices_ref, True)