Ejemplo n.º 1
0
 def _in_place_apply(apply_func: Callable[..., Union[xr.DataArray,
                                                     np.ndarray]],
                     data: np.ndarray, clip_method: Union[str, Clip],
                     **kwargs) -> None:
     result = apply_func(data, **kwargs)
     if clip_method == Clip.CLIP:
         data[:] = preserve_float_range(result, rescale=False)
     elif clip_method == Clip.SCALE_BY_CHUNK:
         data[:] = preserve_float_range(result, rescale=True)
     else:
         data[:] = result
Ejemplo n.º 2
0
    def run(
        self,
        stack: ImageStack,
        *args,
    ) -> ImageStack:
        """Performs the dimension reduction with the specifed function

        Parameters
        ----------
        stack : ImageStack
            Stack to be filtered.

        Returns
        -------
        ImageStack :
            Return the results of filter as a new stack.

        """

        # Apply the reducing function
        reduced = stack.xarray.reduce(self.func,
                                      dim=[dim.value for dim in self.dims],
                                      **self.kwargs)

        # Add the reduced dims back and align with the original stack
        reduced = reduced.expand_dims(tuple(dim.value for dim in self.dims))
        reduced = reduced.transpose(*stack.xarray.dims)

        if self.clip_method == Clip.CLIP:
            reduced = preserve_float_range(reduced, rescale=False)
        else:
            reduced = preserve_float_range(reduced, rescale=True)

        # Update the physical coordinates
        physical_coords: MutableMapping[Coordinates, Sequence[Number]] = {}
        for axis, coord in ((Axes.X, Coordinates.X), (Axes.Y, Coordinates.Y),
                            (Axes.ZPLANE, Coordinates.Z)):
            if axis in self.dims:
                # this axis was projected out of existence.
                assert coord.value not in reduced.coords
                physical_coords[coord] = [
                    np.average(stack._data.coords[coord.value])
                ]
            else:
                physical_coords[coord] = cast(Sequence[Number],
                                              reduced.coords[coord.value])
        reduced_stack = ImageStack.from_numpy(reduced.values,
                                              coordinates=physical_coords)

        return reduced_stack
Ejemplo n.º 3
0
    def _high_pass(image: Union[xr.DataArray, np.ndarray],
                   size: Number,
                   rescale: bool = False) -> np.ndarray:
        """
        Applies a mean high pass filter to an image

        Parameters
        ----------
        image : Union[xr.DataArray, numpy.ndarray]
            2-d or 3-d image data
        size : Union[Number, Tuple[Number]]
            width of the kernel
        rescale : bool
            If true scales data by max value, if false clips max values to one

        Returns
        -------
        np.ndarray[np.float32]:
            Filtered image, same shape as input
        """

        blurred: np.ndarray = uniform_filter(image, size)

        filtered: np.ndarray = image - blurred
        filtered = preserve_float_range(filtered, rescale)

        return filtered
Ejemplo n.º 4
0
    def _low_pass(image: Union[xr.DataArray, np.ndarray],
                  sigma: Union[Number, Tuple[Number]],
                  rescale: bool = False) -> np.ndarray:
        """
        Apply a Gaussian blur operation over a multi-dimensional image.

        Parameters
        ----------
        image : Union[xr.DataArray, np.ndarray]
            2-d or 3-d image data
        sigma : Union[Number, Tuple[Number]]
            Standard deviation of the Gaussian kernel that will be applied. If a float, an
            isotropic kernel will be assumed, otherwise the dimensions of the kernel give (z, y, x)
         rescale : bool
            If true scales data by max value, if false clips max values to one (default False)

        Returns
        -------
        np.ndarray :
            Blurred data in same shape as input image

        """

        filtered = gaussian(image,
                            sigma=sigma,
                            output=None,
                            cval=0,
                            multichannel=False,
                            preserve_range=True,
                            truncate=4.0)

        filtered = preserve_float_range(filtered, rescale)

        return filtered
Ejemplo n.º 5
0
    def _high_pass(image: Union[xr.DataArray, np.ndarray],
                   sigma: Union[Number, Tuple[Number]],
                   rescale: bool = False) -> Union[xr.DataArray, np.ndarray]:
        """
        Applies a gaussian high pass filter to an image

        Parameters
        ----------
        image : Union[xr.DataArray, np.ndarray]
            2-d or 3-d image data
        sigma : Union[Number, Tuple[Number]]
            Standard deviation of gaussian kernel
        rescale : bool
            If true scales data by max value, if false clips max values to one

        Returns
        -------
        np.ndarray :
            filtered image of the same shape as the input image
        """

        blurred = GaussianLowPass._low_pass(image, sigma)
        filtered = image - blurred
        filtered = preserve_float_range(filtered, rescale)

        return filtered
Ejemplo n.º 6
0
    def run(
        self,
        stack: ImageStack,
        in_place: bool = False,
        verbose: bool = 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 filtering progress (default = False)
        n_processes : Optional[int]
            Number of parallel processes to devote to applying the filter. If None, defaults to
            the result of os.cpu_count(). (default None)

        Returns
        -------
        ImageStack :
            If in-place is False, return the results of filter as a new stack.  Otherwise return the
            original stack.

        """
        # Align the axes of the multipliers with ImageStack
        mult_array_aligned: np.ndarray = self.mult_array.transpose(
            *stack.xarray.dims).values
        if not in_place:
            stack = deepcopy(stack)
            self.run(stack, in_place=True)
            return stack

        stack.xarray.values *= mult_array_aligned
        if self.clip_method == Clip.CLIP:
            stack.xarray.values = preserve_float_range(stack.xarray.values,
                                                       rescale=False)
        else:
            stack.xarray.values = preserve_float_range(stack.xarray.values,
                                                       rescale=True)
        return None
Ejemplo n.º 7
0
    def synthetic_intensities(cls,
                              codebook,
                              num_z: int = 12,
                              height: int = 50,
                              width: int = 40,
                              n_spots=10,
                              mean_fluor_per_spot=200,
                              mean_photons_per_fluor=50) -> "IntensityTable":
        """
        Creates an IntensityTable with synthetic spots, that correspond to valid
        codes in a provided codebook.

        Parameters
        ----------
        codebook : Codebook
            Starfish codebook object.
        num_z : int
            Number of z-planes to use when localizing spots.
        height : int
            y dimension of each synthetic plane.
        width : int
            x dimension of each synthetic plane.
        n_spots : int
            Number of spots to generate.
        mean_fluor_per_spot : int
            Mean number of fluorophores per spot.
        mean_photons_per_fluor : int
            Mean number of photons per fluorophore.

        Returns
        -------
        IntensityTable

        """

        # TODO nsofroniew: right now there is no jitter on x-y positions of the spots
        z = np.random.randint(0, num_z, size=n_spots)
        y = np.random.randint(0, height, size=n_spots)
        x = np.random.randint(0, width, size=n_spots)
        r = np.empty(n_spots)
        r.fill(
            np.nan)  # radius is a function of the point-spread gaussian size
        spot_attributes = SpotAttributes(
            pd.DataFrame({
                Axes.ZPLANE.value: z,
                Axes.Y.value: y,
                Axes.X.value: x,
                Features.SPOT_RADIUS: r
            }))

        # empty data tensor
        data = np.zeros(shape=(n_spots, *codebook.shape[1:]))

        targets = np.random.choice(codebook.coords[Features.TARGET],
                                   size=n_spots,
                                   replace=True)
        expected_bright_locations = np.where(codebook.loc[targets])

        # create a binary matrix where "on" spots are 1
        data[expected_bright_locations] = 1

        # add physical properties of fluorescence
        data *= np.random.poisson(mean_photons_per_fluor, size=data.shape)
        data *= np.random.poisson(mean_fluor_per_spot, size=data.shape)

        # convert data to float for consistency with starfish
        data = preserve_float_range(data)
        assert 0 < data.max() <= 1

        intensities = cls.from_spot_data(data, spot_attributes,
                                         np.arange(data.shape[1]),
                                         np.arange(data.shape[2]))
        intensities[Features.TARGET] = (Features.AXIS, targets)

        return intensities
Ejemplo n.º 8
0
    def apply(self,
              func: Callable,
              group_by: Set[Axes] = None,
              in_place=False,
              verbose: bool = False,
              n_processes: Optional[int] = None,
              clip_method: Union[str, Clip] = Clip.CLIP,
              **kwargs) -> "ImageStack":
        """Split the image along a set of axes and apply a function across all the components.  This
        function should yield data of the same dimensionality as the input components.  These
        resulting components are then constituted into an ImageStack and returned.

        Parameters
        ----------
        func : Callable
            Function to apply. must expect a first argument which is a 2d or 3d numpy array and
            return an array of the same shape.
        group_by : Set[Axes]
            Axes to split the data along.
            `ex. splitting a 2D array (axes: X, Y; size:
            3, 4) by X results in 3 arrays of size 4.  (default {Axes.ROUND, Axes.CH,
            Axes.ZPLANE})`
        in_place : bool
            If True, function is executed in place. If n_proc is not 1, the tile or
            volume will be copied once during execution. If false, a new ImageStack object will be
            produced. (Default False)
        verbose : bool
            If True, report on the percentage completed (default = False) during processing
        n_processes : Optional[int]
            The number of processes to use for apply. If None, uses the output of os.cpu_count()
            (default = None).
        kwargs : dict
            Additional arguments to pass to func
        clip_method : Union[str, :py:class:`~starfish.types.Clip`]

            - Clip.CLIP (default): Controls the way that data are scaled to retain skimage dtype
              requirements that float data fall in [0, 1].
            - Clip.CLIP: data above 1 are set to 1, and below 0 are set to 0
            - Clip.SCALE_BY_IMAGE: data above 1 are scaled by the maximum value, with the maximum
              value calculated over the entire ImageStack
            - Clip.SCALE_BY_CHUNK: data above 1 are scaled by the maximum value, with the maximum
              value calculated over each slice, where slice shapes are determined by the group_by
              parameters


        Returns
        -------
        ImageStack :
            If inplace is False, return a new ImageStack, otherwise return a reference to the
            original stack with data modified by application of func

        Raises
        ------
        TypeError :
             If no Clip method given.

        """
        # default grouping is by (x, y) tile
        if group_by is None:
            group_by = {Axes.ROUND, Axes.CH, Axes.ZPLANE}

        if not isinstance(clip_method, (str, Clip)):
            raise TypeError(
                "must pass a Clip method. See starfish.types.Clip for valid options"
            )

        if not in_place:
            # create a copy of the ImageStack, call apply on that stack with in_place=True
            image_stack = deepcopy(self)
            return image_stack.apply(func=func,
                                     group_by=group_by,
                                     in_place=True,
                                     verbose=verbose,
                                     n_processes=n_processes,
                                     clip_method=clip_method,
                                     **kwargs)

        # wrapper adds a target `data` parameter where the results from func will be stored
        # data are clipped or scaled by chunk using preserve_float_range if clip_method != 2
        bound_func = partial(ImageStack._in_place_apply,
                             func,
                             clip_method=clip_method)

        # execute the processing workflow
        self.transform(func=bound_func,
                       group_by=group_by,
                       verbose=verbose,
                       n_processes=n_processes,
                       **kwargs)

        # scale based on values of whole image
        if clip_method == Clip.SCALE_BY_IMAGE:
            self._data.data.values = preserve_float_range(
                self._data.data.values, rescale=True)

        return self