Esempio n. 1
0
    def image_to_spots(
        self,
        data_image: np.ndarray,
    ) -> PerImageSliceSpotResults:
        """
        Find spots using a gaussian blob finding algorithm

        Parameters
        ----------
        data_image : np.ndarray
            image containing spots to be detected

        Returns
        -------
        PerImageSpotResults :
            includes a SpotAttributes DataFrame of metadata containing the coordinates, intensity
            and radius of each spot, as well as any extra information collected during spot finding.

        """

        spot_finding_args = {
            "min_sigma": self.min_sigma,
            "max_sigma": self.max_sigma,
            "threshold": self.threshold,
            "exclude_border": self.exclude_border,
            "overlap": self.overlap,
            "num_sigma": self.num_sigma
        }
        if self.detector_method is not blob_doh:
            spot_finding_args["exclude_border"] = self.exclude_border

        fitted_blobs_array: np.ndarray = self.detector_method(
            data_image, **spot_finding_args)

        if fitted_blobs_array.shape[0] == 0:
            empty_spot_attrs = SpotAttributes.empty(
                extra_fields=[Features.INTENSITY, Features.SPOT_ID])
            return PerImageSliceSpotResults(spot_attrs=empty_spot_attrs,
                                            extras=None)

        # measure intensities
        z_inds = fitted_blobs_array[:, 0].astype(int)
        y_inds = fitted_blobs_array[:, 1].astype(int)
        x_inds = fitted_blobs_array[:, 2].astype(int)
        radius = np.round(fitted_blobs_array[:, 3] * np.sqrt(3))
        data_image = np.asarray(data_image)
        intensities = data_image[tuple([z_inds, y_inds, x_inds])]

        # construct dataframe
        spot_data = pd.DataFrame(
            data={
                Features.INTENSITY: intensities,
                Axes.ZPLANE.value: z_inds,
                Axes.Y.value: y_inds,
                Axes.X.value: x_inds,
                Features.SPOT_RADIUS: radius,
            })
        spots = SpotAttributes(spot_data)
        spots.data[Features.SPOT_ID] = np.arange(spots.data.shape[0])
        return PerImageSliceSpotResults(spot_attrs=spots, extras=None)
    def _create_spot_attributes(
        self,
        region_properties: List[_RegionProperties],
        decoded_image: np.ndarray,
        target_map: TargetsMap,
        n_processes: Optional[int] = None
    ) -> Tuple[SpotAttributes, np.ndarray]:
        """

        Parameters
        ----------
        region_properties : List[_RegionProperties]
            Properties of the each connected component. Output of skimage.measure.regionprops
        decoded_image : np.ndarray
            Image whose pixels correspond to the targets that the given position in the ImageStack
            decodes to.
        target_map : TargetsMap
            Unique mapping between string target names and int target IDs.
        n_processes : Optional[int]=None
            number of processes to devote to measuring spot properties. If None, defaults to the
            result of os.nproc()

        Returns
        -------
        pd.DataFrame :
            DataFrame containing x, y, z, radius, and target name for each connected component
            feature.
        np.ndarray[bool] :
            An array with length equal to the number of features. If zero, indicates that a feature
            has failed area filters.
        """
        with ThreadPoolExecutor(max_workers=n_processes) as tpe:
            mapfunc = tpe.map
            applyfunc = partial(self._single_spot_attributes,
                                decoded_image=decoded_image,
                                target_map=target_map,
                                min_area=self._min_area,
                                max_area=self._max_area)

            iterable = tqdm(region_properties,
                            disable=(not StarfishConfig().verbose))
            results = mapfunc(applyfunc, iterable)
            if not results:
                # no spots found
                warnings.warn(
                    "No spots found, please adjust threshold parameters")
                return SpotAttributes.empty(extra_fields=['target']), np.array(
                    0, dtype=bool)
            spot_attrs, passes_area_filter = zip(*results)

            # update passes filter
            passes_filter = np.array(passes_area_filter, dtype=bool)

            spot_attributes = SpotAttributes(
                pd.DataFrame.from_records(spot_attrs))
            spot_attributes.data[Features.SPOT_ID] = np.arange(
                0, len(spot_attributes.data))
            return spot_attributes, passes_filter
Esempio n. 3
0
    def image_to_spots(
            self, data_image: Union[np.ndarray,
                                    xr.DataArray]) -> SpotAttributes:
        """
        Find spots using a gaussian blob finding algorithm

        Parameters
        ----------
        data_image : Union[np.ndarray, xr.DataArray]
            image containing spots to be detected

        Returns
        -------
        SpotAttributes :
            DataFrame of metadata containing the coordinates, intensity and radius of each spot

        """

        fitted_blobs_array: np.ndarray = self.detector_method(
            data_image,
            min_sigma=self.min_sigma,
            max_sigma=self.max_sigma,
            threshold=self.threshold,
            exclude_border=self.exclude_border,
            overlap=self.overlap,
            num_sigma=self.num_sigma)

        if fitted_blobs_array.shape[0] == 0:
            return SpotAttributes.empty(
                extra_fields=[Features.INTENSITY, Features.SPOT_ID])

        # measure intensities
        z_inds = fitted_blobs_array[:, 0].astype(int)
        y_inds = fitted_blobs_array[:, 1].astype(int)
        x_inds = fitted_blobs_array[:, 2].astype(int)
        radius = np.round(fitted_blobs_array[:, 3] * np.sqrt(3))
        data_image = data_image.values if isinstance(
            data_image, xr.DataArray) else data_image
        intensities = data_image[tuple([z_inds, y_inds, x_inds])]

        # construct dataframe
        spot_data = pd.DataFrame(
            data={
                Features.INTENSITY: intensities,
                Axes.ZPLANE.value: z_inds,
                Axes.Y.value: y_inds,
                Axes.X.value: x_inds,
                Features.SPOT_RADIUS: radius,
            })
        spots = SpotAttributes(spot_data)
        spots.data[Features.SPOT_ID] = np.arange(spots.data.shape[0])
        return spots
Esempio n. 4
0
def build_traces_sequential(spot_results: SpotFindingResults,
                            **kwargs) -> IntensityTable:
    """
    Build spot traces  without merging across channels and imaging rounds. Used for sequential
    methods like smFIsh.

    Parameters
    ----------
    spot_results: SpotFindingResults
        Spots found across rounds/channels of an ImageStack

    Returns
    -------
    IntensityTable :
        concatenated input SpotAttributes, converted to an IntensityTable object

    """

    all_spots = pd.concat([sa.data for sa in spot_results.values()], sort=True)

    intensity_table = IntensityTable.zeros(
        spot_attributes=SpotAttributes(all_spots),
        ch_labels=spot_results.ch_labels,
        round_labels=spot_results.round_labels,
    )

    i = 0
    for (r, c), attrs in spot_results.items():
        for _, row in attrs.data.iterrows():
            selector = dict(features=i, c=c, r=r)
            intensity_table.loc[selector] = row[Features.INTENSITY]
            i += 1
    return intensity_table
Esempio n. 5
0
def test_intensity_table_can_be_created_from_spot_attributes():
    """
    This test creates an IntensityTable from spot attributes, and verifies that the size matches
    what was requested and that the values are all zero.
    """

    # input has two spots
    spot_attributes = SpotAttributes(
        pd.DataFrame(
            data=np.array(
                [[1, 1, 1, 1],
                 [2, 2, 2, 1]]
            ),
            columns=[Axes.ZPLANE, Axes.Y, Axes.X, Features.SPOT_RADIUS]
        )
    )

    intensities = IntensityTable.zeros(
        spot_attributes,
        ch_labels=np.arange(1),
        round_labels=np.arange(3)
    )

    assert intensities.sizes[Axes.CH] == 1
    assert intensities.sizes[Axes.ROUND] == 3
    assert intensities.sizes[Features.AXIS] == 2
    assert np.all(intensities.values == 0)
Esempio n. 6
0
def measure_spot_intensity(
    image: Union[np.ndarray, xr.DataArray],
    spots: SpotAttributes,
    measurement_function: Callable[[Sequence], Number],
    radius_is_gyration: bool = False,
) -> pd.Series:
    """measure the intensity of each spot in spots in the corresponding image

    Parameters
    ----------
    image : Union[np.ndarray, xr.DataArray],
        3-d volume in which to measure intensities
    spots : pd.DataFrame
        SpotAttributes table containing coordinates 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
    -------
    pd.Series :
        Intensities for each spot in SpotAttributes

    """
    def fn(row: pd.Series) -> Number:
        data = image[row['z_min']:row['z_max'], row['y_min']:row['y_max'],
                     row['x_min']:row['x_max']]
        return measurement_function(data)

    if radius_is_gyration:
        radius = np.ceil(
            spots.data[Features.SPOT_RADIUS]).astype(int) + 1  # round up
    else:
        radius = spots.data[Features.SPOT_RADIUS].astype(
            int)  # truncate down to nearest integer
    for v, max_size in zip(['z', 'y', 'x'], image.shape):
        # numpy does exclusive max indexing, so need to subtract 1 from min to get centered box
        spots.data[f'{v}_min'] = np.clip(spots.data[v] - (radius - 1), 0, None)
        spots.data[f'{v}_max'] = np.clip(spots.data[v] + radius, None,
                                         max_size)
    return spots.data[['z_min', 'z_max', 'y_min', 'y_max', 'x_min',
                       'x_max']].astype(int).apply(fn, axis=1)
Esempio n. 7
0
def spot_attribute_factory(n: int) -> SpotAttributes:
    """
    Construct SpotAttributes with n synthetic attributes. Each attribute has radius 1 and
    x, y, z coordinates equal to their index i in [0, n)
    """
    return SpotAttributes(
        pd.DataFrame(
            data=np.array([[i, i, i, 1] for i in np.arange(n)]),
            columns=[Axes.ZPLANE, Axes.Y, Axes.X, Features.SPOT_RADIUS]))
Esempio n. 8
0
    def image_to_spots(
        self,
        data_image: np.ndarray,
    ) -> PerImageSliceSpotResults:
        """

        Parameters
        ----------
        data_image : np.ndarray
            three-dimensional image containing spots to be detected

        Returns
        -------
        PerImageSpotResults :
            includes a SpotAttributes DataFrame of metadata containing the coordinates, intensity
            and radius of each spot, as well as any extra information collected during spot finding.

        """
        data_image = np.asarray(data_image)
        with warnings.catch_warnings():
            warnings.simplefilter(
                'ignore', FutureWarning)  # trackpy numpy indexing warning
            warnings.simplefilter('ignore',
                                  UserWarning)  # yielded if black images
            attributes = locate(
                data_image,
                diameter=self.diameter,
                minmass=self.minmass,
                maxsize=self.maxsize,
                separation=self.separation,
                noise_size=self.noise_size,
                smoothing_size=self.smoothing_size,
                threshold=self.threshold,
                percentile=self.percentile,
                preprocess=self.preprocess,
                max_iterations=self.max_iterations,
            )

        # when zero spots are detected, 'ep' is missing from the trackpy locate results.
        if attributes.shape[0] == 0:
            attributes['ep'] = []

        # TODO ambrosejcarr: data should always be at least pseudo-3d, this may not be necessary
        # TODO ambrosejcarr: this is where max vs. sum vs. mean would be parametrized.
        # here, total_intensity = sum, intensity = max
        new_colnames = [
            'y', 'x', 'total_intensity', 'radius', 'eccentricity', 'intensity',
            'raw_mass', 'ep'
        ]
        if len(data_image.shape) == 3:
            attributes.columns = ['z'] + new_colnames
        else:
            attributes.columns = new_colnames

        attributes['spot_id'] = np.arange(attributes.shape[0])
        return PerImageSliceSpotResults(spot_attrs=SpotAttributes(attributes),
                                        extras=None)
Esempio n. 9
0
    def image_to_spots(self, data_image: Union[np.ndarray, xr.DataArray]) -> SpotAttributes:
        """
        Find spots using a gaussian blob finding algorithm

        Parameters
        ----------
        data_image : Union[np.ndarray, xr.DataArray]
            image containing spots to be detected

        Returns
        -------
        SpotAttributes :
            DataFrame of metadata containing the coordinates, intensity and radius of each spot

        """

        fitted_blobs_array: np.ndarray = self.detector_method(
            data_image,
            self.min_sigma,
            self.max_sigma,
            self.num_sigma,
            self.threshold,
            self.overlap
        )

        if fitted_blobs_array.shape[0] == 0:
            return SpotAttributes.empty(extra_fields=['intensity', 'spot_id'])

        # create the SpotAttributes Table
        columns = [Axes.ZPLANE.value, Axes.Y.value, Axes.X.value, Features.SPOT_RADIUS]
        fitted_blobs = pd.DataFrame(data=fitted_blobs_array, columns=columns)

        # convert standard deviation of gaussian kernel used to identify spot to radius of spot
        converted_radius = np.round(fitted_blobs[Features.SPOT_RADIUS] * np.sqrt(3))
        fitted_blobs[Features.SPOT_RADIUS] = converted_radius

        # convert the array to int so it can be used to index
        spots = SpotAttributes(fitted_blobs)

        spots.data['intensity'] = measure_spot_intensity(
            data_image, spots, self.measurement_function)
        spots.data['spot_id'] = np.arange(spots.data.shape[0])

        return spots
    def image_to_spots(
            self, data_image: Union[np.ndarray,
                                    xr.DataArray]) -> SpotAttributes:
        """

        Parameters
        ----------
        data_image : np.ndarray
            three-dimensional image containing spots to be detected

        Returns
        -------
        SpotAttributes :
            spot attributes table for all detected spots

        """
        data_image = np.asarray(data_image)
        with warnings.catch_warnings():
            warnings.simplefilter(
                'ignore', FutureWarning)  # trackpy numpy indexing warning
            warnings.simplefilter('ignore',
                                  UserWarning)  # yielded if black images
            attributes = locate(data_image,
                                diameter=self.diameter,
                                minmass=self.minmass,
                                maxsize=self.maxsize,
                                separation=self.separation,
                                noise_size=self.noise_size,
                                smoothing_size=self.smoothing_size,
                                threshold=self.threshold,
                                percentile=self.percentile,
                                preprocess=self.preprocess)

        # when zero spots are detected, 'ep' is missing from the trackpy locate results.
        if attributes.shape[0] == 0:
            attributes['ep'] = []

        # TODO ambrosejcarr: data should always be at least pseudo-3d, this may not be necessary
        # TODO ambrosejcarr: this is where max vs. sum vs. mean would be parametrized.
        # here, total_intensity = sum, intensity = max
        new_colnames = [
            'y', 'x', 'total_intensity', 'radius', 'eccentricity', 'intensity',
            'raw_mass', 'ep'
        ]
        if len(data_image.shape) == 3:
            attributes.columns = ['z'] + new_colnames
        else:
            attributes.columns = new_colnames

        attributes['spot_id'] = np.arange(attributes.shape[0])
        # convert these to int so it can be used to index
        attributes.x = attributes.x.astype(int)
        attributes.y = attributes.y.astype(int)
        attributes.z = attributes.z.astype(int)
        return SpotAttributes(attributes)
Esempio n. 11
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
def combine_spot_attributes_by_round_channel(
        spot_attributes_lists: Sequence[Tuple[PerImageSliceSpotResults, Mapping[Axes, int]]],
) -> List[Tuple[PerImageSliceSpotResults, Mapping[Axes, int]]]:
    # first bin by indices
    bins: MutableMapping[Tuple[int, int], List[SpotAttributes]] = collections.defaultdict(list)
    for spot_attributes_list, indices in spot_attributes_lists:
        bins[(indices[Axes.ROUND], indices[Axes.CH])].append(spot_attributes_list.spot_attrs)

    return [
        (
            PerImageSliceSpotResults(SpotAttributes.combine(spot_attributes_lists), None),
            {Axes.ROUND: indices[0], Axes.CH: indices[1]},
        )
        for indices, spot_attributes_lists in bins.items()
    ]
def intensity_table_factory() -> IntensityTable:
    """IntensityTable with a single feature that was measured over 2 channels and 2 rounds."""

    intensities = np.array([[[0, 3], [4, 0]]], dtype=float)
    spot_attribute_data = pd.DataFrame(
        data=[0, 0, 0, 1],
        index=[Axes.ZPLANE, Axes.Y, Axes.X, Features.SPOT_RADIUS]).T
    spot_attributes = SpotAttributes(spot_attribute_data)

    intensity_table = IntensityTable.from_spot_data(
        intensities,
        spot_attributes,
        ch_values=np.arange(intensities.shape[1]),
        round_values=np.arange(intensities.shape[2]),
    )
    return intensity_table
def intensity_table_factory(data: np.ndarray = np.array([[[0, 3], [4, 0]]
                                                         ])) -> IntensityTable:
    """IntensityTable with a single feature that was measured over 2 channels and 2 rounds."""

    # generates spot attributes equal in size to the number of passed features.
    # each attribute has coordinates (z, y, x) equal to the feature index, and radius 1.
    spot_attributes_data = pd.DataFrame(
        data=np.array([[i, i, i, 1] for i in np.arange(data.shape[0])]),
        columns=[Axes.ZPLANE, Axes.Y, Axes.X, Features.SPOT_RADIUS])

    spot_attributes = SpotAttributes(spot_attributes_data)
    intensity_table = IntensityTable.from_spot_data(
        data,
        spot_attributes,
        ch_values=np.arange(data.shape[1]),
        round_values=np.arange(data.shape[2]),
    )
    return intensity_table
def filterSpots(spots, mask, oneIndex=False, invert=False):
    # takes a SpotFindingResults, ImageStack, and BinaryMaskCollection
    # to return a set of SpotFindingResults that are masked by the binary mask
    spot_attributes_list = []
    maskMat = mask.to_label_image().xarray.values
    maskMat[maskMat > 1] = 1
    if invert:
        maskMat = 1 - maskMat
    maskSize = np.shape(maskMat)
    for item in spots.items():
        selectedSpots = item[1].spot_attrs.data
        selectedSpots = selectedSpots.reset_index(drop=True)

        selRow = []
        for ind, row in selectedSpots.iterrows():
            if maskMat[int(row["y"]) - oneIndex][int(row["x"]) -
                                                 oneIndex] == 1:
                selRow.append(ind)

        selectedSpots = selectedSpots.iloc[selRow]
        selectedSpots = selectedSpots.drop_duplicates(
        )  # unsure why the query necessitates this

        spotResults = PerImageSliceSpotResults(
            spot_attrs=SpotAttributes(selectedSpots), extras=None)
        spot_attributes_list.append((spotResults, {
            Axes.ROUND: item[0][0],
            Axes.CH: item[0][1]
        }))
    newCoords = {}

    renameAxes = {
        "x": Coordinates.X.value,
        "y": Coordinates.Y.value,
        "z": Coordinates.Z.value
    }
    for k in renameAxes.keys():
        newCoords[renameAxes[k]] = spots.physical_coord_ranges[k]

    newSpots = SpotFindingResults(imagestack_coords=newCoords,
                                  log=starfish.Log(),
                                  spot_attributes_list=spot_attributes_list)
    return newSpots
Esempio n. 16
0
def concatenate_spot_attributes_to_intensities(
    spot_attributes: Sequence[Tuple[SpotAttributes, Dict[Axes, int]]]
) -> IntensityTable:
    """
    Merge multiple spot attributes frames into a single IntensityTable without merging across
    channels and imaging rounds

    Parameters
    ----------
    spot_attributes : Sequence[Tuple[SpotAttributes, Dict[Axes, int]]]
        A sequence of SpotAttribute objects and the indices (channel, round) that each object is
        associated with.

    Returns
    -------
    IntensityTable :
        concatenated input SpotAttributes, converted to an IntensityTable object

    """
    ch_values: Sequence[int] = sorted(
        set(inds[Axes.CH] for _, inds in spot_attributes))
    round_values: Sequence[int] = sorted(
        set(inds[Axes.ROUND] for _, inds in spot_attributes))

    all_spots = pd.concat([sa.data for sa, inds in spot_attributes], sort=True)
    # this drop call ensures only x, y, z, radius, and quality, are passed to the IntensityTable
    features_coordinates = all_spots.drop(['spot_id', 'intensity'], axis=1)

    intensity_table = IntensityTable.zeros(
        SpotAttributes(features_coordinates),
        ch_values,
        round_values,
    )

    i = 0
    for attrs, inds in spot_attributes:
        for _, row in attrs.data.iterrows():
            selector = dict(features=i, c=inds[Axes.CH], r=inds[Axes.ROUND])
            intensity_table.loc[selector] = row['intensity']
            i += 1

    return intensity_table
Esempio n. 17
0
    def from_image_stack(cls,
                         image_stack,
                         crop_x: int = 0,
                         crop_y: int = 0,
                         crop_z: int = 0) -> "IntensityTable":
        """Generate an IntensityTable from all the pixels in the ImageStack

        Parameters
        ----------
        crop_x : int
            Number of pixels to crop from both top and bottom of x.
        crop_y : int
            Number of pixels to crop from both top and bottom of y.
        crop_z : int
            Number of pixels to crop from both top and bottom of z.
        image_stack : ImageStack
            ImageStack containing pixels to be treated as intensities.

        Returns
        -------
        IntensityTable :
            IntensityTable containing one intensity per pixel (across channels and rounds).

        """

        # verify the image is large enough to crop
        assert crop_z * 2 < image_stack.shape['z']
        assert crop_y * 2 < image_stack.shape['y']
        assert crop_x * 2 < image_stack.shape['x']

        zmin = image_stack.axis_labels(Axes.ZPLANE)[crop_z]
        ymin = crop_y
        xmin = crop_x
        zmax = image_stack.axis_labels(Axes.ZPLANE)[-crop_z - 1]
        ymax = image_stack.shape['y'] - crop_y
        xmax = image_stack.shape['x'] - crop_x
        cropped_stack = image_stack.sel({
            Axes.ZPLANE: (zmin, zmax),
            Axes.Y: (ymin, ymax),
            Axes.X: (xmin, xmax)
        })

        data = cropped_stack.xarray.transpose(
            Axes.ZPLANE.value,
            Axes.Y.value,
            Axes.X.value,
            Axes.CH.value,
            Axes.ROUND.value,
        )

        # (pixels, ch, round)
        intensity_data = data.values.reshape(-1, image_stack.num_chs,
                                             image_stack.num_rounds)

        # IntensityTable pixel coordinates
        z = image_stack.axis_labels(Axes.ZPLANE)
        y = np.arange(ymin, ymax)
        x = np.arange(xmin, xmax)

        feature_attribute_data = pd.DataFrame(data=np.array(
            list(product(z, y, x))),
                                              columns=['z', 'y', 'x'])
        feature_attribute_data[Features.SPOT_RADIUS] = np.full(
            feature_attribute_data.shape[0], fill_value=0.5)

        pixel_coordinates = SpotAttributes(feature_attribute_data)

        return IntensityTable.from_spot_data(
            intensity_data,
            pixel_coordinates,
            image_stack.axis_labels(Axes.CH),
            image_stack.axis_labels(Axes.ROUND),
        )
Esempio n. 18
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
Esempio n. 19
0
    def image_to_spots(self,
                       data_image: np.ndarray) -> PerImageSliceSpotResults:
        """measure attributes of spots detected by binarizing the image using the selected threshold

        Parameters
        ----------
        data_image : np.ndarray
            image containing spots to be detected

        Returns
        -------
        PerImageSpotResults :
            includes a SpotAttributes DataFrame of metadata containing the coordinates, intensity
            and radius of each spot, as well as any extra information collected during spot finding.
        """

        optimal_threshold, thresholds, spot_counts = self._compute_threshold(
            data_image)

        data_image = np.asarray(data_image)

        # identify each spot's size by binarizing and calculating regionprops
        masked_image = data_image[:, :] > optimal_threshold
        labels = label(masked_image)[0]
        spot_props = regionprops(np.squeeze(labels))

        # mask spots whose areas are too small or too large
        for spot_prop in spot_props:
            if spot_prop.area < self.min_obj_area or spot_prop.area > self.max_obj_area:
                masked_image[0, spot_prop.coords[:, 0],
                             spot_prop.coords[:, 1]] = 0

        # store re-calculated regionprops and labels based on the area-masked image
        labels = label(masked_image)[0]

        if self.verbose:
            print('computing final spots ...')

        spot_coords = peak_local_max(data_image,
                                     min_distance=self.min_distance,
                                     threshold_abs=optimal_threshold,
                                     exclude_border=False,
                                     indices=True,
                                     num_peaks=np.inf,
                                     footprint=None,
                                     labels=labels)

        res = {
            Axes.X.value:
            spot_coords[:, 2],
            Axes.Y.value:
            spot_coords[:, 1],
            Axes.ZPLANE.value:
            spot_coords[:, 0],
            Features.SPOT_RADIUS:
            1,
            Features.SPOT_ID:
            np.arange(spot_coords.shape[0]),
            Features.INTENSITY:
            data_image[spot_coords[:, 0], spot_coords[:, 1], spot_coords[:,
                                                                         2]],
        }
        extras: Mapping[str, Any] = {
            "threshold": optimal_threshold,
            "thresholds": thresholds,
            "spot_counts": spot_counts
        }

        return PerImageSliceSpotResults(spot_attrs=SpotAttributes(
            pd.DataFrame(res)),
                                        extras=extras)
Esempio n. 20
0
    def image_to_spots(
            self, data_image: Union[np.ndarray,
                                    xr.DataArray]) -> SpotAttributes:
        """measure attributes of spots detected by binarizing the image using the selected threshold

        Parameters
        ----------
        data_image : Union[np.ndarray, xr.DataArray]
            image containing spots to be detected

        Returns
        -------
        SpotAttributes
            Attributes for each detected spot
        """

        threshold = self._compute_threshold(data_image)

        data_image = np.asarray(data_image)

        # identify each spot's size by binarizing and calculating regionprops
        masked_image = data_image[:, :] > threshold
        labels = label(masked_image)[0]
        spot_props = regionprops(np.squeeze(labels))

        # mask spots whose areas are too small or too large
        for spot_prop in spot_props:
            if spot_prop.area < self.min_obj_area or spot_prop.area > self.max_obj_area:
                masked_image[0, spot_prop.coords[:, 0],
                             spot_prop.coords[:, 1]] = 0

        # store re-calculated regionprops and labels based on the area-masked image
        labels = label(masked_image)[0]

        if self.verbose:
            print('computing final spots ...')

        spot_coords = peak_local_max(data_image,
                                     min_distance=self.min_distance,
                                     threshold_abs=threshold,
                                     exclude_border=False,
                                     indices=True,
                                     num_peaks=np.inf,
                                     footprint=None,
                                     labels=labels)

        # TODO how to get the radius? unlikely that this can be pulled out of
        # self._spot_props, since the last call to peak_local_max can find multiple
        # peaks per label
        res = {
            Axes.X.value:
            spot_coords[:, 2],
            Axes.Y.value:
            spot_coords[:, 1],
            Axes.ZPLANE.value:
            spot_coords[:, 0],
            Features.SPOT_RADIUS:
            1,
            Features.SPOT_ID:
            np.arange(spot_coords.shape[0]),
            Features.INTENSITY:
            data_image[spot_coords[:, 0], spot_coords[:, 1], spot_coords[:, 2]]
        }

        return SpotAttributes(pd.DataFrame(res))