Пример #1
0
def test_synthetic_spot_creation_raises_error_with_coords_too_small(
        synthetic_intensity_table):
    num_z = 0
    height = 40
    width = 50
    with pytest.raises(ValueError):
        ImageStack.synthetic_spots(synthetic_intensity_table, num_z, height,
                                   width)
Пример #2
0
def test_multiple_tiles_of_different_kind():
    with pytest.raises(TypeError):
        ImageStack.synthetic_stack(
            NUM_HYB,
            NUM_CH,
            NUM_Z,
            HEIGHT,
            WIDTH,
            tile_data_provider=create_tile_data_provider(
                np.uint32, np.float32),
        )
Пример #3
0
def labeled_synthetic_dataset():
    stp = synthesize.SyntheticSpotTileProvider()
    image = ImageStack.synthetic_stack(tile_data_provider=stp.tile)
    max_proj = image.max_proj(Indices.HYB, Indices.CH, Indices.Z)
    view = max_proj.reshape((1, 1, 1) + max_proj.shape)
    dots = ImageStack.from_numpy_array(view)

    def labeled_synthetic_dataset_factory():
        return deepcopy(image), deepcopy(dots), deepcopy(stp.codebook)

    return labeled_synthetic_dataset_factory
Пример #4
0
def test_from_numpy_array_raises_error_when_incorrect_dims_passed():
    array = np.ones((2, 2))
    # 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 filter(self,
               stack: ImageStack,
               in_place: bool = True) -> Optional[ImageStack]:
        """Perform filtering of an image stack

        Parameters
        ----------
        stack : ImageStack
            Stack to be filtered.
        in_place : bool
            if True, process ImageStack in-place, otherwise return a new stack

        Returns
        -------
        Optional[ImageStack] :
            if in-place is False, return the results of filter as a new stack

        """
        func: Callable = partial(self.richardson_lucy_deconv,
                                 num_iter=self.num_iter,
                                 psf=self.psf,
                                 clip=self.clip)
        result = stack.apply(func, in_place=in_place, verbose=self.verbose)
        if not in_place:
            return result
        return None
Пример #6
0
def test_iss_pipeline():
    np.random.seed(2)
    synthesizer = SyntheticData(n_spots=5)
    codebook = synthesizer.codebook()
    true_intensities = synthesizer.intensities(codebook=codebook)
    image = synthesizer.spots(intensities=true_intensities)

    dots_data = image.max_proj(Indices.HYB, Indices.CH, Indices.Z)
    dots = ImageStack.from_numpy_array(
        dots_data.reshape((1, 1, 1, *dots_data.shape)))

    wth = WhiteTophat(disk_size=15)
    wth.filter(image)
    wth.filter(dots)

    fsr = FourierShiftRegistration(upsampling=1000, reference_stack=dots)
    fsr.register(image)

    min_sigma = 1.5
    max_sigma = 5
    num_sigma = 10
    threshold = 1e-4
    gsd = GaussianSpotDetector(
        min_sigma=min_sigma,
        max_sigma=max_sigma,
        num_sigma=num_sigma,
        threshold=threshold,
        blobs_stack=dots,
        measurement_type='max',
    )

    intensities = gsd.find(hybridization_image=image)
    assert intensities.shape[0] == 5

    codebook.decode_euclidean(intensities)
Пример #7
0
    def __init__(self,
                 min_sigma,
                 max_sigma,
                 num_sigma,
                 threshold,
                 blobs_stack,
                 overlap=0.5,
                 measurement_type='max',
                 is_volume: bool = True,
                 **kwargs) -> None:
        """Multi-dimensional gaussian spot detector

        Parameters
        ----------
        min_sigma : float
            The minimum standard deviation for Gaussian Kernel. Keep this low to
            detect smaller blobs.
        max_sigma : float
            The maximum standard deviation for Gaussian Kernel. Keep this high to
            detect larger blobs.
        num_sigma : int
            The number of intermediate values of standard deviations to consider
            between `min_sigma` and `max_sigma`.
        threshold : float
            The absolute lower bound for scale space maxima. Local maxima smaller
            than thresh are ignored. Reduce this to detect blobs with less
            intensities.
        overlap : float [0, 1]
            If two spots have more than this fraction of overlap, the spots are combined (default = 0.5)
        blobs_stack : Union[ImageStack, str]
            ImageStack or the path or URL that references the ImageStack that contains the blobs.
        measurement_type : str ['max', 'mean']
            name of the function used to calculate the intensity for each identified spot area

        Notes
        -----
        This spot detector is very sensitive to the threshold that is selected, and the threshold is defined as an
        absolute value -- therefore it must be adjusted depending on the datatype of the passed image.


        """
        self.min_sigma = min_sigma
        self.max_sigma = max_sigma
        self.num_sigma = num_sigma
        self.threshold = threshold
        self.overlap = overlap
        self.is_volume = is_volume
        if isinstance(blobs_stack, ImageStack):
            self.blobs_stack = blobs_stack
        else:
            self.blobs_stack = ImageStack.from_path_or_url(blobs_stack)
        self.blobs_image: np.ndarray = self.blobs_stack.max_proj(
            Indices.HYB, Indices.CH)

        try:
            self.measurement_function = getattr(np, measurement_type)
        except AttributeError:
            raise ValueError(
                f'measurement_type must be a numpy reduce function such as "max" or "mean". {measurement_type} '
                f'not found.')
Пример #8
0
    def _cli(cls, args, print_help=False):
        """Runs the spot finder component based on parsed arguments."""

        if args.spot_finder_algorithm_class is None or print_help:
            cls.spot_finder_group.print_help()
            cls.spot_finder_group.exit(status=2)

        print('Detecting Spots ...')
        hybridization_stack = ImageStack.from_path_or_url(args.input)
        instance = args.spot_finder_algorithm_class(**vars(args))
        spot_attributes, encoded_spots = instance.find(hybridization_stack)

        if args.show:
            encoded_spots.show(figsize=(10, 10))

        path = os.path.join(args.output, 'spots.geojson')
        print(f"Writing | spots geojson to: {path}")
        spot_attributes.save_geojson(path)

        path = os.path.join(args.output, 'spots.json')
        print(f"Writing | spot_id | x | y | z | to: {path}")
        spot_attributes.save(path)

        path = os.path.join(args.output, 'encoder_table.json')
        print(f"Writing | spot_id | hyb | ch | val | to: {path}")
        encoded_spots.save(path)
Пример #9
0
def test_max_projection_preserves_dtype():
    original_dtype = np.uint16
    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.HYB, Indices.Z)
    assert max_projection.dtype == original_dtype
Пример #10
0
    def filter(self,
               stack: ImageStack,
               in_place: bool = True) -> Optional[ImageStack]:
        """Perform filtering of an image stack

        Parameters
        ----------
        stack : ImageStack
            Stack to be filtered.
        in_place : bool
            if True, process ImageStack in-place, otherwise return a new stack

        Returns
        -------
        Optional[ImageStack] :
            if in-place is False, return the results of filter as a new stack

        """
        clip = partial(self.clip, p_min=self.p_min, p_max=self.p_max)
        result = stack.apply(clip,
                             is_volume=self.is_volume,
                             verbose=self.verbose,
                             in_place=in_place)
        if not in_place:
            return result
        return None
Пример #11
0
    def filter(self,
               stack: ImageStack,
               in_place: bool = True) -> Optional[ImageStack]:
        """Perform filtering of an image stack

        Parameters
        ----------
        stack : ImageStack
            Stack to be filtered.
        in_place : bool
            if True, process ImageStack in-place, otherwise return a new stack

        Returns
        -------
        Optional[ImageStack] :
            if in-place is False, return the results of filter as a new stack

        """
        high_pass: Callable = partial(self.high_pass, sigma=self.sigma)
        result = stack.apply(high_pass,
                             is_volume=self.is_volume,
                             verbose=self.verbose,
                             in_place=in_place)
        if not in_place:
            return result
        return None
Пример #12
0
 def __init__(self, upsampling: int,
              reference_stack: Union[str, ImageStack], **kwargs) -> None:
     self.upsampling = upsampling
     if isinstance(reference_stack, ImageStack):
         self.reference_stack = reference_stack
     else:
         self.reference_stack = ImageStack.from_path_or_url(reference_stack)
Пример #13
0
    def _cli(cls, args, print_help=False):
        """Runs the segmentation component based on parsed arguments."""
        if args.segmentation_algorithm_class is None or print_help:
            cls.segmentation_group.print_help()
            cls.segmentation_group.exit(status=2)

        instance = args.segmentation_algorithm_class(**vars(args))

        print('Segmenting ...')
        hybridization_stack = ImageStack.from_path_or_url(args.hybridization_stack)
        nuclei_stack = ImageStack.from_path_or_url(args.nuclei_stack)
        regions = instance.segment(hybridization_stack, nuclei_stack)
        geojson = regions_to_geojson(regions, use_hull=False)

        print("Writing | regions geojson to: {}".format(args.output))
        with open(args.output, "w") as f:
            f.write(json.dumps(geojson))
Пример #14
0
def test_synthetic_spot_creation_produces_an_imagestack(
        synthetic_intensity_table):
    num_z = 12
    height = 50
    width = 40
    image = ImageStack.synthetic_spots(synthetic_intensity_table, num_z,
                                       height, width)
    assert isinstance(image, ImageStack)
Пример #15
0
 def spots(self, intensities=None) -> ImageStack:
     if intensities is None:
         intensities = self.intensities()
     return ImageStack.synthetic_spots(
         intensities, self.n_z, self.height, self.width,
         self.n_photons_background, self.point_spread_function,
         self.camera_detection_efficiency, self.background_electrons,
         self.gray_level, self.ad_coversion_bits)
Пример #16
0
    def _cli(cls, args, print_help=False):
        """Runs the registration component based on parsed arguments."""
        if args.registration_algorithm_class is None or print_help:
            cls.register_group.print_help()
            cls.register_group.exit(status=2)

        print('Registering ...')
        stack = ImageStack.from_path_or_url(args.input)
        instance = args.registration_algorithm_class(**vars(args))
        instance.register(stack)

        stack.write(args.output)
Пример #17
0
    def _cli(cls, args, print_help=False):
        """Runs the spot finder component based on parsed arguments."""

        if args.spot_finder_algorithm_class is None or print_help:
            cls.spot_finder_group.print_help()
            cls.spot_finder_group.exit(status=2)

        print('Detecting Spots ...')
        hybridization_stack = ImageStack.from_path_or_url(args.input)
        instance = args.spot_finder_algorithm_class(**vars(args))
        intensities = instance.find(hybridization_stack)
        intensities.save(os.path.join(args.output, 'spots.nc'))
Пример #18
0
def test_multiple_tiles_of_same_dtype():
    stack = ImageStack.synthetic_stack(
        NUM_HYB,
        NUM_CH,
        NUM_Z,
        HEIGHT,
        WIDTH,
        tile_data_provider=create_tile_data_provider(np.uint32, np.uint32),
    )
    expected = np.ones((NUM_HYB, NUM_CH, NUM_Z, HEIGHT, WIDTH),
                       dtype=np.uint32)
    assert np.array_equal(stack.numpy_array, expected)
Пример #19
0
def synthetic_spot_pass_through_stack(synthetic_dataset_with_truth_values):
    codebook, true_intensities, _ = synthetic_dataset_with_truth_values
    true_intensities = true_intensities[:2]
    # transfer the intensities to the stack but don't do anything to them.
    img_stack = ImageStack.synthetic_spots(true_intensities,
                                           num_z=12,
                                           height=50,
                                           width=45,
                                           n_photons_background=0,
                                           point_spread_function=(0, 0, 0),
                                           camera_detection_efficiency=1.0,
                                           background_electrons=0,
                                           graylevel=1)
    return codebook, true_intensities, img_stack
Пример #20
0
    def register(self, image: ImageStack):
        # TODO: (ambrosejcarr) is this the appropriate way of dealing with Z in registration?
        mp = image.max_proj(Indices.CH, Indices.Z)
        reference_image = self.reference_stack.max_proj(
            Indices.HYB, Indices.CH, Indices.Z)

        for h in range(image.num_hybs):
            # compute shift between maximum projection (across channels) and dots, for each hyb round
            # TODO: make the max projection array ignorant of axes ordering.
            shift, error = compute_shift(mp[h, :, :], reference_image,
                                         self.upsampling)
            print("For hyb: {}, Shift: {}, Error: {}".format(h, shift, error))

            for c in range(image.num_chs):
                for z in range(image.num_zlayers):
                    # apply shift to all zlayers, channels, and hyb rounds
                    indices = {Indices.HYB: h, Indices.CH: c, Indices.Z: z}
                    data, axes = image.get_slice(indices=indices)
                    assert len(axes) == 0
                    result = shift_im(data, shift)
                    image.set_slice(indices=indices, data=result)

        return image
Пример #21
0
def test_set_slice_range():
    """
    Sets a slice across a range of one of the dimensions.
    """
    stack = ImageStack.synthetic_stack()
    zrange = slice(1, 3)
    y, x = stack.tile_shape

    expected = np.ones((stack.shape[Indices.HYB], stack.shape[Indices.CH],
                        zrange.stop - zrange.start, y, x)) * 10
    index = {Indices.Z: zrange}

    stack.set_slice(index, expected)

    assert np.array_equal(stack.get_slice(index)[0], expected)
Пример #22
0
def synthetic_stack(
        num_hyb: int=DEFAULT_NUM_HYB,
        num_ch: int=DEFAULT_NUM_CH,
        num_z: int=DEFAULT_NUM_Z,
        tile_height: int=DEFAULT_HEIGHT,
        tile_width: int=DEFAULT_WIDTH,
        tile_data_provider: Callable[[int, int, int, int, int], np.ndarray]=default_tile_data_provider,
        tile_extras_provider: Callable[[int, int, int], Any]=default_tile_extras_provider
) -> ImageStack:
    """generate a synthetic ImageStack

    Returns
    -------
    ImageStack :
        imagestack containing a tensor of (2, 3, 4, 30, 20) whose values are all 1.

    """
    img = TileSet(
        {Coordinates.X, Coordinates.Y, Indices.HYB, Indices.CH, Indices.Z},
        {
            Indices.HYB: num_hyb,
            Indices.CH: num_ch,
            Indices.Z: num_z,
        },
        default_tile_shape=(tile_height, tile_width),
    )
    for hyb in range(num_hyb):
        for ch in range(num_ch):
            for z in range(num_z):
                tile = Tile(
                    {
                        Coordinates.X: (0.0, 0.001),
                        Coordinates.Y: (0.0, 0.001),
                        Coordinates.Z: (0.0, 0.001),
                    },
                    {
                        Indices.HYB: hyb,
                        Indices.CH: ch,
                        Indices.Z: z,
                    },
                    extras=tile_extras_provider(hyb, ch, z),
                )
                tile.numpy_array = tile_data_provider(hyb, ch, z, tile_height, tile_width)

                img.add_tile(tile)

    stack = ImageStack(img)
    return stack
Пример #23
0
def test_set_slice_simple_index():
    """
    Sets a slice across one of the indices at the end.  For instance, if the dimensions are
    (P, Q0,..., Qn-1, R), sets a slice across either P or R.
    """
    stack = ImageStack.synthetic_stack()
    hyb = 1
    y, x = stack.tile_shape

    expected = np.ones(
        (stack.shape[Indices.CH], stack.shape[Indices.Z], y, x)) * 2
    index = {Indices.HYB: hyb}

    stack.set_slice(index, expected)

    assert np.array_equal(stack.get_slice(index)[0], expected)
Пример #24
0
def test_set_slice_middle_index():
    """
    Sets a slice across one of the indices in the middle.  For instance, if the dimensions are
    (P, Q0,..., Qn-1, R), slice across one of the Q axes.
    """
    stack = ImageStack.synthetic_stack()
    ch = 1
    y, x = stack.tile_shape

    expected = np.ones(
        (stack.shape[Indices.HYB], stack.shape[Indices.Z], y, x)) * 2
    index = {Indices.CH: ch}

    stack.set_slice(index, expected)

    assert np.array_equal(stack.get_slice(index)[0], expected)
Пример #25
0
def test_float_type_promotion():
    with warnings.catch_warnings(record=True) as w:
        stack = ImageStack.synthetic_stack(
            NUM_HYB,
            NUM_CH,
            NUM_Z,
            HEIGHT,
            WIDTH,
            tile_data_provider=create_tile_data_provider(
                np.float64, np.float32),
        )
        assert len(w) == 1
        assert issubclass(w[0].category, DataFormatWarning)
    expected = np.ones((NUM_HYB, NUM_CH, NUM_Z, HEIGHT, WIDTH),
                       dtype=np.float64)
    assert np.array_equal(stack.numpy_array, expected)
Пример #26
0
def test_conflict():
    """
    Tiles that have extras that conflict with indices should produce an error.
    """
    def tile_extras_provider(hyb: int, ch: int, z: int) -> Any:
        return {
            Indices.HYB: hyb,
            Indices.CH: ch,
            Indices.Z: z,
        }

    stack = ImageStack.synthetic_stack(
        num_hyb=NUM_HYB, num_ch=NUM_CH, num_z=NUM_Z, tile_extras_provider=tile_extras_provider,
    )
    with pytest.raises(ValueError):
        stack.tile_metadata
Пример #27
0
def test_int_type_promotion():
    with warnings.catch_warnings(record=True) as w:
        stack = ImageStack.synthetic_stack(
            NUM_HYB,
            NUM_CH,
            NUM_Z,
            HEIGHT,
            WIDTH,
            tile_data_provider=create_tile_data_provider(np.int32, np.int8),
        )
        assert len(w) == 1
        assert issubclass(w[0].category, DataFormatWarning)
    expected = np.ones((NUM_HYB, NUM_CH, NUM_Z, HEIGHT, WIDTH), dtype=np.int32)
    corner = np.empty((HEIGHT, WIDTH), dtype=np.int32)
    corner.fill(16777216)
    expected[0, 0, 0] = corner
    assert np.array_equal(stack.numpy_array, expected)
Пример #28
0
def test_metadata():
    """
    Normal situation where all the tiles have uniform keys for both indices and extras.
    """
    def tile_extras_provider(hyb: int, ch: int, z: int) -> Any:
        return {
            'random_key': {
                Indices.HYB: hyb,
                Indices.CH: ch,
                Indices.Z: z,
            }
        }

    stack = ImageStack.synthetic_stack(
        num_hyb=NUM_HYB, num_ch=NUM_CH, num_z=NUM_Z, tile_extras_provider=tile_extras_provider,
    )
    table = stack.tile_metadata
    assert len(table) == NUM_HYB * NUM_CH * NUM_Z
Пример #29
0
    def _measure_spot_intensities(
            self, stack: ImageStack,
            spot_attributes: pd.DataFrame) -> IntensityTable:

        n_ch = stack.shape[Indices.CH]
        n_hyb = stack.shape[Indices.HYB]
        spot_attribute_index = dataframe_to_multiindex(spot_attributes)
        intensity_table = IntensityTable.empty_intensity_table(
            spot_attribute_index, n_ch, n_hyb)

        indices = product(range(n_ch), range(n_hyb))
        for c, h in indices:
            image, _ = stack.get_slice({Indices.CH: c, Indices.HYB: h})
            blob_intensities: pd.Series = self._measure_blob_intensity(
                image, spot_attributes, self.measurement_function)
            intensity_table[:, c, h] = blob_intensities

        return intensity_table
Пример #30
0
def test_get_slice_range():
    """
    Retrieve a slice across a range of one of the dimensions.
    """
    stack = ImageStack.synthetic_stack()
    zrange = slice(1, 3)
    imageslice, axes = stack.get_slice({Indices.Z: zrange})
    y, x = stack.tile_shape
    assert axes == [Indices.HYB, Indices.CH, Indices.Z]

    for hyb in range(stack.shape[Indices.HYB]):
        for ch in range(stack.shape[Indices.CH]):
            for z in range(zrange.stop - zrange.start):
                data = np.empty((y, x))
                data.fill((hyb * stack.shape[Indices.CH] + ch) *
                          stack.shape[Indices.Z] + (z + zrange.start))

                assert data.all() == imageslice[hyb, ch, z].all()