def apply_transformation_workflow(moving_file, output_file, output_transform_file=None): """ """ moving_image = pos_itk_transforms.read_itk_image(moving_file) translation, rotation = get_random_rigid_3d_tranform_parameters( tmean=CONFIG['tmean'], tsigma=CONFIG['tsigma'], rmean=CONFIG['rmean'], rsigma=CONFIG['rsigma']) vol_transform = itk.Euler3DTransform.D.New() vol_transform.SetTranslation([0, 0, 0]) vol_transform.SetRotation(*list(rotation)) center = [0, 0, 0] center[0] = moving_image.GetLargestPossibleRegion().GetSize()[0]/2 center[1] = moving_image.GetLargestPossibleRegion().GetSize()[1]/2 center[2] = moving_image.GetLargestPossibleRegion().GetSize()[2]/2 center = map(int, center) # Sometimes the indexes are returned as long ints phisycal_center = moving_image.TransformIndexToPhysicalPoint(center) vol_transform.SetCenter(phisycal_center) resliced_image = pos_itk_transforms.reslice_image([vol_transform], moving_image, default_pixel_value=CONFIG['default_pixel_value']) pos_itk_transforms.write_itk_image(resliced_image, output_file) if output_transform_file: write_itk_matrix_transformation_to_file(vol_transform, output_transform_file)
def _process_single_section(self, section, section_index): """ Applies relevant transformations to a single two dimensional image (aka a section). This method actually extracts an image from the 3D stack and only then reslices it. Therefore we need to provide the method with the actual section index to process. :param section: Two dimensional image to be resliced. :type section: `itk.Image` :param section_index: Index of the section to be extracted from the three-dimensional image stack and to be resliced. Note that this comes as section index as to be passed to the filename templates no the one starting from zero. The section index which starts from zero will be calculated automatically. :type section_index: int :return: Image resliced with appropriate transformations. :rtype: `itk.Image` """ # This is super irritating, but yes, we have to # convert the section index back to plane_index # to propoerly define an itk region. input_region = self._get_extration_region(section_index, section) extract_single_section = \ itk.ExtractImageFilter[ (self._processing_type, self._section_type)].New() extract_single_section.SetExtractionRegion(input_region) extract_single_section.SetInput(section) extract_single_section.SetDirectionCollapseToIdentity() extract_single_section.Update() section_transform = self.section_transform(section_index) interpolator = \ self._build_interpolator(extract_single_section.GetOutput()) transformed_image = pos_itk_transforms.reslice_image( section_transform, extract_single_section.GetOutput(), interpolator=interpolator) return transformed_image
def process_single_component(self, single_component_image): """ This is where things get a bit more complicated. What happend here is that, depending on the direction of processing (either forward or reverse). When we map data from the raw image space to the atlas space, the coregistration with the atlas is conducted after the (affine and deformable) reconstruction step. When we map data from the atlas space to the raw image space, the mapping to the raw image is conducted as the first stage of processing before inverse mapping from the deformable reconstruction to the raw image space. This method handles mapping in both directions therefore its implementation may look a bit convoluted. :param single_component_image: The three-dimensional image to be processed. The provided image has to be three-dimensional. :type single_component_image: `itk.Image` class instance. :return: Provided single component image processed according to provided options. :rtype: `itk.Image` """ if self.direction == C_DIR_INV: interpolator = self._build_interpolator(single_component_image) input_image = pos_itk_transforms.reslice_image( self._coregistration_transform, single_component_image, self._fixed_image, interpolator=interpolator) elif self.direction == C_DIR_FWD: input_image = single_component_image # Determine the type of the single section. The type of the single # section is determined based on the type of the 3D image. self._section_type = \ pos_itk_core.types_reduced_dimensions[self._processing_type] # This array will hold individual sections once they are processed. collect_sections = [] # Here we determine the number of individual 2D sections to be # processed. The number of sections is determined based on the provided # slicing axis. sections_number = \ input_image.GetLargestPossibleRegion().GetSize()[\ self.options.slicing_axis] sections_range = \ range(self.options.offset, sections_number + self.options.offset) self._logger.info("Determined number of sections: %d.", sections_number) self._logger.info("Indexes of sections to be processed: %s.", " ".join(map(str, sections_range))) self._inspect_input_images(sections_range) # Nothing special going one, we process each section one # by one. for section_index in sections_range: collect_sections.append( self._process_single_section(input_image, section_index)) # Once all sections are processed, we merge them back into # 3D image. Pretty cool, huh... cast_to_volume = itk.JoinSeriesImageFilter[ (self._section_type, self._processing_type)].New() for i, section in enumerate(collect_sections): cast_to_volume.SetInput(i, section) cast_to_volume.Update() # Then we reorient the 3D iamge and we set all the parameters of the # volume so it matches exactly the input stack. permutation_filter = \ itk.PermuteAxesImageFilter[cast_to_volume.GetOutput()].New() permutation_filter.SetInput(cast_to_volume) permutation_filter.SetOrder( self._slicing_planes_settings[ self.options.slicing_axis]['permutation']) permutation_filter.Update() change_stack_information = \ itk.ChangeInformationImageFilter[(self._processing_type,)].New() change_stack_information.ChangeDirectionOn() change_stack_information.ChangeOriginOn() change_stack_information.ChangeSpacingOn() change_stack_information.UseReferenceImageOn() change_stack_information.SetReferenceImage(input_image) change_stack_information.SetInput(permutation_filter) change_stack_information.Update() # Again, depending on the direction of mapping, we either conduct # mapping to the atlas space or not. if self.direction == C_DIR_INV: return change_stack_information.GetOutput() elif self.direction == C_DIR_FWD: interpolator = self._build_interpolator( change_stack_information.GetOutput()) coregistered_to_atlas = pos_itk_transforms.reslice_image( self._coregistration_transform, change_stack_information.GetOutput(), self._fixed_image, interpolator=interpolator) return coregistered_to_atlas