def _register(_moving, _parameters, _output_path, _use_mask=None): if isinstance(_parameters, str): _parameters = [_parameters] if _use_mask is None: _use_mask = target_mask is not None or moving_mask is not None _output_path = os.path.abspath(_output_path) os.makedirs(_output_path, exist_ok=True) elastix_path = _local_exe("elastix") cwd = _local_lib_dir() reg = Registration() if elastix_path: reg._cmd = elastix_path reg.inputs.fixed_image = os.path.abspath(target) reg.inputs.moving_image = os.path.abspath(_moving) reg.inputs.parameters = [os.path.abspath(p) for p in _parameters] reg.inputs.output_path = os.path.abspath(_output_path) reg.terminal_output = preferences.nipype_logging if num_threads: reg.inputs.num_threads = num_threads if _use_mask and target_mask is not None: reg.inputs.fixed_mask = os.path.abspath(target_mask) if _use_mask and moving_mask is not None: reg.inputs.target_mask = os.path.abspath(moving_mask) for k, v in kwargs.items(): setattr(reg.inputs, k, v) return reg.run(cwd=cwd).outputs
def __intraregister__(self, volumes: List[MedicalVolume]): """ Intraregister different subvolumes to ensure they are in the same space during computation :param volumes: a list of MedicalVolumes :return: """ if (not volumes) or (type(volumes) is not list) or (len(volumes) != __EXPECTED_NUM_ECHO_TIMES__): raise TypeError('volumes must be of type List[MedicalVolume]') num_echos = len(volumes) print('') print('==' * 40) print('Intraregistering...') print('==' * 40) # temporarily save subvolumes as nifti file raw_volumes_base_path = io_utils.check_dir(os.path.join(self.temp_path, 'raw')) # Use first subvolume as a basis for registration - save in nifti format to use with elastix/transformix volume_files = [] for echo_index in range(num_echos): filepath = os.path.join(raw_volumes_base_path, '%03d' % echo_index + '.nii.gz') volume_files.append(filepath) volumes[echo_index].save_volume(filepath, data_format=ImageDataFormat.nifti) target_echo_index = 0 target_image_filepath = volume_files[target_echo_index] nr = NiftiReader() intraregistered_volumes = [deepcopy(volumes[target_echo_index])] for echo_index in range(1, num_echos): moving_image = volume_files[echo_index] reg = Registration() reg.inputs.fixed_image = target_image_filepath reg.inputs.moving_image = moving_image reg.inputs.output_path = io_utils.check_dir(os.path.join(self.temp_path, 'intraregistered', '%03d' % echo_index)) reg.inputs.parameters = [fc.ELASTIX_AFFINE_PARAMS_FILE] reg.terminal_output = fc.NIPYPE_LOGGING print('Registering %s --> %s' % (str(echo_index), str(target_echo_index))) tmp = reg.run() warped_file = tmp.outputs.warped_file intrareg_vol = nr.load(warped_file) # copy affine from original volume, because nifti changes loading accuracy intrareg_vol = MedicalVolume(volume=intrareg_vol.volume, affine=volumes[echo_index].affine, headers=deepcopy(volumes[echo_index].headers)) intraregistered_volumes.append(intrareg_vol) self.raw_volumes = deepcopy(volumes) self.volumes = intraregistered_volumes
def __interregister_base_file__( self, base_image_info: tuple, target_path: str, temp_path: str, mask_path: str = None, parameter_files=(fc.ELASTIX_RIGID_PARAMS_FILE, fc.ELASTIX_AFFINE_PARAMS_FILE)): """Interregister the base moving image to the target image :param base_image_info: tuple of filepath, echo index (eg. 'scans/000.nii.gz, 0) :param target_path: filepath to target scan - should be in nifti (.nii.gz) format :param temp_path: path to store temporary data :param mask_path: path to mask to use to use as focus points for registration, mask must be binary recommend that this is the path to a dilated version of the mask for registration purposes :param parameter_files: list of filepaths to elastix parameter files to use for transformations :return: tuple of the path to the transformed moving image and a list of filepaths to elastix transformations (e.g. '/result.nii.gz', ['/tranformation0.txt', '/transformation1.txt']) """ base_image_path, base_time_id = base_image_info # Register base image to the target image print('Registering %s (base image)' % base_image_path) transformation_files = [] use_mask_arr = [False, True] reg_output = None moving_image = base_image_path for i in range(len(parameter_files)): use_mask = use_mask_arr[i] pfile = parameter_files[i] reg = Registration() reg.inputs.fixed_image = target_path reg.inputs.moving_image = moving_image reg.inputs.output_path = io_utils.check_dir( os.path.join(temp_path, '%03d_param%i' % (base_time_id, i))) reg.inputs.parameters = pfile if use_mask and mask_path is not None: fixed_mask_filepath = self.__dilate_mask__( mask_path, temp_path) reg.inputs.fixed_mask = fixed_mask_filepath reg.terminal_output = fc.NIPYPE_LOGGING reg_output = reg.run() reg_output = reg_output.outputs assert reg_output is not None # update moving image to output moving_image = reg_output.warped_file transformation_files.append(reg_output.transform[0]) return reg_output.warped_file, transformation_files
def __interregister_base_file__(self, base_image_info: tuple, target_path: str, temp_path: str, mask_path: str = None, parameter_files=(fc.ELASTIX_RIGID_PARAMS_FILE, fc.ELASTIX_AFFINE_PARAMS_FILE)): """Interregister the base moving image to the target image. Args: base_image_info (tuple[str, int]): File path, echo index (eg. 'scans/000.nii.gz, 0). target_path (str): File path to target scan. Must be in nifti (.nii.gz) format. temp_path (str): Directory path to store temporary data. mask_path (str): Path to mask to use to use as focus points for registration. Mask must be binary. Recommend using dilated mask. parameter_files (list[str]): Transformix parameter files to use for transformations. Returns: tuple[str, list[str]): File path to the transformed moving image and a list of file paths to elastix transformations (e.g. '/result.nii.gz', ['/tranformation0.txt', '/transformation1.txt']). """ base_image_path, base_time_id = base_image_info # Register base image to the target image. logging.info("Registering %s (base image)".format(base_image_path)) transformation_files = [] use_mask_arr = [False, True] reg_output = None moving_image = base_image_path for i in range(len(parameter_files)): use_mask = use_mask_arr[i] pfile = parameter_files[i] reg = Registration() reg.inputs.fixed_image = target_path reg.inputs.moving_image = moving_image reg.inputs.output_path = io_utils.mkdirs(os.path.join(temp_path, '{:03d}_param{}'.format(base_time_id, i))) reg.inputs.parameters = pfile if use_mask and mask_path is not None: fixed_mask_filepath = self.__dilate_mask__(mask_path, temp_path) reg.inputs.fixed_mask = fixed_mask_filepath reg.terminal_output = fc.NIPYPE_LOGGING reg_output = reg.run() reg_output = reg_output.outputs assert reg_output is not None # Update moving image to output. moving_image = reg_output.warped_file transformation_files.append(reg_output.transform[0]) return reg_output.warped_file, transformation_files
def __intraregister__(self, volumes: List[MedicalVolume]): """Intraregister volumes. Sets `self.volumes` to intraregistered volumes. Args: volumes (list[MedicalVolume]): Volumes to register. Raises: TypeError: If `volumes` is not `list[MedicalVolume]`. """ if ((not volumes) or (type(volumes) is not list) or (len(volumes) != __EXPECTED_NUM_ECHO_TIMES__)): raise TypeError("`volumes` must be of type List[MedicalVolume]") num_echos = len(volumes) logging.info("") logging.info("==" * 40) logging.info("Intraregistering...") logging.info("==" * 40) # temporarily save subvolumes as nifti file raw_volumes_base_path = io_utils.mkdirs( os.path.join(self.temp_path, "raw")) # Use first subvolume as a basis for registration # Save in nifti format to use with elastix/transformix volume_files = [] for echo_index in range(num_echos): filepath = os.path.join(raw_volumes_base_path, "{:03d}.nii.gz".format(echo_index)) volume_files.append(filepath) volumes[echo_index].save_volume(filepath, data_format=ImageDataFormat.nifti) target_echo_index = 0 target_image_filepath = volume_files[target_echo_index] nr = NiftiReader() intraregistered_volumes = [deepcopy(volumes[target_echo_index])] for echo_index in range(1, num_echos): moving_image = volume_files[echo_index] reg = Registration() reg.inputs.fixed_image = target_image_filepath reg.inputs.moving_image = moving_image reg.inputs.output_path = io_utils.mkdirs( os.path.join(self.temp_path, "intraregistered", "{:03d}".format(echo_index))) reg.inputs.parameters = [fc.ELASTIX_AFFINE_PARAMS_FILE] reg.terminal_output = preferences.nipype_logging logging.info("Registering {} -> {}".format(str(echo_index), str(target_echo_index))) tmp = reg.run() warped_file = tmp.outputs.warped_file intrareg_vol = nr.load(warped_file) # copy affine from original volume, because nifti changes loading accuracy intrareg_vol = MedicalVolume( volume=intrareg_vol.volume, affine=volumes[echo_index].affine, headers=deepcopy(volumes[echo_index].headers()), ) intraregistered_volumes.append(intrareg_vol) self.volumes = intraregistered_volumes
def __intraregister__(self, subvolumes): """Intraregister cubequant subvolumes to each other Patient could have moved between acquisition of different subvolumes, so different subvolumes of cubequant scan have to be registered with each other The first spin lock time has the highest SNR, so it is used as the target Subvolumes corresponding to the other spin lock times are registered to the target Affine registration is done using elastix :param subvolumes: dictionary of subvolumes mapping spin lock time index --> MedicalVolume (e.g. {0 --> MedicalVolume A, 1 --> MedicalVolume B} :return: a dictionary of base, other files spin-lock index --> nifti filepath """ if subvolumes is None: raise ValueError('subvolumes must be dict()') print('') print('==' * 40) print('Intraregistering...') print('==' * 40) # temporarily save subvolumes as nifti file ordered_spin_lock_time_indices = natsorted(list(subvolumes.keys())) raw_volumes_base_path = io_utils.check_dir( os.path.join(self.temp_path, 'raw')) # Use first spin lock time as a basis for registration spin_lock_nii_files = [] for spin_lock_time_index in ordered_spin_lock_time_indices: filepath = os.path.join(raw_volumes_base_path, '%03d' % spin_lock_time_index + '.nii.gz') spin_lock_nii_files.append(filepath) subvolumes[spin_lock_time_index].save_volume(filepath) target_filepath = spin_lock_nii_files[0] intraregistered_files = [] for i in range(1, len(spin_lock_nii_files)): spin_file = spin_lock_nii_files[i] spin_lock_time_index = ordered_spin_lock_time_indices[i] reg = Registration() reg.inputs.fixed_image = target_filepath reg.inputs.moving_image = spin_file reg.inputs.output_path = io_utils.check_dir( os.path.join(self.temp_path, 'intraregistered', '%03d' % spin_lock_time_index)) reg.inputs.parameters = [fc.ELASTIX_AFFINE_PARAMS_FILE] reg.terminal_output = fc.NIPYPE_LOGGING print('Registering %s --> %s' % (str(spin_lock_time_index), str(ordered_spin_lock_time_indices[0]))) tmp = reg.run() warped_file = tmp.outputs.warped_file intraregistered_files.append((spin_lock_time_index, warped_file)) return { 'BASE': (ordered_spin_lock_time_indices[0], spin_lock_nii_files[0]), 'FILES': intraregistered_files }
def __intraregister__(self, subvolumes): """Intra-register volumes. Patient could have moved between acquisition of different volumes, so different volumes of CubeQuant scan have to be registered with each other. The first spin lock time has the highest SNR, so it is used as the target. Volumes corresponding to the other spin lock times are registered to the target. Affine registration is done using Elastix. Args: subvolumes (dict[int, MedicalVolume]): Dictionary of spin lock time index -> volume. (e.g. {0 --> MedicalVolume A, 1 --> MedicalVolume B}). Returns: dict[int, str]: Dictionary of base, other files spin-lock index -> output nifti file path. """ if subvolumes is None: raise TypeError("subvolumes must be dict") logging.info("") logging.info("==" * 40) logging.info("Intraregistering...") logging.info("==" * 40) # temporarily save subvolumes as nifti file ordered_spin_lock_time_indices = natsorted(list(subvolumes.keys())) raw_volumes_base_path = io_utils.mkdirs( os.path.join(self.temp_path, "raw")) # Use first spin lock time as a basis for registration spin_lock_nii_files = [] for spin_lock_time_index in ordered_spin_lock_time_indices: filepath = os.path.join( raw_volumes_base_path, "{:03d}.nii.gz".format(spin_lock_time_index)) spin_lock_nii_files.append(filepath) subvolumes[spin_lock_time_index].save_volume(filepath) target_filepath = spin_lock_nii_files[0] intraregistered_files = [] for i in range(1, len(spin_lock_nii_files)): spin_file = spin_lock_nii_files[i] spin_lock_time_index = ordered_spin_lock_time_indices[i] reg = Registration() reg.inputs.fixed_image = target_filepath reg.inputs.moving_image = spin_file reg.inputs.output_path = io_utils.mkdirs( os.path.join(self.temp_path, "intraregistered", "{:03d}".format(spin_lock_time_index))) reg.inputs.parameters = [fc.ELASTIX_AFFINE_PARAMS_FILE] reg.terminal_output = fc.NIPYPE_LOGGING logging.info("Registering {} -> {}".format( str(spin_lock_time_index), str(ordered_spin_lock_time_indices[0]))) tmp = reg.run() warped_file = tmp.outputs.warped_file intraregistered_files.append((spin_lock_time_index, warped_file)) return { "BASE": (ordered_spin_lock_time_indices[0], spin_lock_nii_files[0]), "FILES": intraregistered_files }