Esempio n. 1
0
    def test_convert_flirt_to_sitk_transform(self):
        for dim in [3]:
            path_to_flirt_transform = os.path.join(
                DIR_TEST, "%dD_flirt_Target_Source.txt" % dim)
            path_to_fixed = os.path.join(DIR_DATA,
                                         "%dD_Brain_Target.nii.gz" % dim)
            path_to_moving = os.path.join(DIR_DATA,
                                          "%dD_Brain_Source.nii.gz" % dim)
            path_to_res = os.path.join(
                DIR_TMP, "%dD_flirt2sitk_target_Source_.txt" % dim)
            path_to_reference_transform = os.path.join(
                DIR_TEST, "%dD_sitk_Target_Source.txt" % dim)

            flirt2sitk.convert_flirt_to_sitk_transform(path_to_flirt_transform,
                                                       path_to_fixed,
                                                       path_to_moving,
                                                       path_to_res)
            transform_sitk = sitkh.read_transform_sitk(path_to_res)

            transform_ref_sitk = sitkh.read_transform_sitk(
                path_to_reference_transform)

            nda_reference = np.array(transform_ref_sitk.GetParameters())
            nda = np.array(transform_sitk.GetParameters())

            # Conversion to FLIRT only provides 4 decimal places
            self.assertAlmostEqual(np.sum(np.abs(nda - nda_reference)),
                                   0,
                                   places=2)
Esempio n. 2
0
    def test_estimate_motion(self):
        filename = "SRR_reference.nii.gz"
        output = os.path.join(self.dir_output, filename)
        dir_reference = os.path.join(self.dir_data, "estimate_motion")
        dir_reference_mc = os.path.join(dir_reference, "motion_correction")
        path_to_reference = os.path.join(dir_reference, filename)
        path_to_reference_mask = ph.append_to_filename(
            os.path.join(dir_reference, filename), self.suffix_mask)

        two_step_cycles = 1
        iter_max = 5

        exe = os.path.abspath(rsfmri_estimate_motion.__file__)
        cmd_args = ["python %s" % exe]
        cmd_args.append("--filename %s" % self.filename)
        cmd_args.append("--filename-mask %s" % ph.append_to_filename(
            self.filename, self.suffix_mask))
        cmd_args.append("--dir-output %s" % self.dir_output)
        cmd_args.append("--two-step-cycles %s" % two_step_cycles)
        cmd_args.append("--iter-max %d" % iter_max)
        cmd = (" ").join(cmd_args)
        self.assertEqual(ph.execute_command(cmd), 0)

        # Check SRR volume
        res_sitk = sitkh.read_nifti_image_sitk(output)
        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference)
        diff_sitk = res_sitk - ref_sitk
        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))
        self.assertAlmostEqual(error, 0, places=self.precision)

        # Check SRR mask volume
        res_sitk = sitkh.read_nifti_image_sitk(
            ph.append_to_filename(output, self.suffix_mask))
        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference_mask)
        diff_sitk = res_sitk - ref_sitk
        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))
        self.assertAlmostEqual(error, 0, places=self.precision)

        # Check 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_reference_mc, t)
             for t in os.listdir(dir_reference_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)
Esempio n. 3
0
    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 _run_registrations(self, transformations):
        path_to_fixed = os.path.join(DIR_TMP, "fixed.nii.gz")
        path_to_moving = os.path.join(DIR_TMP, "moving.nii.gz")
        path_to_fixed_mask = os.path.join(DIR_TMP, "fixed_mask.nii.gz")
        path_to_moving_mask = os.path.join(DIR_TMP, "moving_mask.nii.gz")
        path_to_tmp_output = os.path.join(DIR_TMP, "foo.nii.gz")
        path_to_transform_regaladin = os.path.join(DIR_TMP,
                                                   "transform_regaladin.txt")
        path_to_transform_sitk = os.path.join(DIR_TMP, "transform_sitk.txt")

        sitkh.write_nifti_image_sitk(self._fixed.sitk, path_to_fixed)
        sitkh.write_nifti_image_sitk(self._moving.sitk, path_to_moving)
        sitkh.write_nifti_image_sitk(self._fixed.sitk_mask, path_to_fixed_mask)
        # sitkh.write_nifti_image_sitk(
        #     self._moving.sitk_mask, path_to_moving_mask)

        for i in range(len(transformations)):
            sitk.WriteTransform(transformations[i], path_to_transform_sitk)

            # Convert SimpleITK to RegAladin transform
            cmd = "simplereg_transform -sitk2nreg %s %s" % (
                path_to_transform_sitk, path_to_transform_regaladin)
            ph.execute_command(cmd, verbose=False)

            # Run NiftyReg
            cmd_args = ["reg_aladin"]
            cmd_args.append("-ref %s" % path_to_fixed)
            cmd_args.append("-flo %s" % path_to_moving)
            cmd_args.append("-res %s" % path_to_tmp_output)
            cmd_args.append("-inaff %s" % path_to_transform_regaladin)
            cmd_args.append("-aff %s" % path_to_transform_regaladin)
            cmd_args.append("-rigOnly")
            cmd_args.append("-ln 2")
            cmd_args.append("-voff")
            cmd_args.append("-rmask %s" % path_to_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")
            ph.print_info(
                "Run Registration (RegAladin) based on PCA-init %d ... " %
                (i + 1))
            ph.execute_command(" ".join(cmd_args), verbose=False)

            # Convert RegAladin to SimpleITK transform
            cmd = "simplereg_transform -nreg2sitk %s %s" % (
                path_to_transform_regaladin, path_to_transform_sitk)
            ph.execute_command(cmd, verbose=False)

            transformations[i] = sitkh.read_transform_sitk(
                path_to_transform_sitk)

        return transformations
Esempio n. 5
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(
        "--refine-pca",
        "-refine-pca",
        action='store_true',
        help="If given, PCA-based initializations will be refined using "
        "RegAladin registrations.")
    input_parser.add_dir_input_mc()
    input_parser.add_verbose(default=0)
    input_parser.add_log_config(default=1)

    args = input_parser.parse_args()
    input_parser.print_arguments(args)

    if args.log_config:
        input_parser.log_config(os.path.abspath(__file__))

    if not args.output.endswith(".txt"):
        raise IOError("output transformation path must end in '.txt'")

    dir_output = os.path.dirname(args.output)
    ph.create_directory(dir_output)

    # --------------------------------Read Data--------------------------------
    ph.print_title("Read Data")
    fixed = st.Stack.from_filename(file_path=args.fixed,
                                   file_path_mask=args.fixed_mask,
                                   extract_slices=False)
    moving = st.Stack.from_filename(file_path=args.moving,
                                    file_path_mask=args.moving_mask,
                                    extract_slices=False)

    path_to_tmp_output = os.path.join(
        DIR_TMP, ph.append_to_filename(os.path.basename(args.moving),
                                       "_warped"))

    # ---------------------------- Initialization ----------------------------
    if args.initial_transform is None:
        ph.print_title("Estimate initial transform using PCA")

        if args.moving_mask is None or args.fixed_mask is None:
            ph.print_warning("Fixed and moving masks are strongly recommended")
        transform_initializer = tinit.TransformInitializer(
            fixed=fixed,
            moving=moving,
            similarity_measure="NMI",
            refine_pca_initializations=args.refine_pca,
        )
        transform_initializer.run()
        transform_init_sitk = transform_initializer.get_transform_sitk()
    else:
        transform_init_sitk = sitkh.read_transform_sitk(args.initial_transform)
    sitk.WriteTransform(transform_init_sitk, args.output)

    # -------------------Register Reconstruction to Template-------------------
    ph.print_title("Registration")

    if args.method == "RegAladin":

        path_to_transform_regaladin = os.path.join(DIR_TMP,
                                                   "transform_regaladin.txt")

        # Convert SimpleITK to RegAladin transform
        cmd = "simplereg_transform -sitk2nreg %s %s" % (
            args.output, path_to_transform_regaladin)
        ph.execute_command(cmd, verbose=False)

        # Run NiftyReg
        cmd_args = ["reg_aladin"]
        cmd_args.append("-ref '%s'" % args.fixed)
        cmd_args.append("-flo '%s'" % args.moving)
        cmd_args.append("-res '%s'" % path_to_tmp_output)
        cmd_args.append("-inaff '%s'" % path_to_transform_regaladin)
        cmd_args.append("-aff '%s'" % path_to_transform_regaladin)
        cmd_args.append("-rigOnly")
        cmd_args.append("-ln 2")  # seems to perform better for spina bifida
        cmd_args.append("-voff")
        if args.fixed_mask is not None:
            cmd_args.append("-rmask '%s'" % args.fixed_mask)

        # To avoid error "0 correspondences between blocks were found" that can
        # occur for some cases. Also, disable moving mask, as this would be ignored
        # anyway
        cmd_args.append("-noSym")
        # if args.moving_mask is not None:
        #     cmd_args.append("-fmask '%s'" % args.moving_mask)

        ph.print_info("Run Registration (RegAladin) ... ", newline=False)
        ph.execute_command(" ".join(cmd_args), verbose=False)
        print("done")

        # Convert RegAladin to SimpleITK transform
        cmd = "simplereg_transform -nreg2sitk '%s' '%s'" % (
            path_to_transform_regaladin, args.output)
        ph.execute_command(cmd, verbose=False)

    else:
        path_to_transform_flirt = os.path.join(DIR_TMP, "transform_flirt.txt")

        # Convert SimpleITK into FLIRT transform
        cmd = "simplereg_transform -sitk2flirt '%s' '%s' '%s' '%s'" % (
            args.output, args.fixed, args.moving, path_to_transform_flirt)
        ph.execute_command(cmd, verbose=False)

        # Define search angle ranges for FLIRT in all three dimensions
        search_angles = [
            "-searchr%s -%d %d" % (x, 180, 180) for x in ["x", "y", "z"]
        ]

        cmd_args = ["flirt"]
        cmd_args.append("-in '%s'" % args.moving)
        cmd_args.append("-ref '%s'" % args.fixed)
        if args.initial_transform is not None:
            cmd_args.append("-init '%s'" % path_to_transform_flirt)
        cmd_args.append("-omat '%s'" % path_to_transform_flirt)
        cmd_args.append("-out '%s'" % path_to_tmp_output)
        cmd_args.append("-dof 6")
        cmd_args.append((" ").join(search_angles))
        if args.moving_mask is not None:
            cmd_args.append("-inweight '%s'" % args.moving_mask)
        if args.fixed_mask is not None:
            cmd_args.append("-refweight '%s'" % args.fixed_mask)
        ph.print_info("Run Registration (FLIRT) ... ", newline=False)
        ph.execute_command(" ".join(cmd_args), verbose=False)
        print("done")

        # Convert FLIRT to SimpleITK transform
        cmd = "simplereg_transform -flirt2sitk '%s' '%s' '%s' '%s'" % (
            path_to_transform_flirt, args.fixed, args.moving, args.output)
        ph.execute_command(cmd, verbose=False)

    if args.dir_input_mc is not None:
        ph.print_title("Update Motion-Correction Transformations")
        transform_sitk = sitkh.read_transform_sitk(args.output, inverse=1)

        if args.dir_input_mc.endswith("/"):
            subdir_mc = args.dir_input_mc.split("/")[-2]
        else:
            subdir_mc = args.dir_input_mc.split("/")[-1]
        dir_output_mc = os.path.join(dir_output, subdir_mc)

        ph.create_directory(dir_output_mc, delete_files=True)
        pattern = REGEX_FILENAMES + "[.]tfm"
        p = re.compile(pattern)
        trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)]
        for t in trafos:
            path_to_input_transform = os.path.join(args.dir_input_mc, t)
            path_to_output_transform = os.path.join(dir_output_mc, t)
            t_sitk = sitkh.read_transform_sitk(path_to_input_transform)
            t_sitk = sitkh.get_composite_sitk_affine_transform(
                transform_sitk, t_sitk)
            sitk.WriteTransform(t_sitk, path_to_output_transform)
        ph.print_info("%d transformations written to '%s'" %
                      (len(trafos), dir_output_mc))

    if args.verbose:
        ph.show_niftis([args.fixed, path_to_tmp_output])

    elapsed_time_total = ph.stop_timing(time_start)

    # Summary
    ph.print_title("Summary")
    print("Computational Time: %s" % (elapsed_time_total))

    return 0
Esempio n. 6
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?")
Esempio n. 7
0
    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?")
Esempio n. 8
0
    def test_reconstruct_volume(self):
        filename = "SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz"
        output = os.path.join(self.dir_output, filename)
        dir_reference = os.path.join(self.dir_data, "reconstruct_volume")
        dir_reference_mc = os.path.join(dir_reference, "motion_correction")
        path_to_reference = os.path.join(dir_reference, filename)
        path_to_reference_mask = ph.append_to_filename(
            os.path.join(dir_reference, filename), self.suffix_mask)

        two_step_cycles = 1
        iter_max = 5
        threshold = 0.75
        alpha = 0.02
        alpha_first = 0.2
        sigma = 1
        intensity_correction = 1
        isotropic_resolution = 1.02
        v2v_method = "RegAladin"

        cmd_args = []
        cmd_args.append("--filenames %s" % " ".join(self.filenames))
        cmd_args.append("--output %s" % output)
        cmd_args.append("--suffix-mask %s" % self.suffix_mask)
        cmd_args.append("--two-step-cycles %s" % two_step_cycles)
        cmd_args.append("--iter-max %d" % iter_max)
        cmd_args.append("--threshold-first %f" % threshold)
        cmd_args.append("--sigma %f" % sigma)
        cmd_args.append("--threshold %f" % threshold)
        cmd_args.append("--intensity-correction %d" % intensity_correction)
        cmd_args.append("--isotropic-resolution %s" % isotropic_resolution)
        cmd_args.append("--alpha %f" % alpha)
        cmd_args.append("--alpha-first %f" % alpha_first)
        cmd_args.append("--v2v-method %s" % v2v_method)
        # cmd_args.append("--verbose 1")

        cmd = "niftymic_reconstruct_volume %s" % (" ").join(cmd_args)
        self.assertEqual(ph.execute_command(cmd), 0)

        # Check SRR volume
        res_sitk = sitkh.read_nifti_image_sitk(output)
        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference)
        diff_sitk = res_sitk - ref_sitk
        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))
        self.assertAlmostEqual(error, 0, places=self.precision)

        # Check SRR mask volume
        res_sitk = sitkh.read_nifti_image_sitk(
            ph.append_to_filename(output, "_mask"))
        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference_mask)
        diff_sitk = res_sitk - ref_sitk
        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))
        self.assertAlmostEqual(error, 0, places=self.precision)

        # Check 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_reference_mc, t)
            for t in os.listdir(dir_reference_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)
Esempio n. 9
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)
Esempio n. 10
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()
    input_parser.add_moving_mask()
    input_parser.add_dir_input_mc()
    input_parser.add_search_angle(default=180)
    input_parser.add_option(option_string="--initial-transform",
                            type=str,
                            help="Path to initial transform.",
                            default=None)
    input_parser.add_option(
        option_string="--test-ap-flip",
        type=int,
        help="Turn on/off functionality to run an additional registration "
        "after an AP-flip. Seems to be more robust to find a better "
        "registration outcome in general.",
        default=1)
    input_parser.add_option(
        option_string="--use-flirt",
        type=int,
        help="Turn on/off functionality to use FLIRT for the registration.",
        default=1)
    input_parser.add_option(
        option_string="--use-regaladin",
        type=int,
        help="Turn on/off functionality to use RegAladin for the "
        "registration.",
        default=1)
    input_parser.add_verbose(default=0)
    input_parser.add_log_config(default=1)

    args = input_parser.parse_args()
    input_parser.print_arguments(args)

    debug = 0

    if args.log_config:
        input_parser.log_config(os.path.abspath(__file__))

    if not args.use_regaladin and not args.use_flirt:
        raise IOError("Either RegAladin or FLIRT must be activated.")

    if not args.output.endswith(".txt"):
        raise IOError("output transformation path must end in '.txt'")

    dir_output = os.path.dirname(args.output)

    # --------------------------------Read Data--------------------------------
    ph.print_title("Read Data")
    fixed = st.Stack.from_filename(file_path=args.fixed,
                                   file_path_mask=args.fixed_mask,
                                   extract_slices=False)
    moving = st.Stack.from_filename(file_path=args.moving,
                                    file_path_mask=args.moving_mask,
                                    extract_slices=False)

    if args.initial_transform is not None:
        transform_sitk = sitkh.read_transform_sitk(args.initial_transform)
    else:
        transform_sitk = sitk.AffineTransform(fixed.sitk.GetDimension())
    sitk.WriteTransform(transform_sitk, args.output)

    path_to_tmp_output = os.path.join(
        DIR_TMP, ph.append_to_filename(os.path.basename(args.moving),
                                       "_warped"))

    # -------------------Register Reconstruction to Template-------------------
    ph.print_title("Register Reconstruction to Template")

    if args.use_flirt:
        path_to_transform_flirt = os.path.join(DIR_TMP, "transform_flirt.txt")

        # Convert SimpleITK into FLIRT transform
        cmd = "simplereg_transform -sitk2flirt %s %s %s %s" % (
            args.output, args.fixed, args.moving, path_to_transform_flirt)
        ph.execute_command(cmd, verbose=False)

        # Define search angle ranges for FLIRT in all three dimensions
        search_angles = [
            "-searchr%s -%d %d" % (x, args.search_angle, args.search_angle)
            for x in ["x", "y", "z"]
        ]

        # flt = nipype.interfaces.fsl.FLIRT()
        # flt.inputs.in_file = args.moving
        # flt.inputs.reference = args.fixed
        # if args.initial_transform is not None:
        #     flt.inputs.in_matrix_file = path_to_transform_flirt
        # flt.inputs.out_matrix_file = path_to_transform_flirt
        # # flt.inputs.output_type = "NIFTI_GZ"
        # flt.inputs.out_file = path_to_tmp_output
        # flt.inputs.args = "-dof 6"
        # flt.inputs.args += " %s" % " ".join(search_angles)
        # if args.moving_mask is not None:
        #     flt.inputs.in_weight = args.moving_mask
        # if args.fixed_mask is not None:
        #     flt.inputs.ref_weight = args.fixed_mask
        # ph.print_info("Run Registration (FLIRT) ... ", newline=False)
        # flt.run()
        # print("done")

        cmd_args = ["flirt"]
        cmd_args.append("-in %s" % args.moving)
        cmd_args.append("-ref %s" % args.fixed)
        if args.initial_transform is not None:
            cmd_args.append("-init %s" % path_to_transform_flirt)
        cmd_args.append("-omat %s" % path_to_transform_flirt)
        cmd_args.append("-out %s" % path_to_tmp_output)
        cmd_args.append("-dof 6")
        cmd_args.append((" ").join(search_angles))
        if args.moving_mask is not None:
            cmd_args.append("-inweight %s" % args.moving_mask)
        if args.fixed_mask is not None:
            cmd_args.append("-refweight %s" % args.fixed_mask)
        ph.print_info("Run Registration (FLIRT) ... ", newline=False)
        ph.execute_command(" ".join(cmd_args), verbose=False)
        print("done")

        # Convert FLIRT to SimpleITK transform
        cmd = "simplereg_transform -flirt2sitk %s %s %s %s" % (
            path_to_transform_flirt, args.fixed, args.moving, args.output)
        ph.execute_command(cmd, verbose=False)

        if debug:
            ph.show_niftis([args.fixed, path_to_tmp_output])

    # Additionally, use RegAladin for more accurate alignment
    # Rationale: FLIRT has better capture range, but RegAladin seems to
    # find better alignment once it is within its capture range.
    if args.use_regaladin:
        path_to_transform_regaladin = os.path.join(DIR_TMP,
                                                   "transform_regaladin.txt")

        # Convert SimpleITK to RegAladin transform
        cmd = "simplereg_transform -sitk2nreg %s %s" % (
            args.output, path_to_transform_regaladin)
        ph.execute_command(cmd, verbose=False)

        # nreg = nipype.interfaces.niftyreg.RegAladin()
        # nreg.inputs.ref_file = args.fixed
        # nreg.inputs.flo_file = args.moving
        # nreg.inputs.res_file = path_to_tmp_output
        # nreg.inputs.in_aff_file = path_to_transform_regaladin
        # nreg.inputs.aff_file = path_to_transform_regaladin
        # nreg.inputs.args = "-rigOnly -voff"
        # if args.moving_mask is not None:
        #     nreg.inputs.fmask_file = args.moving_mask
        # if args.fixed_mask is not None:
        #     nreg.inputs.rmask_file = args.fixed_mask
        # ph.print_info("Run Registration (RegAladin) ... ", newline=False)
        # nreg.run()
        # print("done")

        cmd_args = ["reg_aladin"]
        cmd_args.append("-ref %s" % args.fixed)
        cmd_args.append("-flo %s" % args.moving)
        cmd_args.append("-res %s" % path_to_tmp_output)
        if args.initial_transform is not None or args.use_flirt == 1:
            cmd_args.append("-inaff %s" % path_to_transform_regaladin)
        cmd_args.append("-aff %s" % path_to_transform_regaladin)
        # cmd_args.append("-cog")
        # cmd_args.append("-ln 2")
        cmd_args.append("-rigOnly")
        cmd_args.append("-voff")
        if args.moving_mask is not None:
            cmd_args.append("-fmask %s" % args.moving_mask)
        if args.fixed_mask is not None:
            cmd_args.append("-rmask %s" % args.fixed_mask)
        ph.print_info("Run Registration (RegAladin) ... ", newline=False)
        ph.execute_command(" ".join(cmd_args), verbose=False)
        print("done")

        # Convert RegAladin to SimpleITK transform
        cmd = "simplereg_transform -nreg2sitk %s %s" % (
            path_to_transform_regaladin, args.output)
        ph.execute_command(cmd, verbose=False)

        if debug:
            ph.show_niftis([args.fixed, path_to_tmp_output])

    if args.test_ap_flip:
        path_to_transform_flip = os.path.join(DIR_TMP, "transform_flip.txt")
        path_to_tmp_output_flip = os.path.join(DIR_TMP, "output_flip.nii.gz")

        # Get AP-flip transform
        transform_ap_flip_sitk = get_ap_flip_transform(args.fixed)
        path_to_transform_flip_regaladin = os.path.join(
            DIR_TMP, "transform_flip_regaladin.txt")
        sitk.WriteTransform(transform_ap_flip_sitk, path_to_transform_flip)

        # Compose current transform with AP flip transform
        cmd = "simplereg_transform -c %s %s %s" % (
            args.output, path_to_transform_flip, path_to_transform_flip)
        ph.execute_command(cmd, verbose=False)

        # Convert SimpleITK to RegAladin transform
        cmd = "simplereg_transform -sitk2nreg %s %s" % (
            path_to_transform_flip, path_to_transform_flip_regaladin)
        ph.execute_command(cmd, verbose=False)

        # nreg = nipype.interfaces.niftyreg.RegAladin()
        # nreg.inputs.ref_file = args.fixed
        # nreg.inputs.flo_file = args.moving
        # nreg.inputs.res_file = path_to_tmp_output_flip
        # nreg.inputs.in_aff_file = path_to_transform_flip_regaladin
        # nreg.inputs.aff_file = path_to_transform_flip_regaladin
        # nreg.inputs.args = "-rigOnly -voff"
        # if args.moving_mask is not None:
        #     nreg.inputs.fmask_file = args.moving_mask
        # if args.fixed_mask is not None:
        #     nreg.inputs.rmask_file = args.fixed_mask
        # ph.print_info("Run Registration AP-flipped (RegAladin) ... ",
        #               newline=False)
        # nreg.run()
        # print("done")

        cmd_args = ["reg_aladin"]
        cmd_args.append("-ref %s" % args.fixed)
        cmd_args.append("-flo %s" % args.moving)
        cmd_args.append("-res %s" % path_to_tmp_output_flip)
        cmd_args.append("-inaff %s" % path_to_transform_flip_regaladin)
        cmd_args.append("-aff %s" % path_to_transform_flip_regaladin)
        cmd_args.append("-rigOnly")
        # cmd_args.append("-ln 2")
        cmd_args.append("-voff")
        if args.moving_mask is not None:
            cmd_args.append("-fmask %s" % args.moving_mask)
        if args.fixed_mask is not None:
            cmd_args.append("-rmask %s" % args.fixed_mask)
        ph.print_info("Run Registration AP-flipped (RegAladin) ... ",
                      newline=False)
        ph.execute_command(" ".join(cmd_args), verbose=False)
        print("done")

        if debug:
            ph.show_niftis(
                [args.fixed, path_to_tmp_output, path_to_tmp_output_flip])

        warped_moving = st.Stack.from_filename(path_to_tmp_output,
                                               extract_slices=False)
        warped_moving_flip = st.Stack.from_filename(path_to_tmp_output_flip,
                                                    extract_slices=False)
        fixed = st.Stack.from_filename(args.fixed, args.fixed_mask)

        stacks = [warped_moving, warped_moving_flip]
        image_similarity_evaluator = ise.ImageSimilarityEvaluator(
            stacks=stacks, reference=fixed)
        image_similarity_evaluator.compute_similarities()
        similarities = image_similarity_evaluator.get_similarities()

        if similarities["NMI"][1] > similarities["NMI"][0]:
            ph.print_info("AP-flipped outcome better")

            # Convert RegAladin to SimpleITK transform
            cmd = "simplereg_transform -nreg2sitk %s %s" % (
                path_to_transform_flip_regaladin, args.output)
            ph.execute_command(cmd, verbose=False)

            # Copy better outcome
            cmd = "cp -p %s %s" % (path_to_tmp_output_flip, path_to_tmp_output)
            ph.execute_command(cmd, verbose=False)

        else:
            ph.print_info("AP-flip does not improve outcome")

    if args.dir_input_mc is not None:
        transform_sitk = sitkh.read_transform_sitk(args.output, inverse=1)

        if args.dir_input_mc.endswith("/"):
            subdir_mc = args.dir_input_mc.split("/")[-2]
        else:
            subdir_mc = args.dir_input_mc.split("/")[-1]
        dir_output_mc = os.path.join(dir_output, subdir_mc)

        ph.create_directory(dir_output_mc, delete_files=True)
        pattern = REGEX_FILENAMES + "[.]tfm"
        p = re.compile(pattern)
        trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)]
        for t in trafos:
            path_to_input_transform = os.path.join(args.dir_input_mc, t)
            path_to_output_transform = os.path.join(dir_output_mc, t)
            t_sitk = sitkh.read_transform_sitk(path_to_input_transform)
            t_sitk = sitkh.get_composite_sitk_affine_transform(
                transform_sitk, t_sitk)
            sitk.WriteTransform(t_sitk, path_to_output_transform)

    if args.verbose:
        ph.show_niftis([args.fixed, path_to_tmp_output])

    elapsed_time_total = ph.stop_timing(time_start)

    # Summary
    ph.print_title("Summary")
    print("Computational Time: %s" % (elapsed_time_total))

    return 0