def test_make_correspondence(): import hasi.align # Load meshes atlas_mesh = itk.meshread(OUTPUT_DIRECTORY + MESH_DIRECTORY + 'atlas.vtk') meshes = list() for idx in range(len(IMAGE_FILES)): mesh_file = IMAGE_FILES[idx].replace('.nrrd', '.vtk') meshes.append( itk.meshread(OUTPUT_DIRECTORY + MESH_DIRECTORY + mesh_file, itk.F)) # Get mesh correspondence for idx in range(len(meshes)): mesh = meshes[idx] registered_atlas = hasi.align.register_template_to_sample( template_mesh=atlas_mesh, sample_mesh=mesh, max_iterations=500) mesh = hasi.align.resample_template_from_target( template_mesh=registered_atlas, target_mesh=mesh) meshes[idx] = mesh # Write out for shape analysis for idx in range(len(IMAGE_FILES)): mesh = meshes[idx] mesh_file = IMAGE_FILES[idx].replace('.nrrd', '.vtk') itk.meshwrite(mesh, OUTPUT_DIRECTORY + CORRESPONDENCE_DIRECTORY + mesh_file)
def test_create_atlas(): import hasi.align # Read in images images = list() for image_file in IMAGE_FILES: image_name = str(Path(image_file).stem) cid = DATA_INDEX[image_name] store = IPFS_FS.get_mapper(f'ipfs://{cid}') image_ds = xr.open_zarr(store) image_da = image_ds[image_name] image = itk.image_from_xarray(image_da) images.append(image) # Paste into standard space images = hasi.align.paste_to_common_space(images) # Downsample template image TEMPLATE_IDX = 0 SPARSE_DOWNSAMPLE_RATIO = 14 template_image = \ hasi.align.downsample_images([images[TEMPLATE_IDX]],SPARSE_DOWNSAMPLE_RATIO)[0] # Downsample dense images DENSE_DOWNSAMPLE_RATIO = 2 images = hasi.align.downsample_images(images, DENSE_DOWNSAMPLE_RATIO) # Generate initial template mesh FEMUR_OBJECT_PIXEL_VALUE = 1 template_mesh = hasi.align.binary_image_list_to_meshes( [template_image], object_pixel_value=FEMUR_OBJECT_PIXEL_VALUE)[0] # Generate meshes meshes = hasi.align.binary_image_list_to_meshes( images, object_pixel_value=FEMUR_OBJECT_PIXEL_VALUE) # Write out sample meshes for later shape analysis for idx in range(len(IMAGE_FILES)): mesh_file = IMAGE_FILES[idx].replace('.nrrd', '.vtk') mesh = meshes[idx] itk.meshwrite(mesh, OUTPUT_DIRECTORY + MESH_DIRECTORY + mesh_file) # Iteratively refine atlas NUM_ITERATIONS = 3 for iteration in range(NUM_ITERATIONS): updated_mesh = hasi.align.refine_template_from_population( template_mesh=template_mesh, target_meshes=meshes, registration_iterations=200) distance = hasi.align.get_pairwise_hausdorff_distance( updated_mesh, template_mesh) template_mesh = updated_mesh # Verify final alignment distance assert 0.1 < distance < 0.5 itk.meshwrite(updated_mesh, OUTPUT_DIRECTORY + MESH_DIRECTORY + 'atlas.vtk')
def register(self, template_mesh: MeshType, target_mesh: MeshType, filepath: str = None, verbose=False, max_iterations=MAX_ITERATIONS) -> (TransformType, MeshType): template_image = self.mesh_to_image(template_mesh) target_image = self.mesh_to_image(target_mesh, template_image) self.filter.SetFixedImage(template_image) self.filter.SetMovingImage(target_image) self.filter.SetNumberOfIterations(max_iterations) def print_iteration(): metric = self.filter.GetMetric() print(f'{metric}') if (verbose): self.filter.AddObserver(itk.ProgressEvent(), print_iteration) # Run registration self.filter.Update() # Update template mesh to match target transform = self.TransformType.New() transform.SetDisplacementField(self.filter.GetOutput()) # TODO pythonic style TransformFilterType = \ itk.TransformMeshFilter[self.MeshType, self.MeshType, itk.Transform[itk.F,self.Dimension,self.Dimension]] transform_filter = TransformFilterType.New(Input=template_mesh, Transform=transform) transform_filter.Update() if filepath is not None: itk.meshwrite(transform_filter.GetOutput(), filepath) if (verbose): print(f'Wrote resulting mesh to {filepath}') return (transform, transform_filter.GetOutput())
raise Exception("`itk.imread()` fallback_only should have failed") except Exception as e: if str(e) == "pixel_type must be set when using the fallback_only option": pass else: raise e # test mesh read / write mesh = itk.meshread(mesh_filename) assert type(mesh) == itk.Mesh[itk.F, 3] mesh = itk.meshread(mesh_filename, itk.UC) assert type(mesh) == itk.Mesh[itk.UC, 3] mesh = itk.meshread(mesh_filename, itk.UC, fallback_only=True) assert type(mesh) == itk.Mesh[itk.F, 3] itk.meshwrite(mesh, sys.argv[4]) itk.meshwrite(mesh, sys.argv[4], compression=True) # test search res = itk.search("Index") assert res[0] == "Index" assert res[1] == "index" assert "ContinuousIndex" in res res = itk.search("index", True) assert "Index" not in res # test down_cast obj = itk.Object.cast(reader) # be sure that the reader is casted to itk::Object assert obj.__class__ == itk.Object
def register(self, template_mesh: MeshType, target_mesh: MeshType, filepath: str = None, verbose=False, num_iterations=MAX_ITERATIONS) -> (TransformType, MeshType): template_image = self.mesh_to_image(template_mesh) target_image = self.mesh_to_image(target_mesh, template_image) ImageType = type(template_image) Dimension = template_image.GetImageDimension() metric = itk.MeanSquaresImageToImageMetricv4[ImageType, ImageType].New() # Set physical dimensions for transform fixed_physical_dimensions = list() fixed_origin = list(template_image.GetOrigin()) for i in range(0, Dimension): fixed_physical_dimensions.append(template_image.GetSpacing()[i] * \ (template_image.GetLargestPossibleRegion().GetSize()[i] - 1)) transform = self.TransformType.New( TransformDomainOrigin=fixed_origin, TransformDomainPhysicalDimensions=fixed_physical_dimensions, TransformDomainDirection=template_image.GetDirection()) transform.SetTransformDomainMeshSize( [self.GRID_NODES_IN_ONE_DIMENSION - self.V_SPLINE_ORDER] * Dimension) # Transform parameters unbounded for typical registration problem number_of_parameters = transform.GetNumberOfParameters() self.optimizer.SetBoundSelection([0] * number_of_parameters) self.optimizer.SetUpperBound([0] * number_of_parameters) self.optimizer.SetLowerBound([0] * number_of_parameters) # Monitor optimization via observer def print_iteration(): iteration = self.optimizer.GetCurrentIteration() metric = self.optimizer.GetCurrentMetricValue() infnorm = self.optimizer.GetInfinityNormOfProjectedGradient() print(f'{iteration} {metric} {infnorm}') if (verbose): self.optimizer.AddObserver(itk.IterationEvent(), print_iteration) self.optimizer.SetNumberOfIterations(num_iterations) # Define object to handle image registration RegistrationType = \ itk.ImageRegistrationMethodv4[ImageType,ImageType] registration = RegistrationType.New( InitialTransform=transform, FixedImage=template_image, MovingImage=target_image, Metric=metric, Optimizer=self.optimizer, NumberOfLevels=self.NUMBER_OF_LEVELS, ShrinkFactorsPerLevel=self.SHRINK_FACTORS_PER_LEVEL) registration.SetSmoothingSigmasPerLevel( self.SMOOTHING_SIGMAS_PER_LEVEL) registration.InPlaceOn() # Run registration # NOTE ignore warning: "LBFGSBOptimizer does not support # scaling, all scales set to one" # Registration likely attempts to set scales by default, # no observed impact on performance from this warning registration.Update() # TODO functional interface # image_registration_method() # Report results if (verbose): print('Solution = ' + str(list(transform.GetParameters()))) # Update fixed image # TODO try inverting moving transform and compare results transformed_mesh = itk.transform_mesh_filter(template_mesh, transform=transform) # Write out if filepath is not None: itk.meshwrite(transformed_mesh, filepath) if (verbose): print(f'Wrote resulting mesh to {filepath}') return (transform, transformed_mesh)