Esempio n. 1
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. 2
0
def build_spot_traces_exact_match(spot_results: SpotFindingResults,
                                  **kwargs) -> IntensityTable:
    """
    Combines spots found in matching x/y positions across rounds and channels of
    an ImageStack into traces represented as an IntensityTable.

    Parameters
    -----------
    spot_results: SpotFindingResults
        Spots found across rounds/channels of an ImageStack
    """
    # create IntensityTable with same x/y/z info accross all r/ch
    spot_attributes = list(spot_results.values())[0].spot_attrs
    intensity_table = IntensityTable.zeros(
        spot_attributes=spot_attributes,
        round_labels=spot_results.round_labels,
        ch_labels=spot_results.ch_labels,
    )
    for r, c in spot_results.keys():
        value = spot_results[{
            Axes.ROUND: r,
            Axes.CH: c
        }].spot_attrs.data[Features.INTENSITY]
        # if no exact match set value to 0
        value = 0 if value.empty else value
        intensity_table.loc[dict(c=c, r=r)] = value
    return intensity_table
Esempio n. 3
0
    def run(self, spots: SpotFindingResults, *args) -> DecodedIntensityTable:
        """
        Decode spots by looking up the associated target value for the round and ch each spot is
        in.

        Parameters
        ----------
        spots: SpotFindingResults
            A Dict of tile indices and their corresponding measured spots

        Returns
        -------
        DecodedIntensityTable :
            IntensityTable decoded and appended with Features.TARGET and values.

        """
        lookup_table: Dict[Tuple, str] = {}
        for target in self.codebook[Features.TARGET]:
            for ch_label in self.codebook[Axes.CH.value]:
                for round_label in self.codebook[Axes.ROUND.value]:
                    if self.codebook.loc[target, round_label, ch_label]:
                        lookup_table[(int(round_label),
                                      int(ch_label))] = str(target.values)

        for r_ch_index, results in spots.items():
            target = lookup_table[
                r_ch_index] if r_ch_index in lookup_table else 'nan'
            results.spot_attrs.data[Features.TARGET] = target
        intensities = build_traces_sequential(spots)
        return DecodedIntensityTable(intensities)
Esempio n. 4
0
def _merge_spots_by_round(
        spot_results: SpotFindingResults) -> Dict[int, pd.DataFrame]:
    """Merge DataFrames containing spots from different channels into one DataFrame per round.

    Parameters
    ----------
    spot_results : Dict[Tuple[int, int], pd.DataFrame]
        Output of _find_spots. Dictionary mapping (round, channel) volumes to the spots detected
        in them.

    Returns
    -------
    Dict[int, pd.DataFrame]
        Dictionary mapping round volumes to the spots detected in them. Contains an additional
        column labeled by Axes.CH which identifies the channel in which a given spot was
        detected.

    """

    # add channel information to each table and add it to round_data
    round_data: Mapping[int, List] = defaultdict(list)
    for (r, c), df in spot_results.items():
        df = df.spot_attrs.data
        df[Axes.CH.value] = np.full(df.shape[0], c)
        round_data[r].append(df)

    # create one dataframe per round
    round_dataframes = {
        k: pd.concat(v, axis=0).reset_index().drop('index', axis=1)
        for k, v in round_data.items()
    }

    return round_dataframes
Esempio n. 5
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 run(
            self,
            image_stack: ImageStack,
            reference_image: Optional[ImageStack] = None,
            n_processes: Optional[int] = None,
            *args,
            **kwargs
    ) -> SpotFindingResults:
        """
        Find spots in the given ImageStack using a local maxima finding algorithm.
        If a reference image is provided the spots will be detected there then measured
        across all rounds and channels in the corresponding ImageStack. If a reference_image
        is not provided spots will be detected _independently_ in each channel. This assumes
        a non-multiplex imaging experiment, as only one (ch, round) will be measured for each spot.

        Parameters
        ----------
        image_stack : ImageStack
            ImageStack where we find the spots in.
        reference_image : Optional[ImageStack]
            (Optional) a reference image. If provided, spots will be found in this image, and then
            the locations that correspond to these spots will be measured across each channel.
        n_processes : Optional[int] = None,
            Number of processes to devote to spot finding.
        """
        spot_finding_method = partial(self.image_to_spots, **self.kwargs)
        if reference_image:
            shape = reference_image.shape
            assert shape[Axes.ROUND] == 1
            assert shape[Axes.CH] == 1
            spot_attributes_lists = reference_image.transform(
                func=spot_finding_method,
                group_by=determine_axes_to_group_by(self.is_volume),
                n_processes=n_processes
            )

            spot_attributes_lists = combine_spot_attributes_by_round_channel(spot_attributes_lists)
            assert len(spot_attributes_lists) == 1
            results = spot_finding_utils.measure_intensities_at_spot_locations_across_imagestack(
                data_image=image_stack,
                reference_spots=spot_attributes_lists[0][0],
                measurement_function=self.measurement_function)
        else:
            spot_attributes_lists = image_stack.transform(
                func=spot_finding_method,
                group_by=determine_axes_to_group_by(self.is_volume),
                n_processes=n_processes
            )
            spot_attributes_lists = combine_spot_attributes_by_round_channel(spot_attributes_lists)
            results = SpotFindingResults(imagestack_coords=image_stack.xarray.coords,
                                         log=image_stack.log,
                                         spot_attributes_list=spot_attributes_lists)
        return results
Esempio n. 7
0
    def run(
        self,
        image_stack: ImageStack,
        reference_image: Optional[ImageStack] = None,
        n_processes: Optional[int] = None,
        *args,
    ) -> SpotFindingResults:
        """
        Find spots in the given ImageStack using a gaussian blob finding algorithm.
        If a reference image is provided the spots will be detected there then measured
        across all rounds and channels in the corresponding ImageStack. If a reference_image
        is not provided spots will be detected _independently_ in each channel. This assumes
        a non-multiplex imaging experiment, as only one (ch, round) will be measured for each spot.

        Parameters
        ----------
        image_stack : ImageStack
            ImageStack where we find the spots in.
        reference_image : Optional[ImageStack]
            (Optional) a reference image. If provided, spots will be found in this image, and then
            the locations that correspond to these spots will be measured across each channel.
        n_processes : Optional[int] = None,
            Number of processes to devote to spot finding.
        """
        spot_finding_method = partial(self.image_to_spots, *args)
        if reference_image:
            data_image = reference_image._squeezed_numpy(
                *{Axes.ROUND, Axes.CH})
            if self.detector_method is blob_doh and data_image.ndim > 2:
                raise ValueError("blob_doh only support 2d images")
            reference_spots = spot_finding_method(data_image)
            results = spot_finding_utils.measure_intensities_at_spot_locations_across_imagestack(
                data_image=image_stack,
                reference_spots=reference_spots,
                measurement_function=self.measurement_function)
        else:
            if self.detector_method is blob_doh and self.is_volume:
                raise ValueError("blob_doh only support 2d images")
            spot_attributes_list = image_stack.transform(
                func=spot_finding_method,
                group_by=determine_axes_to_group_by(self.is_volume),
                n_processes=n_processes)
            results = SpotFindingResults(
                imagestack_coords=image_stack.xarray.coords,
                log=image_stack.log,
                spot_attributes_list=spot_attributes_list)
        return results
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. 9
0
    def run(
        self,
        image_stack: ImageStack,
        reference_image: Optional[ImageStack] = None,
        n_processes: Optional[int] = None,
        *args,
    ) -> SpotFindingResults:
        """
        Find spots.

        Parameters
        ----------
        image_stack : ImageStack
            ImageStack where we find the spots in.
        reference_image : xr.DataArray
            (Optional) a reference image. If provided, spots will be found in this image, and then
            the locations that correspond to these spots will be measured across each channel.
        n_processes : Optional[int] = None,
            Number of processes to devote to spot finding.
        """
        spot_finding_method = partial(self.image_to_spots, *args)
        if reference_image:
            data_image = reference_image._squeezed_numpy(
                *{Axes.ROUND, Axes.CH})
            reference_spots = spot_finding_method(data_image)
            results = spot_finding_utils.measure_intensities_at_spot_locations_across_imagestack(
                image_stack,
                reference_spots,
                measurement_function=self.measurement_function,
                radius_is_gyration=self.radius_is_gyration)
        else:
            spot_attributes_list = image_stack.transform(
                func=spot_finding_method,
                group_by=determine_axes_to_group_by(self.is_volume),
                n_processes=n_processes)
            results = SpotFindingResults(
                imagestack_coords=image_stack.xarray.coords,
                log=image_stack.log,
                spot_attributes_list=spot_attributes_list)
        return results
            args.segmentation_loc
    ):  # if this is true, going to assume baysorStaged dir-wise FOV structure
        segmentation = {}
        for name in transcripts.keys():
            segmentation[name] = pd.read_csv("{}/{}/segmentation.csv".format(
                args.segmentation_loc, name))

    spots = False
    if args.spots_pkl:
        spots = pickle.load(open(args.spots_pkl, "rb"))
    elif args.has_spots:
        # load spots from exp dir
        spots = {}
        for k in transcripts.keys():
            spots[k] = SpotFindingResults.load(
                "{}/spots/{}_SpotFindingResults.json".format(
                    args.exp_output, k))

    size = [0, 0, 0]
    if args.x_size:  # specify in CWL that all or none must be specified, only needed when doRipley
        size[0] = args.x_size
        size[1] = args.y_size
        size[2] = args.z_size

    fovs = False
    if args.exp_output:
        # reading in from experiment can have multiple FOVs
        fovs = [k for k in transcripts.keys()]
    run(transcripts, codebook, size, fovs, spots, roi, segmentation,
        args.run_ripley, args.save_pdf)