コード例 #1
0
    def _assign(
        label_image: np.ndarray,
        intensities: IntensityTable,
        in_place: bool,
    ) -> IntensityTable:

        if len(label_image.shape) == 3:
            cell_ids = label_image[
                intensities[Axes.ZPLANE.value].values,
                intensities[Axes.Y.value].values,
                intensities[Axes.X.value].values
            ]
        elif len(label_image.shape) == 2:
            cell_ids = label_image[
                intensities[Axes.Y.value].values,
                intensities[Axes.X.value].values
            ]
        else:
            raise ValueError(
                f"`label_image` must be 2 or 3 dimensional, not {len(label_image.shape)}D."
            )

        if not in_place:
            intensities = intensities.copy()

        intensities[Features.CELL_ID] = (Features.AXIS, cell_ids)

        return intensities
コード例 #2
0
def _max_intensity_table_maintain_dims(
    intensity_table: IntensityTable,
    dimensions: Set[Axes],
) -> IntensityTable:
    """
    Maximum project an IntensityTable over dimensions, retaining singletons in place of dimensions
    reduced by the max operation.

    For example, xarray.max(Axes.CH.value) would usually produce a 2-d (features, rounds) output.
    This function ensures that the output is instead (features, 1, rounds), by inserting singleton
    dimensions in place of any projected axes.

    Parameters
    ----------
    intensity_table : IntensityTable
        Input intensities
    dimensions : Set[Axes]
        Dimensions to project

    Returns
    -------
    IntensityTable :
        full-dimensionality (3-d) IntensityTable

    """
    str_dimensions = _normalize_axes(dimensions)
    initial_dimensions = OrderedDict(intensity_table.sizes)
    projected_intensities = intensity_table.max(str_dimensions)
    expanded_intensities = projected_intensities.expand_dims(str_dimensions)
    return expanded_intensities.transpose(*tuple(initial_dimensions.keys()))
コード例 #3
0
    def _cli(ctx, label_image, intensities, output):

        print('Assigning targets to cells...')
        ctx.obj = dict(component=TargetAssignment,
                       output=output,
                       intensity_table=IntensityTable.load(intensities),
                       label_image=imread(label_image))
コード例 #4
0
    def run(
        self, stack: ImageStack,
    ) -> Tuple[IntensityTable, ConnectedComponentDecodingResult]:
        """decode pixels and combine them into spots using connected component labeling

        Parameters
        ----------
        stack : ImageStack
            ImageStack containing spots

        Returns
        -------
        IntensityTable :
            IntensityTable containing decoded spots
        ConnectedComponentDecodingResult :
            Results of connected component labeling

        """
        pixel_intensities = IntensityTable.from_image_stack(
            stack, crop_x=self.crop_x, crop_y=self.crop_y, crop_z=self.crop_z)
        decoded_intensities = self.codebook.metric_decode(
            pixel_intensities,
            max_distance=self.distance_threshold,
            min_intensity=self.magnitude_threshold,
            norm_order=self.norm_order,
            metric=self.metric
        )
        caf = CombineAdjacentFeatures(
            min_area=self.min_area,
            max_area=self.max_area,
            mask_filtered_features=True
        )
        decoded_spots, image_decoding_results = caf.run(intensities=decoded_intensities)

        return decoded_spots, image_decoding_results
コード例 #5
0
ファイル: synthesize.py プロジェクト: xchang1/starfish
 def intensities(self, codebook=None) -> IntensityTable:
     if codebook is None:
         codebook = self.codebook()
     intensities = IntensityTable.synthetic_intensities(
         codebook, self.n_z, self.height, self.width, self.n_spots,
         self.mean_fluor_per_spot, self.mean_photons_per_fluor)
     assert intensities.dtype == np.float32 and intensities.max() <= 1
     return intensities
コード例 #6
0
 def _cli(ctx, input, output, codebook):
     ctx.obj = dict(
         component=Decoder,
         input=input,
         output=output,
         intensities=IntensityTable.load(input),
         codebook=Codebook.from_json(codebook),
     )
コード例 #7
0
ファイル: _base_cli_test.py プロジェクト: xchang1/starfish
    def test_run_pipline(self):
        tempdir = exec.stages(self.stages, self.subdirs, keep_data=True)
        intensities = IntensityTable.load(
            os.path.join(tempdir, "results", self.spots_file))
        self.verify_results(intensities)

        if os.getenv("TEST_KEEP_DATA") is None:
            shutil.rmtree(tempdir)
コード例 #8
0
def test_imagestack_to_intensity_table_no_noise(
        synthetic_spot_pass_through_stack):
    codebook, intensity_table, image = synthetic_spot_pass_through_stack
    pixel_intensities = IntensityTable.from_image_stack(image)
    pixel_intensities = codebook.metric_decode(pixel_intensities,
                                               max_distance=0,
                                               min_intensity=1000,
                                               norm_order=2)
    assert isinstance(pixel_intensities, IntensityTable)
コード例 #9
0
 def _cli(ctx, input, output, codebook):
     """assign genes to spots"""
     ctx.obj = dict(
         component=Decoder,
         input=input,
         output=output,
         intensities=IntensityTable.load(input),
         codebook=codebook,
     )
コード例 #10
0
def test_imagestack_to_intensity_table():
    codebook, intensity_table, image = codebook_intensities_image_for_single_synthetic_spot(
    )
    pixel_intensities = IntensityTable.from_image_stack(image)
    pixel_intensities = codebook.metric_decode(pixel_intensities,
                                               max_distance=0,
                                               min_intensity=1000,
                                               norm_order=2)
    assert isinstance(pixel_intensities, IntensityTable)
コード例 #11
0
ファイル: detect.py プロジェクト: ttung/starfish
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
    n_ch = data_image.shape[Axes.CH]
    n_round = data_image.shape[Axes.ROUND]

    # construct the empty intensity table
    intensity_table = IntensityTable.empty_intensity_table(
        spot_attributes=spot_attributes,
        n_ch=n_ch,
        n_round=n_round,
    )

    # 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(range(n_ch), range(n_round))
    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[:, c, r] = blob_intensities

    return intensity_table
コード例 #12
0
def testing_data():
    np.random.seed(1)
    codebook = test_utils.codebook_array_factory()
    num_z, height, width = 3, 4, 5
    intensities = IntensityTable.synthetic_intensities(codebook,
                                                       num_z=num_z,
                                                       height=height,
                                                       width=width,
                                                       n_spots=4)
    return intensities
コード例 #13
0
def test_reshaping_between_stack_and_intensities():
    """
    transform an pixels of an ImageStack into an IntensityTable and back again, then verify that
    the created Imagestack is the same as the original
    """
    np.random.seed(777)
    image = ImageStack.from_numpy_array(
        np.random.rand(1, 2, 3, 4, 5).astype(np.float32))
    pixel_intensities = IntensityTable.from_image_stack(image, 0, 0, 0)
    image_shape = (image.shape['z'], image.shape['y'], image.shape['x'])
    image_from_pixels = pixel_intensities_to_imagestack(
        pixel_intensities, image_shape)
    assert np.array_equal(image.xarray, image_from_pixels.xarray)
コード例 #14
0
def transfer_physical_coords_from_imagestack_to_intensity_table(image_stack: ImageStack,
                                                                intensity_table: IntensityTable
                                                                ) -> IntensityTable:
    """
    Transfers physical coordinates from an Imagestack's coordinates xarray to an intensity table

    1. Creates three new coords on the intensity table (xc, yc, zc)
    2. For every spot:
        - Get pixel x,y values
        - Calculate the physical x,y values
        - Assign those values to the coords arrays for this spot
    """
    # Add three new coords to xarray (xc, yc, zc)
    num_features = intensity_table.sizes[Features.AXIS]
    intensity_table[Coordinates.X.value] = xr.DataArray(np.zeros(num_features, np.float32),
                                                        dims='features')
    intensity_table[Coordinates.Y.value] = xr.DataArray(np.zeros(num_features, np.float32),
                                                        dims='features')
    intensity_table[Coordinates.Z.value] = xr.DataArray(np.zeros(num_features, np.float32),
                                                        dims='features')
    for ind, spot in intensity_table.groupby(Features.AXIS):
        # Iterate through r, ch per spot
        for ch, _round in np.ndindex(spot.data.shape):
            # if non zero value set coords
            if spot[ch][_round].data == 0:
                continue
            # get pixel coords of this tile
            pixel_x = spot.coords[Axes.X].data
            pixel_y = spot.coords[Axes.Y].data
            pixel_z = spot.coords[Axes.ZPLANE].data
            tile_indices = {
                Axes.ROUND.value: _round,
                Axes.CH.value: ch,
                Axes.ZPLANE.value: pixel_z,
            }
            # Get corresponding physical coordinates
            physical_coords = get_physical_coordinates_of_spot(
                image_stack._coordinates,
                tile_indices,
                pixel_x,
                pixel_y,
                image_stack._tile_shape)
            # Assign to coordinates arrays
            intensity_table[Coordinates.X.value][ind] = physical_coords[0]
            intensity_table[Coordinates.Y.value][ind] = physical_coords[1]
            intensity_table[Coordinates.Z.value][ind] = physical_coords[2]
            break
    return intensity_table
コード例 #15
0
ファイル: pixel_spot_decoder.py プロジェクト: mmmika/starfish
    def run(
            self,
            primary_image: ImageStack,
            n_processes: Optional[int] = None,
            *args,
    ) -> Tuple[IntensityTable, ConnectedComponentDecodingResult]:
        """decode pixels and combine them into spots using connected component labeling

        Parameters
        ----------
        primary_image : ImageStack
            ImageStack containing spots
        n_processes : Optional[int]
            The number of processes to use for CombineAdjacentFeatures.
             If None, uses the output of os.cpu_count() (default = None).

        Returns
        -------
        IntensityTable :
            IntensityTable containing decoded spots
        ConnectedComponentDecodingResult :
            Results of connected component labeling

        """
        pixel_intensities = IntensityTable.from_image_stack(
            primary_image, crop_x=self.crop_x, crop_y=self.crop_y, crop_z=self.crop_z)
        decoded_intensities = self.codebook.metric_decode(
            pixel_intensities,
            max_distance=self.distance_threshold,
            min_intensity=self.magnitude_threshold,
            norm_order=self.norm_order,
            metric=self.metric
        )
        caf = CombineAdjacentFeatures(
            min_area=self.min_area,
            max_area=self.max_area,
            mask_filtered_features=True
        )
        decoded_spots, image_decoding_results = caf.run(intensities=decoded_intensities,
                                                        n_processes=n_processes)

        transfer_physical_coords_from_imagestack_to_intensity_table(image_stack=primary_image,
                                                                    intensity_table=decoded_spots)
        return decoded_spots, image_decoding_results
コード例 #16
0
ファイル: detect.py プロジェクト: neuromusic/starfish
def concatenate_spot_attributes_to_intensities(
    spot_attributes: Sequence[Tuple[SpotAttributes, Dict[Indices, 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[Indices, 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

    """
    n_ch: int = max(inds[Indices.CH] for _, inds in spot_attributes) + 1
    n_round: int = max(inds[Indices.ROUND] for _, inds in spot_attributes) + 1

    all_spots = pd.concat([sa.data for sa, inds in spot_attributes])
    # 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.empty_intensity_table(
        SpotAttributes(features_coordinates),
        n_ch,
        n_round,
    )

    i = 0
    for attrs, inds in spot_attributes:
        for _, row in attrs.data.iterrows():
            intensity_table[i, inds[Indices.CH],
                            inds[Indices.ROUND]] = row['intensity']
            i += 1

    return intensity_table
コード例 #17
0
def small_intensity_table():
    intensities = np.array([
        [[0, 1], [1, 0]],
        [[1, 0], [0, 1]],
        [[0, 0], [1, 1]],
        [
            [0.5, 0.5],  # this one should fail decoding
            [0.5, 0.5]
        ],
        [[0.1, 0], [0,
                    0.1]],  # this one is a candidate for intensity filtering
    ])

    spot_attributes = pd.DataFrame(
        data={
            Indices.X.value: [0, 1, 2, 3, 4],
            Indices.Y.value: [3, 4, 5, 6, 7],
            Indices.Z.value: [0, 0, 0, 0, 0],
            Features.SPOT_RADIUS: [0.1, 2, 3, 2, 1]
        })

    return IntensityTable.from_spot_data(intensities, spot_attributes)
コード例 #18
0
    def _calculate_mean_pixel_traces(
            label_image: np.ndarray,
            intensities: IntensityTable,
    ) -> IntensityTable:
        """
        For all pixels that contribute to a connected component, calculate the mean value for
        each (ch, round), producing an average "trace" of a feature across the imaging experiment

        Parameters
        ----------
        label_image : np.ndarray
            An image where all pixels of a connected component share the same integer ID
        intensities : IntensityTable
            decoded intensities

        Returns
        -------
        IntensityTable :
            an IntensityTable where the number of features equals the number of connected components
            and the intensities of each each feature is its mean trace.

        """
        pixel_labels = label_image.reshape(-1)
        intensities['spot_id'] = (Features.AXIS, pixel_labels)
        mean_pixel_traces = intensities.groupby('spot_id').mean(Features.AXIS)
        mean_distances = intensities[Features.DISTANCE].groupby('spot_id').mean(Features.AXIS)
        mean_pixel_traces[Features.DISTANCE] = (
            'spot_id',
            np.ravel(mean_distances)
        )

        # the 0th pixel trace corresponds to background. If present, drop it.
        try:
            mean_pixel_traces = mean_pixel_traces.drop(0, dim='spot_id')
        except KeyError:
            pass

        return mean_pixel_traces
コード例 #19
0
    def _build_intensity_table(
        round_dataframes: Dict[int, pd.DataFrame],
        dist: pd.DataFrame,
        ind: pd.DataFrame,
        channels: Sequence[int],
        rounds: Sequence[int],
        search_radius: int,
        anchor_round: int,
    ) -> IntensityTable:
        """Construct an intensity table from the results of a local search over detected spots

        Parameters
        ----------
        round_dataframes : Dict[int, pd.DataFrame]
            Output from _merge_spots_by_round, contains mapping of image volumes from each round to
            all the spots detected in them.
        dist, ind : pd.DataFrame
            Output from _match_spots, contains distances and indices to the nearest spot for each
            spot in anchor_round.
        channels, rounds : Sequence[int]
            Channels and rounds present in the ImageStack from which spots were detected.
        search_radius : int
            The maximum (euclidean) distance in pixels for a spot to be considered matching in
            a round subsequent to the anchor round.
        anchor_round : int
            The imaging round to seed the local search from.

        """

        anchor_df = round_dataframes[anchor_round]

        # create empty IntensityTable filled with np.nan
        data = np.full((dist.shape[0], len(channels), len(rounds)),
                       fill_value=np.nan)
        dims = (Features.AXIS, Axes.CH.value, Axes.ROUND.value)
        coords = {
            Features.SPOT_RADIUS:
            (Features.AXIS, anchor_df[Features.SPOT_RADIUS]),
            Axes.ZPLANE.value: (Features.AXIS, anchor_df[Axes.ZPLANE]),
            Axes.Y.value: (Features.AXIS, anchor_df[Axes.Y]),
            Axes.X.value: (Features.AXIS, anchor_df[Axes.X]),
            Axes.ROUND.value: (Axes.ROUND.value, rounds),
            Axes.CH.value: (Axes.CH.value, channels)
        }
        intensity_table = IntensityTable(data=data, dims=dims, coords=coords)

        # fill IntensityTable
        for r in rounds:

            # get intensity data and indices
            spot_indices = ind[r]
            intensity_data = round_dataframes[r].loc[spot_indices, 'intensity']
            channel_index = round_dataframes[r].loc[spot_indices, Axes.CH]
            round_index = np.full(ind.shape[0], fill_value=r, dtype=int)
            feature_index = np.arange(ind.shape[0], dtype=int)

            # mask spots that are outside the search radius
            mask = np.asarray(
                dist[r] < search_radius)  # indices need not match
            feature_index = feature_index[mask]
            channel_index = channel_index[mask]
            round_index = round_index[mask]
            intensity_data = intensity_data[mask]

            # need numpy indexing to set values in vectorized manner
            intensity_table.values[feature_index, channel_index,
                                   round_index] = intensity_data

        return intensity_table
コード例 #20
0
ファイル: imagestack.py プロジェクト: xchang1/starfish
    def synthetic_spots(
        cls,
        intensities: IntensityTable,
        num_z: int,
        height: int,
        width: int,
        n_photons_background=1000,
        point_spread_function=(4, 2, 2),
        camera_detection_efficiency=0.25,
        background_electrons=1,
        graylevel: float = 37000.0 / 2**16,
        ad_conversion_bits=16,
    ) -> "ImageStack":
        """Generate a synthetic ImageStack from a set of Features stored in an IntensityTable

        Parameters
        ----------
        intensities : IntensityTable
            IntensityTable containing coordinates of fluorophores. Used to position and generate
            spots in the output ImageStack
        num_z : int
            Number of z-planes in the ImageStack
        height : int
            Height in pixels of the ImageStack
        width : int
            Width in pixels of the ImageStack
        n_photons_background : int
            Poisson rate for the number of background photons to add to each pixel of the image.
            Set this parameter to 0 to eliminate background.
            (default 1000)
        point_spread_function : Tuple[int]
            The width of the gaussian density wherein photons spread around their light source.
            Set to zero to eliminate this (default (4, 2, 2))
        camera_detection_efficiency : float
            The efficiency of the camera to detect light. Set to 1 to remove this filter (default
            0.25)
        background_electrons : int
            Poisson rate for the number of spurious electrons detected per pixel during image
            capture by the camera (default 1)
        graylevel : float
            The number of shades of gray displayable by the synthetic camera. Larger numbers will
            produce higher resolution images (default 37000 / 2 ** 16)
        ad_conversion_bits : int
            The number of bits used during analog to visual conversion (default 16)

        Returns
        -------
        ImageStack :
            synthetic spots

        """
        # check some params
        if not 0 < camera_detection_efficiency <= 1:
            raise ValueError(
                f'invalid camera_detection_efficiency value: {camera_detection_efficiency}. '
                f'Must be in the interval (0, 1].')

        def select_uint_dtype(array):
            """choose appropriate dtype based on values of an array"""
            max_val = np.max(array)
            for dtype in (np.uint8, np.uint16, np.uint32):
                if max_val <= np.iinfo(dtype).max:
                    return array.astype(dtype)
            raise ValueError(
                'value exceeds dynamic range of largest skimage-supported type'
            )

        # make sure requested dimensions are large enough to support intensity values
        indices = zip((Indices.Z.value, Indices.Y.value, Indices.X.value),
                      (num_z, height, width))
        for index, requested_size in indices:
            required_size = intensities.coords[index].values.max()
            if required_size > requested_size:
                raise ValueError(
                    f'locations of intensities contained in table exceed the size of requested '
                    f'dimension {index}. Required size {required_size} > {requested_size}.'
                )

        # create an empty array of the correct size
        image = np.zeros(
            (intensities.sizes[Indices.ROUND.value],
             intensities.sizes[Indices.CH.value], num_z, height, width),
            dtype=np.uint32)

        # starfish uses float images, but the logic here requires uint. We cast, and will cast back
        # at the end of the function
        intensities.values = img_as_uint(intensities)

        for ch, round_ in product(*(range(s) for s in intensities.shape[1:])):
            spots = intensities[:, ch, round_]

            # numpy deprecated casting a specific way of casting floats that is triggered in xarray
            with warnings.catch_warnings():
                warnings.simplefilter('ignore', FutureWarning)
                values = spots.where(spots, drop=True)

            image[round_, ch, values.z, values.y, values.x] = values

        intensities.values = img_as_float32(intensities)

        # add imaging noise
        image += np.random.poisson(n_photons_background,
                                   size=image.shape).astype(np.uint32)

        # blur image over coordinates, but not over round_/channels (dim 0, 1)
        sigma = (0, 0) + point_spread_function
        image = gaussian_filter(image, sigma=sigma, mode='nearest')

        image = image * camera_detection_efficiency

        image += np.random.normal(scale=background_electrons, size=image.shape)

        # mimic analog to digital conversion
        image = (image / graylevel).astype(int).clip(0, 2**ad_conversion_bits)

        # clip in case we've picked up some negative values
        image = np.clip(image, 0, a_max=None)

        # set the smallest int datatype that supports the data's intensity range
        image = select_uint_dtype(image)

        # convert to float for ImageStack
        with warnings.catch_warnings():
            # possible precision loss when casting from uint to float is acceptable
            warnings.simplefilter('ignore', UserWarning)
            image = img_as_float32(image)

        return cls.from_numpy_array(image)
コード例 #21
0
ファイル: codebook.py プロジェクト: neuromusic/starfish
    def metric_decode(self,
                      intensities: IntensityTable,
                      max_distance: Number,
                      min_intensity: Number,
                      norm_order: int,
                      metric: str = 'euclidean') -> IntensityTable:
        """Assign the closest target by euclidean distance to each feature in an intensity table

        Normalizes both the codes and the features to be unit vectors and finds the closest code
        for each feature

        Parameters
        ----------
        intensities : IntensityTable
            features to be decoded
        max_distance : Number
            maximum distance between a feature and its closest code for which the coded target will
            be assigned.
        min_intensity : Number
            minimum intensity for a feature to receive a target annotation
        norm_order : int
            the scipy.linalg norm to apply to normalize codes and intensities
        metric : str
            the sklearn metric string to pass to NearestNeighbors

        See Also
        --------
        The available norms for this function can be found at the following link:
        https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.linalg.norm.html

        Returns
        -------
        IntensityTable :
            Intensity table containing normalized intensities, target assignments, distances to
            the nearest code, and the filtering status of each feature.

        """

        self._validate_decode_intensity_input_matches_codebook_shape(
            intensities)

        # normalize both the intensities and the codebook
        norm_intensities, norms = self._normalize_features(
            intensities, norm_order=norm_order)
        norm_codes, _ = self._normalize_features(self, norm_order=norm_order)

        metric_outputs, targets = self._approximate_nearest_code(
            norm_codes, norm_intensities, metric=metric)

        # only targets with low distances and high intensities should be retained
        passes_filters = np.logical_and(norms >= min_intensity,
                                        metric_outputs <= max_distance,
                                        dtype=np.bool)

        # set targets, distances, and filtering results
        norm_intensities[Features.TARGET] = (Features.AXIS, targets)
        norm_intensities[Features.DISTANCE] = (Features.AXIS, metric_outputs)
        norm_intensities[Features.PASSES_THRESHOLDS] = (Features.AXIS,
                                                        passes_filters)

        # norm_intensities is a DataArray, make it back into an IntensityTable
        return IntensityTable(norm_intensities)
コード例 #22
0
    def run(
        self, intensities: IntensityTable
    ) -> Tuple[IntensityTable, ConnectedComponentDecodingResult]:
        """
        Execute the combine_adjacent_features method on an IntensityTable containing pixel
        intensities

        Parameters
        ----------
        intensities : IntensityTable
            Pixel intensities of an imaging experiment

        Returns
        -------
        IntensityTable :
            Table whose features comprise sets of adjacent pixels that decoded to the same target
        ConnectedComponentDecodingResult :
            NamedTuple containing :
                region_properties :
                    the properties of each connected component, in the same order as the
                    IntensityTable
                label_image : np.ndarray
                    An image where all pixels of a connected component share the same integer ID
                decoded_image : np.ndarray
                    Image whose pixels correspond to the targets that the given position in the
                    ImageStack decodes to.

        """

        # map target molecules to integers so they can be reshaped into an image that can
        # be subjected to a connected-component algorithm to find adjacent pixels with the
        # same targets
        targets = intensities[Features.TARGET].values
        target_map = TargetsMap(targets)

        # create the decoded_image
        decoded_image = self._intensities_to_decoded_image(
            intensities,
            target_map,
            self._mask_filtered,
        )

        # label the decoded image to extract connected component features
        label_image: np.ndarray = label(decoded_image,
                                        connectivity=self._connectivity)

        # calculate properties of each feature
        props: List = regionprops(np.squeeze(label_image))

        # calculate mean intensities across the pixels of each feature
        mean_pixel_traces = self._calculate_mean_pixel_traces(
            label_image,
            intensities,
        )

        # Create SpotAttributes and determine feature filtering outcomes
        spot_attributes, passes_filter = self._create_spot_attributes(
            props,
            decoded_image,
            target_map,
        )

        # augment the SpotAttributes with filtering results and distances from nearest codes
        spot_attributes.data[Features.DISTANCE] = mean_pixel_traces[
            Features.DISTANCE]
        spot_attributes.data[Features.PASSES_THRESHOLDS] = passes_filter

        # create new indexes for the output IntensityTable
        channel_index = mean_pixel_traces.indexes[Axes.CH]
        round_index = mean_pixel_traces.indexes[Axes.ROUND]
        coords = IntensityTable._build_xarray_coords(spot_attributes,
                                                     channel_index,
                                                     round_index)

        # create the output IntensityTable
        dims = (Features.AXIS, Axes.CH.value, Axes.ROUND.value)
        intensity_table = IntensityTable(data=mean_pixel_traces,
                                         coords=coords,
                                         dims=dims)

        # combine the various non-IntensityTable results into a NamedTuple before returning
        ccdr = ConnectedComponentDecodingResult(props, label_image,
                                                decoded_image)

        return intensity_table, ccdr
コード例 #23
0
def synthetic_intensity_table(loaded_codebook) -> IntensityTable:
    return IntensityTable.synthetic_intensities(loaded_codebook, n_spots=2)
コード例 #24
0
ファイル: codebook.py プロジェクト: neuromusic/starfish
    def decode_per_round_max(self,
                             intensities: IntensityTable) -> IntensityTable:
        """decode each feature by selecting the per-imaging-round max-valued channel

        Notes
        -----
        - If no code matches the per-round maximum for a feature, it will be assigned 'nan' instead
          of a target value
        - Numpy's argmax breaks ties by picking the first channel -- this can lead to
          unexpected results where some features with "tied" channels will decode, but others will
          be assigned 'nan'.

        Parameters
        ----------
        intensities : IntensityTable
            features to be decoded

        Returns
        -------
        IntensityTable :
            intensity table containing additional data variables for target assignments

        """
        def _view_row_as_element(array: np.ndarray) -> np.ndarray:
            """view an entire code as a single element

            This view allows vectors (codes) to be compared for equality without need for multiple
            comparisons by casting the data in each code to a structured dtype that registers as
            a single value

            Parameters
            ----------
            array : np.ndarray
                2-dimensional numpy array of shape (n_observations, (n_ch * n_round)) where
                observations may be either features or codes.

            Returns
            -------
            np.ndarray :
                1-dimensional vector of shape n_observations

            """
            nrows, ncols = array.shape
            dtype = {
                'names': ['f{}'.format(i) for i in range(ncols)],
                'formats': ncols * [array.dtype]
            }
            return array.view(dtype)

        self._validate_decode_intensity_input_matches_codebook_shape(
            intensities)

        max_channels = intensities.argmax(Indices.CH.value)
        codes = self.argmax(Indices.CH.value)

        # TODO ambrosejcarr, dganguli: explore this quality score further
        # calculate distance scores by evaluating the fraction of signal in each round that is
        # found in the non-maximal channels.
        max_intensities = intensities.max(Indices.CH.value)
        round_intensities = intensities.sum(Indices.CH.value)
        distance = 1 - (max_intensities / round_intensities).mean(
            Indices.ROUND.value)

        a = _view_row_as_element(codes.values.reshape(self.shape[0], -1))
        b = _view_row_as_element(
            max_channels.values.reshape(intensities.shape[0], -1))

        targets = np.full(intensities.shape[0],
                          fill_value=np.nan,
                          dtype=object)

        # decode the intensities
        for i in np.arange(codes.shape[0]):
            targets[np.where(a[i] == b)[0]] = codes[Features.TARGET][i]

        # a code passes filters if it decodes successfully
        passes_filters = ~pd.isnull(targets)

        intensities[Features.TARGET] = (Features.AXIS, targets.astype('U'))
        intensities[Features.DISTANCE] = (Features.AXIS, distance)
        intensities[Features.PASSES_THRESHOLDS] = (Features.AXIS,
                                                   passes_filters)

        return intensities