Skip to content

Commit

Permalink
adding coordinate support to SpotFindingResults
Browse files Browse the repository at this point in the history
  • Loading branch information
Shannon Axelrod committed Sep 23, 2019
1 parent 414ebc6 commit 403b0bd
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 25 deletions.
36 changes: 26 additions & 10 deletions starfish/core/intensity_table/intensity_table_coordinates.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
from typing import Callable
from typing import Callable, Hashable, Mapping, Optional

import numpy as np
import xarray as xr

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.intensity_table.intensity_table import IntensityTable
from starfish.core.types import Axes, Coordinates, Features
from starfish.core.types import Axes, Coordinates, Features, SpotFindingResults


def transfer_physical_coords_from_imagestack_to_intensity_table(
image_stack: ImageStack, intensity_table: IntensityTable
def transfer_physical_coords_to_intensity_table(
*,
intensity_table: IntensityTable,
image_stack: Optional[ImageStack] = None,
spots: Optional[SpotFindingResults] = None,
) -> IntensityTable:
"""
Transfers physical coordinates from an Imagestack's coordinates xarray to an intensity table
Transfers physical coordinates from either an Imagestack or SpotFindingResults 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
"""
# TODO shanaxel42 consider refactoring pixel case where were can just reshape from Imagestack

pairs = (
(Axes.X.value, Coordinates.X.value),
(Axes.Y.value, Coordinates.Y.value),
(Axes.ZPLANE.value, Coordinates.Z.value)
)
if bool(image_stack) == bool(spots):
raise ValueError("Must provide either SpotFindingResults or ImageStack to calculate "
"coordinates from, and not both.")

coord_ranges: Mapping[Hashable, xr.DataArray]
if image_stack is not None:
coord_ranges = {
Axes.X.value: image_stack.xarray[Coordinates.X.value],
Axes.Y.value: image_stack.xarray[Coordinates.Y.value],
Axes.ZPLANE.value: image_stack.xarray[Coordinates.Z.value]
}
elif spots is not None:
coord_ranges = spots.physical_coord_ranges

# make sure the intensity table gets empty metadata if there are no intensities
if intensity_table.sizes[Features.AXIS] == 0:
Expand All @@ -35,15 +51,15 @@ def transfer_physical_coords_from_imagestack_to_intensity_table(
return intensity_table

for axis, coord in pairs:
imagestack_pixels: xr.DataArray = image_stack.xarray[axis]
pixels: xr.DataArray = coord_ranges[axis]
intensity_table_pixel_offsets: np.ndarray = intensity_table[axis].values

# can't interpolate if the axis size == 1, so just select in that case.
coordinate_fetcher: Callable
if len(imagestack_pixels) == 1:
coordinate_fetcher = imagestack_pixels.sel
if len(pixels) == 1:
coordinate_fetcher = pixels.sel
else:
coordinate_fetcher = imagestack_pixels.interp
coordinate_fetcher = pixels.interp
coordinates = coordinate_fetcher({axis: intensity_table_pixel_offsets})[coord]

intensity_table[coord] = xr.DataArray(coordinates.values, dims='features')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .factories import synthetic_decoded_intenisty_table
from ..intensity_table import IntensityTable
from ..intensity_table_coordinates import (
transfer_physical_coords_from_imagestack_to_intensity_table,
transfer_physical_coords_to_intensity_table,
)

NUMBER_SPOTS = 10
Expand Down Expand Up @@ -46,7 +46,8 @@ def test_tranfering_physical_coords_to_intensity_table():
n_spots=NUMBER_SPOTS
)

intensities = transfer_physical_coords_from_imagestack_to_intensity_table(stack, intensities)
intensities = transfer_physical_coords_to_intensity_table(intensity_table=intensities,
image_stack=stack)

# Assert that new cords were added
xc = intensities.coords[Coordinates.X]
Expand Down Expand Up @@ -100,8 +101,8 @@ def test_tranfering_physical_coords_to_expression_matrix():
n_spots=NUMBER_SPOTS
)

intensities = transfer_physical_coords_from_imagestack_to_intensity_table(
stack, decoded_intensities)
intensities = transfer_physical_coords_to_intensity_table(
image_stack=stack, intensity_table=decoded_intensities)

# Check that error is thrown before target assignment
try:
Expand Down
6 changes: 3 additions & 3 deletions starfish/core/spots/DetectPixels/pixel_spot_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.intensity_table.intensity_table import IntensityTable
from starfish.core.intensity_table.intensity_table_coordinates import \
transfer_physical_coords_from_imagestack_to_intensity_table
transfer_physical_coords_to_intensity_table
from ._base import DetectPixelsAlgorithm
from .combine_adjacent_features import CombineAdjacentFeatures, ConnectedComponentDecodingResult

Expand Down Expand Up @@ -85,6 +85,6 @@ def run(
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)
transfer_physical_coords_to_intensity_table(image_stack=primary_image,
intensity_table=decoded_spots)
return decoded_spots, image_decoding_results
6 changes: 3 additions & 3 deletions starfish/core/spots/DetectSpots/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.intensity_table.intensity_table import IntensityTable
from starfish.core.intensity_table.intensity_table_coordinates import \
transfer_physical_coords_from_imagestack_to_intensity_table
transfer_physical_coords_to_intensity_table
from starfish.core.types import Axes, Features, Number, SpotAttributes


Expand Down Expand Up @@ -242,7 +242,7 @@ def detect_spots(data_stack: ImageStack,
)
intensity_table = concatenate_spot_attributes_to_intensities(spot_attributes_list)

transfer_physical_coords_from_imagestack_to_intensity_table(image_stack=data_stack,
intensity_table=intensity_table)
transfer_physical_coords_to_intensity_table(image_stack=data_stack,
intensity_table=intensity_table)

return intensity_table
4 changes: 2 additions & 2 deletions starfish/core/spots/DetectSpots/local_search_blob_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.intensity_table.intensity_table import IntensityTable
from starfish.core.intensity_table.intensity_table_coordinates import \
transfer_physical_coords_from_imagestack_to_intensity_table
transfer_physical_coords_to_intensity_table
from starfish.core.types import Axes, Features, Number, SpotAttributes
from ._base import DetectSpotsAlgorithm

Expand Down Expand Up @@ -435,7 +435,7 @@ def run(
anchor_round=self.anchor_round
)

transfer_physical_coords_from_imagestack_to_intensity_table(
transfer_physical_coords_to_intensity_table(
image_stack=primary_image, intensity_table=intensity_table
)

Expand Down
22 changes: 19 additions & 3 deletions starfish/core/types/_spot_finding_results.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from typing import List, Mapping, MutableMapping, Optional, Tuple
from typing import Hashable, List, Mapping, MutableMapping, Optional, Tuple

import xarray as xr

from starfish.core.types import Axes, Coordinates, SpotAttributes

from starfish.core.types import Axes, SpotAttributes

AXES_ORDER = (Axes.ROUND, Axes.CH)

Expand All @@ -12,7 +15,7 @@ class SpotFindingResults:
SpotAttributes.
"""

def __init__(self, spot_attributes_list: Optional[List[Tuple]] = None):
def __init__(self, imagestack_coords, spot_attributes_list: Optional[List[Tuple]] = None):
"""
Construct a SpotFindingResults instance
Expand All @@ -28,6 +31,10 @@ def __init__(self, spot_attributes_list: Optional[List[Tuple]] = None):
indices: spots
for indices, spots in spot_attributes_list
}
self.physical_coord_ranges: Mapping[Hashable, xr.DataArray] = {
Axes.X.value: imagestack_coords[Coordinates.X.value],
Axes.Y.value: imagestack_coords[Coordinates.Y.value],
Axes.ZPLANE.value: imagestack_coords[Coordinates.Z.value]}

def __setitem__(self, indices: Mapping[Axes, int], spots: SpotAttributes):
"""
Expand Down Expand Up @@ -83,4 +90,13 @@ def ch_labels(self):
"""
Return the set of ch labels in the SpotFindingResults
"""

return sorted(set(ch for (r, ch) in self.keys()))

@property
def get_physical_coord_ranges(self) -> Mapping[Hashable, xr.DataArray]:
"""
Returns the physical coordinate ranges the SpotResults cover. Needed
information for calculating the physical coordinate values of decoded spots.
"""
return self.physical_coord_ranges

0 comments on commit 403b0bd

Please sign in to comment.