Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
    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
Esempio n. 4
0
    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