Skip to content

ratiopath.openslide.OpenSlide

Bases: OpenSlide

A wrapper around the OpenSlide library to provide additional functionality.

Source code in ratiopath/openslide.py
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class OpenSlide(openslide.OpenSlide):
    """A wrapper around the OpenSlide library to provide additional functionality."""

    def closest_level(self, mpp: float | tuple[float, float]) -> int:
        """Finds the closest slide level to match the desired MPP.

        This method compares the desired MPP (µm/px) with the MPP of the
        available levels in the slide and selects the level with the closest match.

        Args:
            mpp: The desired µm/px value.

        Returns:
            The index of the level with the closest µm/px resolution to the desired value.
        """
        scale_factor = np.mean(
            np.asarray(mpp)
            / np.array(
                [
                    float(self.properties[PROPERTY_NAME_MPP_X]),
                    float(self.properties[PROPERTY_NAME_MPP_Y]),
                ]
            )
        )

        return np.abs(np.asarray(self.level_downsamples) - scale_factor).argmin().item()

    def slide_resolution(self, level: int) -> tuple[float, float]:
        """Returns the resolution of the slide in µm/px at the given level.

        Args:
            level: The level of the slide to calculate the resolution.

        Returns:
            The [x, y] resolution of the slide in µm/px.
        """
        slide_mpp_x = float(self.properties[PROPERTY_NAME_MPP_X])
        slide_mpp_y = float(self.properties[PROPERTY_NAME_MPP_Y])

        return (
            slide_mpp_x * self.level_downsamples[level],
            slide_mpp_y * self.level_downsamples[level],
        )

    def read_region_relative(
        self, location: tuple[int, int], level: int, size: tuple[int, int]
    ) -> Image.Image:
        """Reads a region from the slide with coordinates relative to the specified level.

        This method adjusts the coordinates based on the level's downsampling factor
        before reading the region from the slide.

        Args:
            location: The (x, y) coordinates at the specified level.
            level: The level of the slide to read from.
            size: The (width, height) of the region to read.

        Returns:
            The image of the requested region.
        """
        downsample = self.level_downsamples[level]
        location = (int(location[0] * downsample), int(location[1] * downsample))

        return super().read_region(location, level, size)

    def read_tile(
        self,
        x: int | pa.Scalar,
        y: int | pa.Scalar,
        extent_x: int | pa.Scalar,
        extent_y: int | pa.Scalar,
        level: int | pa.Scalar,
        background: tuple[int, int, int] | int = 255,
    ) -> np.ndarray:
        """Reads a tile from the slide at the specified coordinates and level.

        This method reads a tile from the slide based on the provided x and y
        coordinates, tile extent, and level. It also composites the tile onto a
        white background to remove any alpha channel.

        Args:
            x: The x-coordinate of the tile.
            y: The y-coordinate of the tile.
            extent_x: The width of the tile in pixels.
            extent_y: The height of the tile in pixels.
            level: The level of the slide to read from.
            background: The RGB value (0-255) to use for transparent areas. Defaults to 255 (white).

        Returns:
            The RGB image of the requested tile.

        """
        # Convert PyArrow scalars to native Python ints if needed
        x_val = x.as_py() if isinstance(x, pa.Scalar) else x
        y_val = y.as_py() if isinstance(y, pa.Scalar) else y
        extent_x_val = extent_x.as_py() if isinstance(extent_x, pa.Scalar) else extent_x
        extent_y_val = extent_y.as_py() if isinstance(extent_y, pa.Scalar) else extent_y
        level_val = level.as_py() if isinstance(level, pa.Scalar) else level

        background_broadcasted = tuple(np.broadcast_to(background, (3,)))

        rgba_region = self.read_region_relative(
            (x_val, y_val), level_val, (extent_x_val, extent_y_val)
        )

        rgb_region = Image.alpha_composite(
            Image.new("RGBA", rgba_region.size, background_broadcasted),
            rgba_region,
        ).convert("RGB")

        return np.asarray(rgb_region)

closest_level(mpp)

Finds the closest slide level to match the desired MPP.

This method compares the desired MPP (µm/px) with the MPP of the available levels in the slide and selects the level with the closest match.

Parameters:

Name Type Description Default
mpp float | tuple[float, float]

The desired µm/px value.

required

Returns:

Type Description
int

The index of the level with the closest µm/px resolution to the desired value.

Source code in ratiopath/openslide.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def closest_level(self, mpp: float | tuple[float, float]) -> int:
    """Finds the closest slide level to match the desired MPP.

    This method compares the desired MPP (µm/px) with the MPP of the
    available levels in the slide and selects the level with the closest match.

    Args:
        mpp: The desired µm/px value.

    Returns:
        The index of the level with the closest µm/px resolution to the desired value.
    """
    scale_factor = np.mean(
        np.asarray(mpp)
        / np.array(
            [
                float(self.properties[PROPERTY_NAME_MPP_X]),
                float(self.properties[PROPERTY_NAME_MPP_Y]),
            ]
        )
    )

    return np.abs(np.asarray(self.level_downsamples) - scale_factor).argmin().item()

read_region_relative(location, level, size)

Reads a region from the slide with coordinates relative to the specified level.

This method adjusts the coordinates based on the level's downsampling factor before reading the region from the slide.

Parameters:

Name Type Description Default
location tuple[int, int]

The (x, y) coordinates at the specified level.

required
level int

The level of the slide to read from.

required
size tuple[int, int]

The (width, height) of the region to read.

required

Returns:

Type Description
Image

The image of the requested region.

Source code in ratiopath/openslide.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def read_region_relative(
    self, location: tuple[int, int], level: int, size: tuple[int, int]
) -> Image.Image:
    """Reads a region from the slide with coordinates relative to the specified level.

    This method adjusts the coordinates based on the level's downsampling factor
    before reading the region from the slide.

    Args:
        location: The (x, y) coordinates at the specified level.
        level: The level of the slide to read from.
        size: The (width, height) of the region to read.

    Returns:
        The image of the requested region.
    """
    downsample = self.level_downsamples[level]
    location = (int(location[0] * downsample), int(location[1] * downsample))

    return super().read_region(location, level, size)

read_tile(x, y, extent_x, extent_y, level, background=255)

Reads a tile from the slide at the specified coordinates and level.

This method reads a tile from the slide based on the provided x and y coordinates, tile extent, and level. It also composites the tile onto a white background to remove any alpha channel.

Parameters:

Name Type Description Default
x int | Scalar

The x-coordinate of the tile.

required
y int | Scalar

The y-coordinate of the tile.

required
extent_x int | Scalar

The width of the tile in pixels.

required
extent_y int | Scalar

The height of the tile in pixels.

required
level int | Scalar

The level of the slide to read from.

required
background tuple[int, int, int] | int

The RGB value (0-255) to use for transparent areas. Defaults to 255 (white).

255

Returns:

Type Description
ndarray

The RGB image of the requested tile.

Source code in ratiopath/openslide.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def read_tile(
    self,
    x: int | pa.Scalar,
    y: int | pa.Scalar,
    extent_x: int | pa.Scalar,
    extent_y: int | pa.Scalar,
    level: int | pa.Scalar,
    background: tuple[int, int, int] | int = 255,
) -> np.ndarray:
    """Reads a tile from the slide at the specified coordinates and level.

    This method reads a tile from the slide based on the provided x and y
    coordinates, tile extent, and level. It also composites the tile onto a
    white background to remove any alpha channel.

    Args:
        x: The x-coordinate of the tile.
        y: The y-coordinate of the tile.
        extent_x: The width of the tile in pixels.
        extent_y: The height of the tile in pixels.
        level: The level of the slide to read from.
        background: The RGB value (0-255) to use for transparent areas. Defaults to 255 (white).

    Returns:
        The RGB image of the requested tile.

    """
    # Convert PyArrow scalars to native Python ints if needed
    x_val = x.as_py() if isinstance(x, pa.Scalar) else x
    y_val = y.as_py() if isinstance(y, pa.Scalar) else y
    extent_x_val = extent_x.as_py() if isinstance(extent_x, pa.Scalar) else extent_x
    extent_y_val = extent_y.as_py() if isinstance(extent_y, pa.Scalar) else extent_y
    level_val = level.as_py() if isinstance(level, pa.Scalar) else level

    background_broadcasted = tuple(np.broadcast_to(background, (3,)))

    rgba_region = self.read_region_relative(
        (x_val, y_val), level_val, (extent_x_val, extent_y_val)
    )

    rgb_region = Image.alpha_composite(
        Image.new("RGBA", rgba_region.size, background_broadcasted),
        rgba_region,
    ).convert("RGB")

    return np.asarray(rgb_region)

slide_resolution(level)

Returns the resolution of the slide in µm/px at the given level.

Parameters:

Name Type Description Default
level int

The level of the slide to calculate the resolution.

required

Returns:

Type Description
tuple[float, float]

The [x, y] resolution of the slide in µm/px.

Source code in ratiopath/openslide.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def slide_resolution(self, level: int) -> tuple[float, float]:
    """Returns the resolution of the slide in µm/px at the given level.

    Args:
        level: The level of the slide to calculate the resolution.

    Returns:
        The [x, y] resolution of the slide in µm/px.
    """
    slide_mpp_x = float(self.properties[PROPERTY_NAME_MPP_X])
    slide_mpp_y = float(self.properties[PROPERTY_NAME_MPP_Y])

    return (
        slide_mpp_x * self.level_downsamples[level],
        slide_mpp_y * self.level_downsamples[level],
    )