def find_image_shifts(data, options, photosensor=0, fixed_idx=12): """ Register all images in an ISM ArrayDetectorData dataset. The central image (pixel 12) is used as reference and other images are aligned with it. This function used an iterative algorithm (ITK) that can be customized with various command line options. :param options: Various options that can be used on fine tune the image registration. Look into supertomo_options.py file :param data: ArrayDetectorData object with all the individual images :param photosensor: The photosensor number (from 0 upwards) that is to be processed :param fixed_idx: The index of the reference image. Defaults to 12 (IIT SPAD array) :return: a three element tuple: x offset, y offset, transforms. The x and y offsets are expressed in physical units (um). The transforms are sitk.TranslationTransform objects that can be used to resample the images into a common coordinate system. """ assert isinstance(data, ArrayDetectorData) assert photosensor < data.ngates fixed_image = itk.convert_to_itk_image(data[photosensor, fixed_idx]) x = [] y = [] transforms = [] for idx in range(data.ndetectors): image = data[photosensor, idx] moving_image = itk.convert_to_itk_image(image) transform = registration.itk_registration_rigid_2d( fixed_image, moving_image, options) x_new, y_new = transform.GetParameters() x.append(x_new) y.append(y_new) transforms.append(transform) return x, y, transforms
def shift(data, transforms): """ Resamples all the images in an ArrayDetectorData structure with the supplied transforms, and saves the result in a new ArrayDetectorData structure :param data: ArrayDetectorData object with images :param transforms: A list of transforms (Simple ITK), one for each image :return: ArrayDetectorDAta object with shifted images """ assert isinstance(transforms, list) and len(transforms) == data.ndetectors shifted = ArrayDetectorData(data.ndetectors, data.ngates) reference = Image(np.zeros(data[0, 0].shape, dtype=np.float64), data[0, 0].spacing) for gate in range(data.ngates): for i in range(data.ndetectors): image = itk.resample_image( itk.convert_to_itk_image(data[gate, i]), transforms[i], reference=itk.convert_to_itk_image(reference)) shifted[gate, i] = itk.convert_from_itk_image(image) return shifted
def shift_and_sum(data, transforms, photosensor=0, detectors=None, supersampling=1.0): """ Adaptive ISM pixel reassignment. Please use one of the functions above to figure out the shifts first, if you haven't already. :param supersampling: Insert a number != 1, if you want to rescale the result image to a different size. This might make sense, if you the original sampling has been sampled sparsely :param data: ArrayDetectorData object with all the individual images :param transforms: ITK spatial transformation that are to be used for the resampling :param photosensor: The photosensor index, if more than one :param detectors: a list of detectors to be included in the reconstruction. If None given (default), all the images will be used :return: reconstruction result Image """ assert isinstance(data, ArrayDetectorData) assert isinstance(transforms, list) and len(transforms) == data.ndetectors if supersampling != 1.0: new_shape = list( int(i * supersampling) for i in data[photosensor, 0].shape) new_spacing = list(i / supersampling for i in data[photosensor, 0].spacing) output = Image(np.zeros(new_shape, dtype=np.float64), new_spacing) else: output = Image(np.zeros(data[photosensor, 0].shape, dtype=np.float64), data[photosensor, 0].spacing) if detectors is None: detectors = list(range(data.ndetectors)) for i in detectors: image = itk.resample_image(itk.convert_to_itk_image(data[photosensor, i]), transforms[i], reference=itk.convert_to_itk_image(output)) output += itk.convert_from_itk_image(image) return output
def __itk_image(path, image): """ A writer for ITK supported image formats. :param path: A full path to the image. :param image: An image as :type image: numpy.ndarray. :param spacing: Pixel size ZXY, as a :type spacing: list. """ assert isinstance(image, Image) image = itkutils.convert_to_itk_image(image) sitk.WriteImage(image, path)
def find_image_shifts(data, options, photosensor=0, fixed_idx=12): """ Register all images in an ISM ArrayDetectorData dataset. The central image (pixel 12) is used as reference and other images are aligned with it. This function used an iterative algorithm (ITK) that can be customized with various command line options. :param options: Various options that can be used on fine tune the image registration. Look into supertomo_options.py file :param data: ArrayDetectorData object with all the individual images :param photosensor: The photosensor number (from 0 upwards) that is to be processed :param fixed_idx: The index of the reference image. Defaults to 12 (IIT SPAD array) :return: a three element tuple: x offset, y offset, transforms. The x and y offsets are expressed in physical units (um). The transforms are sitk.TranslationTransform objects that can be used to resample the images into a common coordinate system. """ assert photosensor < data.ngates fixed_image = itk.convert_to_itk_image(data[photosensor, fixed_idx]) transforms = [] ndims = fixed_image.GetDimension() shifts = np.zeros((data.ndetectors, ndims), dtype=np.float) if ndims == 2: registration_func = registration.itk_registration_rigid_2d elif ndims == 3: registration_func = registration.itk_registration_rigid_3d else: ValueError(f"Invalid image dimensions {ndims}") return for idx in range(data.ndetectors): image = data[photosensor, idx] moving_image = itk.convert_to_itk_image(image) transform = registration_func(fixed_image, moving_image, options) shifts_ = transform.GetParameters() shifts[idx] = shifts_[::-1] transforms.append(transform) return shifts, transforms