def test_from_numpy_array_raises_error_when_incorrect_dims_passed(): array = np.ones((2, 2), dtype=np.float32) # verify this method works with the correct shape image = ImageStack.from_numpy_array(array.reshape((1, 1, 1, 2, 2))) assert isinstance(image, ImageStack) with pytest.raises(ValueError): ImageStack.from_numpy_array(array.reshape((1, 1, 2, 2))) ImageStack.from_numpy_array(array.reshape((1, 2, 2))) ImageStack.from_numpy_array(array) ImageStack.from_numpy_array(array.reshape((1, 1, 1, 1, 2, 2)))
def test_max_projection_preserves_dtype(): original_dtype = np.float32 array = np.ones((2, 2, 2), dtype=original_dtype) image = ImageStack.from_numpy_array(array.reshape((1, 1, 2, 2, 2))) max_projection = image.max_proj(Indices.CH, Indices.ROUND, Indices.Z) assert max_projection.dtype == original_dtype
def pixel_intensities_to_imagestack( intensities: IntensityTable, image_shape: Tuple[int, int, int]) -> ImageStack: """Re-create the pixel intensities from an IntensityTable Parameters ---------- intensities : IntensityTable intensities to transform into an ImageStack image_shape : Tuple[int, int, int] the dimensions of z, y, and x for the original image that the intensity table was generated from Returns ------- ImageStack : ImageStack containing Intensity information """ # reverses the process used to produce the intensity table in to_pixel_intensities data = intensities.values.reshape([ *image_shape, intensities.sizes[Axes.CH], intensities.sizes[Axes.ROUND] ]) data = data.transpose(4, 3, 0, 1, 2) return ImageStack.from_numpy_array(data)
def test_match_histograms(): linear_gradient = np.linspace(0, 0.5, 2000, dtype=np.float32) image = linear_gradient.reshape(2, 4, 5, 5, 10) # linear_gradient = np.linspace(0, 1, 10)[::-1] # grad = np.repeat(linear_gradient[np.newaxis, :], 10, axis=0) # image2 = np.tile(grad, (1, 2, 2, 10, 10)) # because of how the image was structured, every volume should be the same after # quantile normalization stack = ImageStack.from_numpy_array(image) mh = MatchHistograms({Axes.CH, Axes.ROUND}) results = mh.run(stack) assert len(np.unique(results.xarray.sum(("x", "y", "z")))) == 1 # because here we are allowing variation to persist across rounds, each # round within each channel should be different mh = MatchHistograms({Axes.CH}) results2 = mh.run(stack) assert len(np.unique(results2.xarray.sum(("x", "y", "z")))) == 2 # same as above, but verifying this functions for a different data shape (2 rounds, 4 channels) mh = MatchHistograms({Axes.ROUND}) results2 = mh.run(stack) assert len(np.unique(results2.xarray.sum(("x", "y", "z")))) == 4
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.numpy_array, image_from_pixels.numpy_array)
def test_apply_clipping_methods(): """test that apply properly clips the imagestack""" # create a half-valued float array data = np.full((2, 2, 2, 5, 5), fill_value=0.5, dtype=np.float32) # set one value to max data[1, 1, 1, 1, 1] = 1 imagestack = ImageStack.from_numpy_array(data) # max value after multiplication == 2, all other values == 1 def apply_function(x): return x * 2 # clip_method 0 # all data are clipped to 1, setting all values to 1 (np.unique(pre_scaled) == [1, 2]) res = imagestack.apply(apply_function, clip_method=Clip.CLIP, in_place=False, n_processes=1) assert np.allclose(res.xarray.values, 1) # clip_method 1 # all data are scaled, resulting in values being multiplied by 0.5, replicating the original # data res = imagestack.apply( apply_function, clip_method=Clip.SCALE_BY_IMAGE, in_place=False, n_processes=1 ) assert np.allclose(imagestack.xarray, res.xarray) # clip_method 2 res = imagestack.apply( apply_function, clip_method=Clip.SCALE_BY_CHUNK, in_place=False, n_processes=1, group_by={Axes.CH, Axes.ROUND}, ) # any (round, ch) combination that was all 0.5 should now be all 1. assert np.allclose(res.sel({Axes.ROUND: 0, Axes.CH: 0}).xarray, 1) assert np.allclose(res.sel({Axes.ROUND: 1, Axes.CH: 0}).xarray, 1) assert np.allclose(res.sel({Axes.ROUND: 0, Axes.CH: 1}).xarray, 1) # the specific (round, ch) combination with the single "1" value should be scaled, and due to # construction, look like the original data. assert np.allclose( res.sel({Axes.ROUND: 1, Axes.CH: 1}).xarray, imagestack.sel({Axes.ROUND: 1, Axes.CH: 1}).xarray )
def synthetic_two_spot_3d_2round_2ch() -> ImageStack: """produce a 2-round 2-channel ImageStack Notes ----- - After Gaussian filtering, all max intensities are 7 - Two spots are located at (4, 10, 90) and (6, 90, 10) - Both spots are 1-hot, and decode to: - spot 1: (round 0, ch 0), (round 1, ch 1) - spot 2: (round 0, ch 1), (round 1, ch 0) Returns ------- ImageStack : noiseless ImageStack containing two spots """ # blank data_image data = np.zeros((2, 2, 10, 100, 100), dtype=np.float32) # round 0 channel 0 data[0, 0, 4, 10, 90] = 1 data[0, 0, 5, 90, 10] = 0 # round 0 channel 1 data[0, 1, 4, 10, 90] = 0 data[0, 1, 5, 90, 10] = 1 # round 1 channel 0 data[1, 0, 4, 10, 90] = 0 data[1, 0, 5, 90, 10] = 1 # round 1 channel 1 data[1, 1, 4, 10, 90] = 1 data[1, 1, 5, 90, 10] = 0 data = gaussian_filter(data, sigma=(0, 0, 2, 2, 2)) return ImageStack.from_numpy_array(data)
def test_from_numpy_array_automatically_handles_float_conversions(): x = np.zeros((1, 1, 1, 20, 20), dtype=np.uint16) stack = ImageStack.from_numpy_array(x) assert stack.numpy_array.dtype == np.float32
from starfish.spots._detector.local_max_peak_finder import LocalMaxPeakFinder from starfish.spots._detector.trackpy_local_max_peak_finder import TrackpyLocalMaxPeakFinder from starfish.test.factories import ( two_spot_informative_blank_coded_data_factory, two_spot_one_hot_coded_data_factory, two_spot_sparse_coded_data_factory, ) from starfish.types import Axes, Features # verify all spot finders handle different coding types _, ONE_HOT_IMAGESTACK, ONE_HOT_MAX_INTENSITY = two_spot_one_hot_coded_data_factory() _, SPARSE_IMAGESTACK, SPARSE_MAX_INTENSITY = two_spot_sparse_coded_data_factory() _, BLANK_IMAGESTACK, BLANK_MAX_INTENSITY = two_spot_informative_blank_coded_data_factory() # make sure that all spot finders handle empty arrays EMPTY_IMAGESTACK = ImageStack.from_numpy_array(np.zeros((4, 2, 10, 100, 100), dtype=np.float32)) def simple_gaussian_spot_detector() -> BlobDetector: """create a basic gaussian spot detector""" return BlobDetector(min_sigma=1, max_sigma=4, num_sigma=5, threshold=0, measurement_type='max') def simple_trackpy_local_max_spot_detector() -> TrackpyLocalMaxPeakFinder: """create a basic local max peak finder""" return TrackpyLocalMaxPeakFinder( spot_diameter=3, min_mass=0.01, max_size=10, separation=2, )
def random_data_image_stack_factory(): data = np.random.uniform(0, 1, 100).reshape(1, 1, 1, 10, 10).astype(np.float32) return ImageStack.from_numpy_array(data)
def synthetic_two_spot_imagestack_3d(synthetic_two_spot_3d): data = synthetic_two_spot_3d return ImageStack.from_numpy_array(data.reshape(1, 1, *data.shape))
def synthetic_single_spot_imagestack_2d(synthetic_single_spot_2d): data = synthetic_single_spot_2d return ImageStack.from_numpy_array(data.reshape(1, 1, 1, *data.shape))