def measure_spot_intensities( data_image: ImageStack, spot_attributes: SpotAttributes, measurement_function: Callable[[Sequence], Number], radius_is_gyration: bool = False, ) -> IntensityTable: """given spots found from a reference image, find those spots across a data_image Parameters ---------- data_image : ImageStack ImageStack containing multiple volumes for which spots' intensities must be calculated spot_attributes : pd.Dataframe Locations and radii of spots measurement_function : Callable[[Sequence], Number]) Function to apply over the spot volumes to identify the intensity (e.g. max, mean, ...) radius_is_gyration : bool if True, indicates that the radius corresponds to radius of gyration, which is a function of spot intensity, but typically is a smaller unit than the sigma generated by blob_log. In this case, the spot's bounding box is rounded up instead of down when measuring intensity. (default False) Returns ------- IntensityTable : 3d tensor of (spot, channel, round) information for each coded spot """ # determine the shape of the intensity table ch_labels = data_image.axis_labels(Axes.CH) round_labels = data_image.axis_labels(Axes.ROUND) # construct the empty intensity table intensity_table = IntensityTable.zeros( spot_attributes=spot_attributes, ch_labels=ch_labels, round_labels=round_labels, ) # if no spots were detected, return the empty IntensityTable if intensity_table.sizes[Features.AXIS] == 0: return intensity_table # fill the intensity table indices = product(ch_labels, round_labels) for c, r in indices: image, _ = data_image.get_slice({Axes.CH: c, Axes.ROUND: r}) blob_intensities: pd.Series = measure_spot_intensity( image, spot_attributes, measurement_function, radius_is_gyration=radius_is_gyration) intensity_table.loc[dict(c=c, r=r)] = blob_intensities return intensity_table
def measure_intensities_at_spot_locations_across_imagestack( data_image: ImageStack, reference_spots: PerImageSliceSpotResults, measurement_function: Callable[[np.ndarray], Number], radius_is_gyration: bool = False) -> SpotFindingResults: """given spots found from a reference image, find those spots across a data_image Parameters ---------- data_image : ImageStack ImageStack containing multiple volumes for which spots' intensities must be calculated reference_spots : PerImageSliceSpotResults Spots found in a reference image measurement_function : Callable[[np.ndarray], Number]) Function to apply over the spot volumes to identify the intensity (e.g. max, mean, ...) radius_is_gyration : bool if True, indicates that the radius corresponds to radius of gyration, which is a function of spot intensity, but typically is a smaller unit than the sigma generated by blob_log. In this case, the spot's bounding box is rounded up instead of down when measuring intensity. (default False) Returns ------- SpotFindingResults : A Dict of tile indices and their corresponding measured SpotAttributes """ ch_labels = data_image.axis_labels(Axes.CH) round_labels = data_image.axis_labels(Axes.ROUND) spot_results = SpotFindingResults( imagestack_coords=data_image.xarray.coords, log=data_image.log) # measure spots in each tile indices = product(ch_labels, round_labels) for c, r in indices: tile_indices = {Axes.ROUND: r, Axes.CH: c} if reference_spots.spot_attrs.data.empty: # if no spots found don't measure spot_results[tile_indices] = reference_spots else: image, _ = data_image.get_slice({Axes.CH: c, Axes.ROUND: r}) blob_intensities: pd.Series = measure_intensities_at_spot_locations_in_image( image, reference_spots.spot_attrs, measurement_function, radius_is_gyration=radius_is_gyration) # copy reference spot positions and attributes tile_spots = SpotAttributes(reference_spots.spot_attrs.data.copy()) # fill in intensities tile_spots.data[Features.INTENSITY] = blob_intensities spot_results[tile_indices] = PerImageSliceSpotResults( spot_attrs=tile_spots, extras=None) return spot_results
def run( self, stack: ImageStack, in_place: bool = False, verbose=False, n_processes: Optional[int] = None, *args, ) -> Optional[ImageStack]: """Perform filtering of an image stack Parameters ---------- stack : ImageStack Stack to be filtered. in_place : bool if True, process ImageStack in-place, otherwise return a new stack verbose : bool if True, report on the percentage completed during processing (default = False) n_processes : Optional[int]: None Not implemented. Number of processes to use when applying filter. Returns ------- ImageStack : If in-place is False, return the results of filter as a new stack. Otherwise return the original stack. """ # The default is False, so even if code requests True require config to be True as well verbose = verbose and StarfishConfig().verbose channels_per_round = stack.xarray.groupby(Axes.ROUND.value) channels_per_round = tqdm(channels_per_round) if verbose else channels_per_round if not in_place: new_stack = deepcopy(stack) self.run(new_stack, in_place=True) return new_stack # compute channel magnitude mask for r, dat in channels_per_round: # nervous about how xarray orders dimensions so i put this here explicitly .... dat = dat.transpose(Axes.CH.value, Axes.ZPLANE.value, Axes.Y.value, Axes.X.value ) # ... to account for this line taking the norm across axis 0, or the channel axis ch_magnitude = np.linalg.norm(dat, ord=2, axis=0) magnitude_mask = ch_magnitude >= self.thresh # apply mask and optionally, normalize by channel magnitude for c in stack.axis_labels(Axes.CH): ind = {Axes.ROUND.value: r, Axes.CH.value: c} stack._data[ind] = stack._data[ind] * magnitude_mask if self.normalize: stack._data[ind] = np.divide(stack._data[ind], ch_magnitude, where=magnitude_mask ) return None
def run(self, stack: ImageStack, verbose: bool = False, *args) -> TransformsList: """ Iterate over the given axes of an ImageStack and learn the translation transform based off the reference_stack passed into :py:class:`Translation`'s constructor. Only supports 2d data. Parameters ---------- stack : ImageStack Stack to calculate the transforms on. verbose : bool if True, report on transformation progress (default = False) Returns ------- List[Tuple[Mapping[Axes, int], SimilarityTransform]] : A list of tuples containing axes of the Imagestack and associated transform to apply. """ transforms = TransformsList() reference_image = np.squeeze(self.reference_stack.xarray) for a in stack.axis_labels(self.axes): target_image = np.squeeze(stack.sel({self.axes: a}).xarray) if len(target_image.shape) != 2: raise ValueError( f"Only axes: {self.axes.value} can have a length > 1, " f"please use the MaxProj filter.") shift, error, phasediff = phase_cross_correlation( reference_image=target_image.data, moving_image=reference_image.data, upsample_factor=self.upsampling) if verbose: print(f"For {self.axes}: {a}, Shift: {shift}, Error: {error}") selectors = {self.axes: a} # reverse shift because SimilarityTransform stores in y,x format shift = shift[::-1] transforms.append(selectors, TransformType.SIMILARITY, SimilarityTransform(translation=shift)) return transforms