def run(self, image: ImageStack, in_place: bool = False) -> Optional[ImageStack]: """Register an ImageStack against a reference image. Parameters ---------- image : ImageStack The stack to be registered in_place : bool If false, return a new registered stack. Else, register in-place (default False) Returns ------- """ if not in_place: image = deepcopy(image) # TODO: (ambrosejcarr) is this the appropriate way of dealing with Z in registration? mp = image.max_proj(Axes.CH, Axes.ZPLANE) mp_numpy = mp._squeezed_numpy(Axes.CH, Axes.ZPLANE) reference_image_mp = self.reference_stack.max_proj( Axes.ROUND, Axes.CH, Axes.ZPLANE) reference_image_numpy = reference_image_mp._squeezed_numpy( Axes.ROUND, Axes.CH, Axes.ZPLANE) for r in image.axis_labels(Axes.ROUND): # compute shift between maximum projection (across channels) and dots, for each round # TODO: make the max projection array ignorant of axes ordering. shift, error = compute_shift(mp_numpy[r, :, :], reference_image_numpy, self.upsampling) print(f"For round: {r}, Shift: {shift}, Error: {error}") for c in image.axis_labels(Axes.CH): for z in image.axis_labels(Axes.ZPLANE): # apply shift to all zplanes, channels, and imaging rounds selector = {Axes.ROUND: r, Axes.CH: c, Axes.ZPLANE: z} data, axes = image.get_slice(selector=selector) assert len(axes) == 0 result = shift_im(data, shift) result = preserve_float_range(result) image.set_slice(selector=selector, data=result) if not in_place: return image return None
def run(self, stack: ImageStack, transforms_list: TransformsList, in_place: bool = False, verbose: bool = False, *args, **kwargs) -> ImageStack: """Applies a list of transformations to an ImageStack Parameters ---------- stack : ImageStack Stack to be transformed. transforms_list: TransformsList The list of transform objects to apply to the ImageStack. in_place : bool if True, process ImageStack in-place, otherwise return a new stack verbose : bool if True, report on transformation progress (default = False) Returns ------- ImageStack : If in-place is False, return the results of the transforms as a new stack. Otherwise return the original stack. """ if not in_place: # create a copy of the ImageStack, call apply on that stack with in_place=True image_stack = deepcopy(stack) return self.run(image_stack, transforms_list, in_place=True, **kwargs) if verbose and StarfishConfig().verbose: transforms_list.transforms = tqdm(transforms_list.transforms) all_axes = {Axes.ROUND, Axes.CH, Axes.ZPLANE} for selector, _, transformation_object in transforms_list.transforms: other_axes = all_axes - set(selector.keys()) # iterate through remaining axes for axes in stack._iter_axes(other_axes): # combine all axes data to select one tile selector.update(axes) # type: ignore selected_image, _ = stack.get_slice(selector) warped_image = warp(selected_image, transformation_object, **kwargs).astype(np.float32) stack.set_slice(selector, warped_image) return stack