Skip to content

image_viewer

Short Description

sm.pl.image_viewer: This function enables users to view OME-TIFF images within the Napari viewer, offering the capability to overlay points based on any categorical column, such as cluster annotations or phenotypes. It provides a dynamic and interactive way to visually explore spatial distributions and relationships of cells directly on the source images, enriching the analysis with spatial context and insights.

Function

image_viewer(image_path, adata, overlay=None, flip_y=True, overlay_category=None, markers=None, channel_names='default', x_coordinate='X_centroid', y_coordinate='Y_centroid', point_size=10, point_color=None, subset=None, imageid='imageid', seg_mask=None, **kwargs)

Parameters:

Name Type Description Default
image_path str

Path to the image file. Supports TIFF, OME.TIFF, and ZARR formats.

required
adata AnnData

The annotated data matrix.

required
overlay str

Column in adata.obs containing categorical data for visualization, such as phenotypes or clusters.

None
flip_y bool

If True, inverts the Y-axis to match image coordinates.

True
overlay_category list

Specific categories within overlay to display. If None, all categories are shown.

None
markers list

List of markers to include in the visualization. If None, all available markers are displayed.

None
channel_names list or str

Specifies the order of channels in the image. Default uses all markers in adata.uns['all_markers'].

'default'
x_coordinate, y_coordinate (str

Columns in adata.obs specifying cell coordinates. Default to 'X_centroid' and 'Y_centroid'.

required
point_size int

Size of the points in the visualization.

10
point_color str or dict

Color(s) for the points. Can specify a single color or a dictionary mapping categories to colors.

None
imageid str

Column in adata.obs identifying different images in the dataset. Useful for datasets with multiple images.

'imageid'
subset str

Identifier for a specific image to analyze, used in conjunction with imageid.

None
seg_mask str

Path to a segmentation mask file to overlay.

None
**kwargs

Additional arguments passed to the napari viewer.

{}

Returns:

Name Type Description
Image napari

Displays the visualization using napari viewer.

Example
1
2
3
4
5
6
# Basic visualization with phenotype overlay
sm.pl.image_viewer(image_path='/path/to/image.ome.tif', adata=adata, overlay='phenotype', point_size=5)

# Visualization with segmentation mask and custom point colors
sm.pl.image_viewer(image_path='/path/to/image.ome.tif', adata=adata, seg_mask='/path/to/mask.tif',
             overlay='phenotype', point_color={'T cell': 'green', 'B cell': 'blue'}, point_size=7)
Source code in scimap/plotting/image_viewer.py
 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
def image_viewer(
    image_path,
    adata,
    overlay=None,
    flip_y=True,
    overlay_category=None,
    markers=None,
    channel_names='default',
    x_coordinate='X_centroid',
    y_coordinate='Y_centroid',
    point_size=10,
    point_color=None,
    subset=None,
    imageid='imageid',
    seg_mask=None,
    **kwargs,
):
    """
    Parameters:
            image_path (str):
                Path to the image file. Supports TIFF, OME.TIFF, and ZARR formats.

            adata (anndata.AnnData):
                The annotated data matrix.

            overlay (str, optional):
                Column in `adata.obs` containing categorical data for visualization, such as phenotypes or clusters.

            flip_y (bool, optional):
                If True, inverts the Y-axis to match image coordinates.

            overlay_category (list, optional):
                Specific categories within `overlay` to display. If None, all categories are shown.

            markers (list, optional):
                List of markers to include in the visualization. If None, all available markers are displayed.

            channel_names (list or str, optional):
                Specifies the order of channels in the image. Default uses all markers in `adata.uns['all_markers']`.

            x_coordinate, y_coordinate (str, optional):
                Columns in `adata.obs` specifying cell coordinates. Default to 'X_centroid' and 'Y_centroid'.

            point_size (int, optional):
                Size of the points in the visualization.

            point_color (str or dict, optional):
                Color(s) for the points. Can specify a single color or a dictionary mapping categories to colors.

            imageid (str, optional):
                Column in `adata.obs` identifying different images in the dataset. Useful for datasets with multiple images.

            subset (str, optional):
                Identifier for a specific image to analyze, used in conjunction with `imageid`.

            seg_mask (str, optional):
                Path to a segmentation mask file to overlay.

            **kwargs:
                Additional arguments passed to the napari viewer.

    Returns:
            Image (napari):
                Displays the visualization using napari viewer.

    Example:
        ```python

        # Basic visualization with phenotype overlay
        sm.pl.image_viewer(image_path='/path/to/image.ome.tif', adata=adata, overlay='phenotype', point_size=5)

        # Visualization with segmentation mask and custom point colors
        sm.pl.image_viewer(image_path='/path/to/image.ome.tif', adata=adata, seg_mask='/path/to/mask.tif',
                     overlay='phenotype', point_color={'T cell': 'green', 'B cell': 'blue'}, point_size=7)

        ```
    """

    # TODO
    # - ADD Subset markers for ZARR ssection
    # - Ability to use ZARR metadata if available

    # adding option to load just the image without an adata object
    if adata is None:
        channel_names = None
    else:
        # All operations on the AnnData object is performed first
        # Plot only the Image that is requested
        if subset is not None:
            adata = adata[adata.obs[imageid] == subset]

        # Recover the channel names from adata
        if channel_names == 'default':
            channel_names = adata.uns['all_markers']
        else:
            channel_names = channel_names

        # Index of the marker of interest and corresponding names
        if markers is None:
            idx = list(range(len(channel_names)))
            channel_names = channel_names
        else:
            idx = []
            for i in markers:
                idx.append(list(channel_names).index(i))
            channel_names = markers

        # Load the segmentation mask
        if seg_mask is not None:
            seg_m = tiff.imread(seg_mask)
            if (len(seg_m.shape) > 2) and (seg_m.shape[0] > 1):
                seg_m = seg_m[0]

    # Operations on the OME TIFF image is performed next
    # check the format of image
    if os.path.isfile(image_path) is True:
        image = tiff.TiffFile(image_path, is_ome=False)  # is_ome=False
        z = zarr.open(image.aszarr(), mode='r')  # convert image to Zarr array
        # Identify the number of pyramids
        n_levels = len(image.series[0].levels)  # pyramid

        # If and if not pyramids are available
        if n_levels > 1:
            pyramid = [da.from_zarr(z[i]) for i in range(n_levels)]
            multiscale = True
        else:
            pyramid = da.from_zarr(z)
            multiscale = False

        # subset channels of interest
        if markers is not None:
            if n_levels > 1:
                for i in range(n_levels - 1):
                    pyramid[i] = pyramid[i][idx, :, :]
                n_channels = pyramid[0].shape[0]  # identify the number of channels
            else:
                pyramid = pyramid[idx, :, :]
                n_channels = pyramid.shape[0]  # identify the number of channels
        else:
            if n_levels > 1:
                n_channels = pyramid[0].shape[0]
            else:
                n_channels = pyramid.shape[0]

        # check if channel names have been passed to all channels
        if channel_names is not None:
            assert n_channels == len(channel_names), (
                f'number of channel names ({len(channel_names)}) must '
                f'match number of channels ({n_channels})'
            )

        # Load the viewer
        viewer = napari.view_image(
            pyramid,
            multiscale=multiscale,
            channel_axis=0,
            visible=False,
            name=None if channel_names is None else channel_names,
            **kwargs,
        )

    # Operations on the ZARR image
    # check the format of image
    if os.path.isfile(image_path) is False:
        # print(image_path)
        viewer = napari.Viewer()
        viewer.open(
            image_path,
            multiscale=True,
            visible=False,
            name=None if channel_names is None else channel_names,
        )

    # Add the seg mask
    if seg_mask is not None:
        viewer.add_labels(seg_m, name='segmentation mask', visible=False)

    # Add phenotype layer function
    def add_phenotype_layer(
        adata,
        overlay,
        phenotype_layer,
        x,
        y,
        viewer,
        point_size,
        point_color,
        available_phenotypes,
    ):
        coordinates = adata[adata.obs[overlay] == phenotype_layer]
        # Flip Y AXIS if needed
        if flip_y is True:
            coordinates = pd.DataFrame(
                {'y': coordinates.obs[y], 'x': coordinates.obs[x]}
            )
        else:
            coordinates = pd.DataFrame(
                {'x': coordinates.obs[x], 'y': coordinates.obs[y]}
            )

        # points = coordinates.values.tolist()
        points = coordinates.values
        if point_color is None:
            r = lambda: random.randint(0, 255)  # random color generator
            point_color = '#%02X%02X%02X' % (r(), r(), r())  # random color generator
        elif isinstance(point_color, dict):
            # if dict identify the color for the given phenotype
            # also if a color is not provided in the dict assign it to white
            try:
                point_color = point_color[available_phenotypes]
            except KeyError:
                point_color = 'white'
                # if the dict has list, we need to account for it and so the following two lines
                if isinstance(point_color, list):
                    point_color = point_color[0]

        # check if point_color is a dict and if so isolate the color to the specific categoty
        viewer.add_points(
            points,
            size=point_size,
            face_color=point_color,
            visible=False,
            name=phenotype_layer,
        )

    if overlay is not None:
        # categories under investigation
        if overlay_category is None:
            available_phenotypes = list(adata.obs[overlay].unique())
        else:
            available_phenotypes = overlay_category

        # Run the function on all phenotypes
        for i in available_phenotypes:
            add_phenotype_layer(
                adata=adata,
                overlay=overlay,
                phenotype_layer=i,
                x=x_coordinate,
                y=y_coordinate,
                viewer=viewer,
                point_size=point_size,
                point_color=point_color,
                available_phenotypes=i,
            )