Coverage for tests/images/test_qupath_server.py: 100%
306 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-31 11:24 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-31 11:24 +0000
1import numpy as np
2import tifffile
3import imageio.v3 as iio
4import base64
5import pytest
6from unittest.mock import Mock
7from qubalab.images.qupath_server import QuPathServer
8from qubalab.images.region_2d import Region2D
9from qubalab.images.metadata.image_metadata import ImageMetadata
10from qubalab.images.metadata.image_shape import ImageShape
11from qubalab.images.metadata.pixel_calibration import PixelCalibration, PixelLength
14sample_RGB_metadata = ImageMetadata(
15 "/path/to/img.tiff",
16 "Image name",
17 (
18 ImageShape(64, 50, c=3),
19 ImageShape(32, 25, c=3),
20 ImageShape(16, 12, c=3)
21 ),
22 PixelCalibration(
23 PixelLength.create_microns(2.5),
24 PixelLength.create_microns(2.5)
25 ),
26 True,
27 np.uint8
28)
29sample_RGB_pixels = [[[[
30 x / shape.x * 255 if c == 0 else (y / shape.y * 255 if c == 1 else 0)
31 for x in range(shape.x)]
32 for y in range(shape.y)]
33 for c in range(shape.c)]
34 for shape in sample_RGB_metadata.shapes
35]
38sample_float32_metadata = ImageMetadata(
39 "/path/to/img.tiff",
40 "Image name",
41 (
42 ImageShape(64, 50, t=5, z=2, c=3),
43 ImageShape(32, 25, t=5, z=2, c=3),
44 ImageShape(16, 12, t=5, z=2, c=3)
45 ),
46 PixelCalibration(
47 PixelLength.create_microns(2.5),
48 PixelLength.create_microns(2.5)
49 ),
50 False,
51 np.float32
52)
53sample_float32_pixels = [[[[[[
54 x/shape.x + y/shape.y + z/shape.z + c/shape.c + t/shape.t
55 for x in range(shape.x)]
56 for y in range(shape.y)]
57 for z in range(shape.z)]
58 for c in range(shape.c)]
59 for t in range(shape.t)]
60 for shape in sample_float32_metadata.shapes
61]
64def _create_qupath_metadata(metadata: ImageMetadata):
65 qupath_metadata = Mock()
66 qupath_metadata.getName.return_value = metadata.name
68 qupath_levels = []
69 for shape in metadata.shapes:
70 level = Mock()
71 level.getWidth.return_value = shape.x
72 level.getHeight.return_value = shape.y
73 qupath_levels.append(level)
74 qupath_metadata.getLevels.return_value = qupath_levels
76 qupath_channels = []
77 for channel in metadata.channels:
78 qupath_channel = Mock()
79 qupath_channel.getName.return_value = channel.name
80 qupath_channel.getColor.return_value = ((255 & 0xff)<<24) + \
81 ((int(channel.color[0]) * 255 & 0xff)<<16) + \
82 ((int(channel.color[1] * 255) & 0xff)<<8) + \
83 (int(channel.color[2] * 255) & 0xff)
84 qupath_channels.append(qupath_channel)
85 qupath_metadata.getChannels.return_value = qupath_channels
87 return qupath_metadata
90def _create_qupath_server(metadata: ImageMetadata):
91 qupath_server = Mock()
92 qupath_server.getMetadata.return_value = _create_qupath_metadata(metadata)
93 qupath_server.getURIs.return_value = ("file://" + metadata.path,)
94 qupath_server.nChannels.return_value = metadata.n_channels
95 qupath_server.nZSlices.return_value = metadata.n_z_slices
96 qupath_server.nTimepoints.return_value = metadata.n_timepoints
97 qupath_server.isRGB.return_value = metadata.is_rgb
98 qupath_server.getPreferredDownsamples.return_value = metadata.downsamples
99 qupath_server.getDownsampleForResolution.side_effect = lambda level : metadata.downsamples[level]
100 qupath_server.getPath.return_value = metadata.path
102 pixel_type_text = Mock()
103 pixel_type_text.lower.return_value = metadata.dtype
104 pixel_type = Mock()
105 pixel_type.toString.return_value = pixel_type_text
106 qupath_server.getPixelType.return_value = pixel_type
108 pixel_calibration = Mock()
109 pixel_calibration.hasPixelSizeMicrons.return_value = metadata.pixel_calibration.length_x.unit == metadata.pixel_calibration.length_y.unit == "micrometer"
110 pixel_calibration.hasZSpacingMicrons.return_value = metadata.pixel_calibration.length_z.unit == "micrometer"
111 pixel_calibration.getPixelWidthMicrons.return_value = metadata.pixel_calibration.length_x.length
112 pixel_calibration.getPixelHeightMicrons.return_value = metadata.pixel_calibration.length_y.length
113 pixel_calibration.getZSpacingMicrons.return_value = metadata.pixel_calibration.length_z.length
114 qupath_server.getPixelCalibration.return_value = pixel_calibration
116 return qupath_server
119def _create_gateway(metadata: ImageMetadata, pixels: list):
120 def _create_region_request(path, downsample, x, y, width, height, z, t):
121 return {
122 "downsample": downsample,
123 "x": x,
124 "y": y,
125 "width": width,
126 "height": height,
127 "z": z,
128 "t": t
129 }
131 def _get_tile(request):
132 level = metadata.downsamples.index(request["downsample"])
133 image = np.array(pixels[level], dtype=metadata.dtype)
135 if metadata.n_timepoints > 1:
136 image = image[request["t"], ...]
137 if metadata.n_z_slices > 1:
138 image = image[..., request["z"], :, :]
139 image = image[:, request["y"]:request["y"]+request["height"], request["x"]:request["x"]+request["width"]]
141 return image
143 def _write_image_region(server, request, path):
144 with tifffile.TiffWriter(path, bigtiff=True) as tif:
145 tif.write(_get_tile(request), photometric='rgb')
147 def _get_image_bytes(server, request, format):
148 return iio.imwrite("<bytes>", _get_tile(request), extension=".tiff", photometric='rgb')
150 def _get_image_base64(server, request, format):
151 return base64.b64encode(_get_image_bytes(server, request, format))
153 gateway = Mock()
155 gateway.jvm.qupath.lib.regions.RegionRequest.createInstance.side_effect = _create_region_request
156 gateway.entry_point.writeImageRegion.side_effect = _write_image_region
157 gateway.entry_point.getImageBytes.side_effect = _get_image_bytes
158 gateway.entry_point.getImageBase64.side_effect = _get_image_base64
160 return gateway
163def test_invalid_pixel_access():
164 pixel_access='invalid_pixel_access'
166 with pytest.raises(ValueError):
167 QuPathServer(pixel_access=pixel_access)
170def test_RGB_metadata():
171 metadata = sample_RGB_metadata
172 gateway = _create_gateway(metadata, sample_RGB_pixels)
173 qupath_server = _create_qupath_server(metadata)
174 server = QuPathServer(gateway, qupath_server)
176 metadata = server.metadata
178 assert metadata == metadata
180 server.close()
183def test_RGB_full_resolution_with_temp_file():
184 level = 0
185 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
186 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
187 qupath_server = _create_qupath_server(sample_RGB_metadata)
188 server = QuPathServer(gateway, qupath_server, 'temp_files')
190 image = server.read_region(
191 sample_RGB_metadata.downsamples[level],
192 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
193 )
195 np.testing.assert_array_equal(image, expected_image)
197 server.close()
200def test_RGB_lowest_resolution_with_temp_file():
201 level = sample_RGB_metadata.n_resolutions - 1
202 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
203 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
204 qupath_server = _create_qupath_server(sample_RGB_metadata)
205 server = QuPathServer(gateway, qupath_server, 'temp_files')
207 image = server.read_region(
208 sample_RGB_metadata.downsamples[level],
209 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
210 )
212 np.testing.assert_array_equal(image, expected_image)
214 server.close()
217def test_RGB_tile_of_full_resolution_with_temp_file():
218 level = 0
219 width = 5
220 height = 6
221 x = 10
222 y = 20
223 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)[:, y:y+height, x:x+width]
224 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
225 qupath_server = _create_qupath_server(sample_RGB_metadata)
226 server = QuPathServer(gateway, qupath_server, 'temp_files')
228 image = server.read_region(
229 sample_RGB_metadata.downsamples[level],
230 Region2D(x, y, width, height)
231 )
233 np.testing.assert_array_equal(image, expected_image)
235 server.close()
238def test_RGB_full_resolution_with_bytes():
239 level = 0
240 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
241 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
242 qupath_server = _create_qupath_server(sample_RGB_metadata)
243 server = QuPathServer(gateway, qupath_server, 'bytes')
245 image = server.read_region(
246 sample_RGB_metadata.downsamples[level],
247 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
248 )
250 np.testing.assert_array_equal(image, expected_image)
252 server.close()
255def test_RGB_lowest_resolution_with_bytes():
256 level = sample_RGB_metadata.n_resolutions - 1
257 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
258 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
259 qupath_server = _create_qupath_server(sample_RGB_metadata)
260 server = QuPathServer(gateway, qupath_server, 'bytes')
262 image = server.read_region(
263 sample_RGB_metadata.downsamples[level],
264 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
265 )
267 np.testing.assert_array_equal(image, expected_image)
269 server.close()
272def test_RGB_tile_of_full_resolution_with_bytes():
273 level = 0
274 width = 5
275 height = 6
276 x = 10
277 y = 20
278 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)[:, y:y+height, x:x+width]
279 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
280 qupath_server = _create_qupath_server(sample_RGB_metadata)
281 server = QuPathServer(gateway, qupath_server, 'bytes')
283 image = server.read_region(
284 sample_RGB_metadata.downsamples[level],
285 Region2D(x, y, width, height)
286 )
288 np.testing.assert_array_equal(image, expected_image)
290 server.close()
293def test_RGB_full_resolution_with_base64():
294 level = 0
295 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
296 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
297 qupath_server = _create_qupath_server(sample_RGB_metadata)
298 server = QuPathServer(gateway, qupath_server, 'base_64')
300 image = server.read_region(
301 sample_RGB_metadata.downsamples[level],
302 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
303 )
305 np.testing.assert_array_equal(image, expected_image)
307 server.close()
310def test_RGB_lowest_resolution_with_base64():
311 level = sample_RGB_metadata.n_resolutions - 1
312 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)
313 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
314 qupath_server = _create_qupath_server(sample_RGB_metadata)
315 server = QuPathServer(gateway, qupath_server, 'base_64')
317 image = server.read_region(
318 sample_RGB_metadata.downsamples[level],
319 Region2D(width=sample_RGB_metadata.width, height=sample_RGB_metadata.height)
320 )
322 np.testing.assert_array_equal(image, expected_image)
324 server.close()
327def test_RGB_tile_of_full_resolution_with_base64():
328 level = 0
329 width = 5
330 height = 6
331 x = 10
332 y = 20
333 expected_image = np.array(sample_RGB_pixels[level], dtype=sample_RGB_metadata.dtype)[:, y:y+height, x:x+width]
334 gateway = _create_gateway(sample_RGB_metadata, sample_RGB_pixels)
335 qupath_server = _create_qupath_server(sample_RGB_metadata)
336 server = QuPathServer(gateway, qupath_server, 'base_64')
338 image = server.read_region(
339 sample_RGB_metadata.downsamples[level],
340 Region2D(x, y, width, height)
341 )
343 np.testing.assert_array_equal(image, expected_image)
345 server.close()
348def test_float32_metadata():
349 metadata = sample_float32_metadata
350 gateway = _create_gateway(metadata, sample_float32_pixels)
351 qupath_server = _create_qupath_server(metadata)
352 server = QuPathServer(gateway, qupath_server)
354 metadata = server.metadata
356 assert metadata == metadata
358 server.close()
361def test_float32_full_resolution_with_temp_file():
362 level = 0
363 t = int(sample_float32_metadata.n_timepoints / 2)
364 z = int(sample_float32_metadata.n_z_slices / 2)
365 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
366 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
367 qupath_server = _create_qupath_server(sample_float32_metadata)
368 server = QuPathServer(gateway, qupath_server, 'temp_files')
370 image = server.read_region(
371 sample_float32_metadata.downsamples[level],
372 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
373 )
375 np.testing.assert_array_equal(image, expected_image)
377 server.close()
380def test_float32_lowest_resolution_with_temp_file():
381 level = sample_float32_metadata.n_resolutions - 1
382 t = int(sample_float32_metadata.n_timepoints / 2)
383 z = int(sample_float32_metadata.n_z_slices / 2)
384 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
385 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
386 qupath_server = _create_qupath_server(sample_float32_metadata)
387 server = QuPathServer(gateway, qupath_server, 'temp_files')
389 image = server.read_region(
390 sample_float32_metadata.downsamples[level],
391 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
392 )
394 np.testing.assert_array_equal(image, expected_image)
396 server.close()
399def test_float32_tile_of_full_resolution_with_temp_file():
400 level = 0
401 width = 5
402 height = 6
403 x = 10
404 y = 20
405 t = int(sample_float32_metadata.n_timepoints / 2)
406 z = int(sample_float32_metadata.n_z_slices / 2)
407 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, y:y+height, x:x+width]
408 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
409 qupath_server = _create_qupath_server(sample_float32_metadata)
410 server = QuPathServer(gateway, qupath_server, 'temp_files')
412 image = server.read_region(
413 sample_float32_metadata.downsamples[level],
414 Region2D(x, y, width, height, z, t)
415 )
417 np.testing.assert_array_equal(image, expected_image)
419 server.close()
422def test_float32_full_resolution_with_bytes():
423 level = 0
424 t = int(sample_float32_metadata.n_timepoints / 2)
425 z = int(sample_float32_metadata.n_z_slices / 2)
426 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
427 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
428 qupath_server = _create_qupath_server(sample_float32_metadata)
429 server = QuPathServer(gateway, qupath_server, 'bytes')
431 image = server.read_region(
432 sample_float32_metadata.downsamples[level],
433 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
434 )
436 np.testing.assert_array_equal(image, expected_image)
438 server.close()
441def test_float32_lowest_resolution_with_bytes():
442 level = sample_float32_metadata.n_resolutions - 1
443 t = int(sample_float32_metadata.n_timepoints / 2)
444 z = int(sample_float32_metadata.n_z_slices / 2)
445 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
446 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
447 qupath_server = _create_qupath_server(sample_float32_metadata)
448 server = QuPathServer(gateway, qupath_server, 'bytes')
450 image = server.read_region(
451 sample_float32_metadata.downsamples[level],
452 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
453 )
455 np.testing.assert_array_equal(image, expected_image)
457 server.close()
460def test_float32_tile_of_full_resolution_with_bytes():
461 level = 0
462 width = 5
463 height = 6
464 x = 10
465 y = 20
466 t = int(sample_float32_metadata.n_timepoints / 2)
467 z = int(sample_float32_metadata.n_z_slices / 2)
468 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, y:y+height, x:x+width]
469 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
470 qupath_server = _create_qupath_server(sample_float32_metadata)
471 server = QuPathServer(gateway, qupath_server, 'bytes')
473 image = server.read_region(
474 sample_float32_metadata.downsamples[level],
475 Region2D(x, y, width, height, z, t)
476 )
478 np.testing.assert_array_equal(image, expected_image)
480 server.close()
483def test_float32_full_resolution_with_base64():
484 level = 0
485 t = int(sample_float32_metadata.n_timepoints / 2)
486 z = int(sample_float32_metadata.n_z_slices / 2)
487 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
488 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
489 qupath_server = _create_qupath_server(sample_float32_metadata)
490 server = QuPathServer(gateway, qupath_server, 'base_64')
492 image = server.read_region(
493 sample_float32_metadata.downsamples[level],
494 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
495 )
497 np.testing.assert_array_equal(image, expected_image)
499 server.close()
502def test_float32_lowest_resolution_with_base64():
503 level = sample_float32_metadata.n_resolutions - 1
504 t = int(sample_float32_metadata.n_timepoints / 2)
505 z = int(sample_float32_metadata.n_z_slices / 2)
506 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, ...]
507 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
508 qupath_server = _create_qupath_server(sample_float32_metadata)
509 server = QuPathServer(gateway, qupath_server, 'base_64')
511 image = server.read_region(
512 sample_float32_metadata.downsamples[level],
513 Region2D(width=sample_float32_metadata.width, height=sample_float32_metadata.height, z=z, t=t)
514 )
516 np.testing.assert_array_equal(image, expected_image)
518 server.close()
521def test_float32_tile_of_full_resolution_with_base64():
522 level = 0
523 width = 5
524 height = 6
525 x = 10
526 y = 20
527 t = int(sample_float32_metadata.n_timepoints / 2)
528 z = int(sample_float32_metadata.n_z_slices / 2)
529 expected_image = np.array(sample_float32_pixels[level], dtype=sample_float32_metadata.dtype)[t, :, z, y:y+height, x:x+width]
530 gateway = _create_gateway(sample_float32_metadata, sample_float32_pixels)
531 qupath_server = _create_qupath_server(sample_float32_metadata)
532 server = QuPathServer(gateway, qupath_server, 'base_64')
534 image = server.read_region(
535 sample_float32_metadata.downsamples[level],
536 Region2D(x, y, width, height, z, t)
537 )
539 np.testing.assert_array_equal(image, expected_image)
541 server.close()