Coverage for tests/objects/test_image_feature.py: 100%
275 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 geojson
2import math
3import numpy as np
4from qubalab.objects.image_feature import ImageFeature
5from qubalab.objects.classification import Classification
6from qubalab.objects.object_type import ObjectType
9def test_geometry():
10 expected_geometry = geojson.Point((-115.81, 37.24))
11 image_feature = ImageFeature(expected_geometry)
13 geometry = image_feature.geometry
15 assert geometry == expected_geometry
18def test_json_serializable_with_geometry():
19 geometry = geojson.Point((-115.81, 37.24))
21 image_feature = ImageFeature(geometry)
23 geojson.dumps(image_feature) # will throw an exception if not serializable
26def test_id():
27 expected_id = 23
28 image_feature = ImageFeature(None, id=expected_id)
30 id = image_feature.id
32 assert id == expected_id
35def test_json_serializable_with_id():
36 id = 23
38 image_feature = ImageFeature(None, id=id)
40 geojson.dumps(image_feature) # will throw an exception if not serializable
43def test_classification():
44 expected_classification = Classification("name", (1, 1, 1))
45 image_feature = ImageFeature(None, classification=expected_classification)
47 classification = image_feature.classification
49 assert classification == expected_classification
52def test_json_serializable_with_classification():
53 classification = Classification("name", (1, 1, 1))
55 image_feature = ImageFeature(None, classification=classification)
57 geojson.dumps(image_feature) # will throw an exception if not serializable
60def test_name():
61 expected_name = "name"
62 image_feature = ImageFeature(None, name=expected_name)
64 name = image_feature.name
66 assert name == expected_name
69def test_json_serializable_with_name():
70 name = "name"
72 image_feature = ImageFeature(None, name=name)
74 geojson.dumps(image_feature) # will throw an exception if not serializable
77def test_measurements():
78 inital_measurements = {
79 "some_value": 0.324,
80 "nan_value": float('nan')
81 }
82 expected_measurements = {k: v for k, v in inital_measurements.items() if not math.isnan(v)} # NaN values are skipped
83 image_feature = ImageFeature(None, measurements=inital_measurements)
85 measurements = image_feature.measurements
87 assert measurements == expected_measurements
90def test_json_serializable_with_measurements():
91 measurements = {
92 "some_value": 0.324,
93 "nan_value": float('nan')
94 }
96 image_feature = ImageFeature(None, measurements=measurements)
98 geojson.dumps(image_feature) # will throw an exception if not serializable
101def test_object_type():
102 expected_object_type = ObjectType.DETECTION
103 image_feature = ImageFeature(None, object_type=expected_object_type)
105 object_type = image_feature.object_type
107 assert object_type == expected_object_type
110def test_json_serializable_with_object_type():
111 object_type = ObjectType.DETECTION
113 image_feature = ImageFeature(None, object_type=object_type)
115 geojson.dumps(image_feature) # will throw an exception if not serializable
118def test_is_detection():
119 expected_object_type = ObjectType.DETECTION
120 image_feature = ImageFeature(None, object_type=expected_object_type)
122 is_detection = image_feature.is_detection
124 assert is_detection
127def test_is_not_detection():
128 expected_object_type = ObjectType.ANNOTATION
129 image_feature = ImageFeature(None, object_type=expected_object_type)
131 is_detection = image_feature.is_detection
133 assert not(is_detection)
136def test_is_cell():
137 expected_object_type = ObjectType.CELL
138 image_feature = ImageFeature(None, object_type=expected_object_type)
140 is_cell = image_feature.is_cell
142 assert is_cell
145def test_is_not_cell():
146 expected_object_type = ObjectType.DETECTION
147 image_feature = ImageFeature(None, object_type=expected_object_type)
149 is_cell = image_feature.is_cell
151 assert not(is_cell)
154def test_is_tile():
155 expected_object_type = ObjectType.TILE
156 image_feature = ImageFeature(None, object_type=expected_object_type)
158 is_tile = image_feature.is_tile
160 assert is_tile
163def test_is_not_tile():
164 expected_object_type = ObjectType.DETECTION
165 image_feature = ImageFeature(None, object_type=expected_object_type)
167 is_tile = image_feature.is_tile
169 assert not(is_tile)
172def test_is_annotation():
173 expected_object_type = ObjectType.ANNOTATION
174 image_feature = ImageFeature(None, object_type=expected_object_type)
176 is_annotation = image_feature.is_annotation
178 assert is_annotation
181def test_is_not_annotation():
182 expected_object_type = ObjectType.DETECTION
183 image_feature = ImageFeature(None, object_type=expected_object_type)
185 is_annotation = image_feature.is_annotation
187 assert not(is_annotation)
190def test_color():
191 expected_color = (4, 5, 6)
192 image_feature = ImageFeature(None, color=expected_color)
194 color = image_feature.color
196 assert color == expected_color
199def test_json_serializable_with_color():
200 color = (4, 5, 6)
202 image_feature = ImageFeature(None, color=color)
204 geojson.dumps(image_feature) # will throw an exception if not serializable
207def test_nucleus_geometry():
208 expected_nucleus_geometry = geojson.Point((-115.81, 37.24))
209 image_feature = ImageFeature(None, extra_geometries={
210 "nucleus": expected_nucleus_geometry
211 })
213 nucleus_geometry = image_feature.nucleus_geometry
215 assert nucleus_geometry == expected_nucleus_geometry
218def test_json_serializable_with_nucleus_geometry():
219 nucleus_geometry = geojson.Point((-115.81, 37.24))
221 image_feature = ImageFeature(None, extra_geometries={
222 "nucleus": nucleus_geometry
223 })
225 geojson.dumps(image_feature) # will throw an exception if not serializable
228def test_geometry_when_created_from_feature():
229 expected_geometry = geojson.Point((-115.81, 37.24))
230 feature = geojson.Feature(geometry=expected_geometry)
231 image_feature = ImageFeature.create_from_feature(feature)
233 geometry = image_feature.geometry
235 assert geometry == expected_geometry
238def test_id_when_created_from_feature():
239 expected_id = 23
240 feature = geojson.Feature(id=expected_id)
241 image_feature = ImageFeature.create_from_feature(feature)
243 id = image_feature.id
245 assert id == expected_id
248def test_classification_when_created_from_feature():
249 expected_classification = Classification("name", (1, 1, 1))
250 feature = geojson.Feature(properties={
251 "classification": expected_classification
252 })
253 image_feature = ImageFeature.create_from_feature(feature)
255 classification = image_feature.classification
257 assert classification == expected_classification
260def test_name_when_created_from_feature():
261 expected_name = "name"
262 feature = geojson.Feature(properties={
263 "name": expected_name
264 })
265 image_feature = ImageFeature.create_from_feature(feature)
267 name = image_feature.name
269 assert name == expected_name
272def test_measurements_when_created_from_feature():
273 inital_measurements = {
274 "some_value": 0.324,
275 "nan_value": float('nan')
276 }
277 expected_measurements = {k: v for k, v in inital_measurements.items() if not math.isnan(v)} # NaN values are skipped
278 feature = geojson.Feature(properties={
279 "measurements": inital_measurements
280 })
281 image_feature = ImageFeature.create_from_feature(feature)
283 measurements = image_feature.measurements
285 assert measurements == expected_measurements
288def test_object_type_when_created_from_feature():
289 expected_object_type = ObjectType.CELL
290 feature = geojson.Feature(properties={
291 "object_type": expected_object_type.name
292 })
293 image_feature = ImageFeature.create_from_feature(feature)
295 object_type = image_feature.object_type
297 assert object_type == expected_object_type
300def test_color_when_created_from_feature():
301 expected_color = (4, 5, 6)
302 feature = geojson.Feature(properties={
303 "color": expected_color
304 })
305 image_feature = ImageFeature.create_from_feature(feature)
307 color = image_feature.color
309 assert color == expected_color
312def test_nucleus_geometry_when_created_from_feature():
313 expected_nucleus_geometry = geojson.Point((-115.81, 37.24))
314 feature = geojson.Feature(properties={
315 "nucleusGeometry": expected_nucleus_geometry
316 })
317 image_feature = ImageFeature.create_from_feature(feature)
319 nucleus_geometry = image_feature.nucleus_geometry
321 assert nucleus_geometry == expected_nucleus_geometry
324def test_number_of_features_when_created_from_label_image_without_scale():
325 label_image = np.array(
326 [[0, 1, 1, 0, 0],
327 [0, 1, 1, 0, 0],
328 [0, 0, 0, 0, 0],
329 [0, 0, 0, 1, 0],
330 [0, 0, 0, 1, 0],
331 [0, 0, 0, 0, 0],
332 [0, 0, 2, 2, 0],
333 [0, 0, 0, 0, 0],
334 [0, 0, 0, 0, 0],
335 [0, 3, 0, 0, 0],],
336 dtype=np.uint8
337 )
338 expected_number_of_features = 3
340 features = ImageFeature.create_from_label_image(label_image)
342 assert len(features) == expected_number_of_features
345def test_number_of_features_when_created_from_label_image_with_scale():
346 scale = 2
347 label_image = np.array(
348 [[0, 1, 1, 0, 0],
349 [0, 1, 1, 0, 0],
350 [0, 0, 0, 0, 0],
351 [0, 0, 0, 1, 0],
352 [0, 0, 0, 1, 0],
353 [0, 0, 0, 0, 0],
354 [0, 0, 2, 2, 0],
355 [0, 0, 0, 0, 0],
356 [0, 0, 0, 0, 0],
357 [0, 3, 0, 0, 0],],
358 dtype=np.uint8
359 )
360 expected_number_of_features = 3
362 features = ImageFeature.create_from_label_image(label_image, scale=scale)
364 assert len(features) == expected_number_of_features
367def test_object_type_when_created_from_label_image():
368 expected_object_type = ObjectType.CELL
369 label_image = np.array(
370 [[0, 1, 1, 0, 0],
371 [0, 1, 1, 0, 0],
372 [0, 0, 0, 0, 0],
373 [0, 0, 0, 1, 0],
374 [0, 0, 0, 1, 0],
375 [0, 0, 0, 0, 0],
376 [0, 0, 2, 2, 0],
377 [0, 0, 0, 0, 0],
378 [0, 0, 0, 0, 0],
379 [0, 3, 0, 0, 0],],
380 dtype=np.uint8
381 )
383 features = ImageFeature.create_from_label_image(label_image, object_type=expected_object_type)
385 assert all(feature.object_type == expected_object_type for feature in features)
388def test_measurement_when_created_from_label_image():
389 expected_measurements = [{'Label': float(label)} for label in [1, 2, 3]]
390 label_image = np.array(
391 [[0, 1, 1, 0, 0],
392 [0, 1, 1, 0, 0],
393 [0, 0, 0, 0, 0],
394 [0, 0, 0, 1, 0],
395 [0, 0, 0, 1, 0],
396 [0, 0, 0, 0, 0],
397 [0, 0, 2, 2, 0],
398 [0, 0, 0, 0, 0],
399 [0, 0, 0, 0, 0],
400 [0, 3, 0, 0, 0],],
401 dtype=np.uint8
402 )
404 features = ImageFeature.create_from_label_image(label_image, include_labels=True)
406 assert all(feature.measurements in expected_measurements for feature in features)
409def test_classification_when_created_from_label_image_and_classification_name_provided():
410 expected_classification_name = "name"
411 label_image = np.array(
412 [[0, 1, 1, 0, 0],
413 [0, 1, 1, 0, 0],
414 [0, 0, 0, 0, 0],
415 [0, 0, 0, 1, 0],
416 [0, 0, 0, 1, 0],
417 [0, 0, 0, 0, 0],
418 [0, 0, 2, 2, 0],
419 [0, 0, 0, 0, 0],
420 [0, 0, 0, 0, 0],
421 [0, 3, 0, 0, 0],],
422 dtype=np.uint8
423 )
425 features = ImageFeature.create_from_label_image(label_image, classification_names=expected_classification_name)
427 assert all(feature.classification.name == expected_classification_name for feature in features)
430def test_classification_when_created_from_label_image_and_classification_dict_provided():
431 classification_dict = {
432 # no classification for label 1
433 2: "name2",
434 3: "name3"
435 }
436 expected_classification_names = classification_dict.values()
437 label_image = np.array(
438 [[0, 1, 1, 0, 0],
439 [0, 1, 1, 0, 0],
440 [0, 0, 0, 0, 0],
441 [0, 0, 0, 1, 0],
442 [0, 0, 0, 1, 0],
443 [0, 0, 0, 0, 0],
444 [0, 0, 2, 2, 0],
445 [0, 0, 0, 0, 0],
446 [0, 0, 0, 0, 0],
447 [0, 3, 0, 0, 0],],
448 dtype=np.uint8
449 )
451 features = ImageFeature.create_from_label_image(label_image, classification_names=classification_dict)
453 assert all(feature.classification is None or feature.classification.name in expected_classification_names for feature in features)
456def test_number_of_features_when_created_from_binary_image_without_scale():
457 binary_image = np.array(
458 [[False, True, True, False, False],
459 [False, True, True, False, False],
460 [False, False, False, False, False],
461 [False, False, False, True, False],
462 [False, False, False, True, False],
463 [False, False, False, False, False],
464 [False, False, True, True, False],
465 [False, False, False, False, False],
466 [False, False, False, False, False],
467 [False, True, False, False, False],],
468 dtype=bool
469 )
470 expected_number_of_features = 1
472 features = ImageFeature.create_from_label_image(binary_image)
474 assert len(features) == expected_number_of_features
477def test_number_of_features_when_created_from_binary_image_with_scale():
478 scale = 2
479 binary_image = np.array(
480 [[False, True, True, False, False],
481 [False, True, True, False, False],
482 [False, False, False, False, False],
483 [False, False, False, True, False],
484 [False, False, False, True, False],
485 [False, False, False, False, False],
486 [False, False, True, True, False],
487 [False, False, False, False, False],
488 [False, False, False, False, False],
489 [False, True, False, False, False],],
490 dtype=bool
491 )
492 expected_number_of_features = 1
494 features = ImageFeature.create_from_label_image(binary_image, scale=scale)
496 assert len(features) == expected_number_of_features
499def test_object_type_when_created_from_binary_image():
500 expected_object_type = ObjectType.TILE
501 binary_image = np.array(
502 [[False, True, True, False, False],
503 [False, True, True, False, False],
504 [False, False, False, False, False],
505 [False, False, False, True, False],
506 [False, False, False, True, False],
507 [False, False, False, False, False],
508 [False, False, True, True, False],
509 [False, False, False, False, False],
510 [False, False, False, False, False],
511 [False, True, False, False, False],],
512 dtype=bool
513 )
515 features = ImageFeature.create_from_label_image(binary_image, object_type=expected_object_type)
517 assert all(feature.object_type == expected_object_type for feature in features)
520def test_measurement_when_created_from_binary_image():
521 expected_measurement = {'Label': 1.0}
522 binary_image = np.array(
523 [[False, True, True, False, False],
524 [False, True, True, False, False],
525 [False, False, False, False, False],
526 [False, False, False, True, False],
527 [False, False, False, True, False],
528 [False, False, False, False, False],
529 [False, False, True, True, False],
530 [False, False, False, False, False],
531 [False, False, False, False, False],
532 [False, True, False, False, False],],
533 dtype=bool
534 )
536 features = ImageFeature.create_from_label_image(binary_image, include_labels=True)
538 assert all(feature.measurements == expected_measurement for feature in features)
541def test_classification_when_created_from_binary_image_and_classification_name_provided():
542 expected_classification_name = "name"
543 binary_image = np.array(
544 [[False, True, True, False, False],
545 [False, True, True, False, False],
546 [False, False, False, False, False],
547 [False, False, False, True, False],
548 [False, False, False, True, False],
549 [False, False, False, False, False],
550 [False, False, True, True, False],
551 [False, False, False, False, False],
552 [False, False, False, False, False],
553 [False, True, False, False, False],],
554 dtype=bool
555 )
557 features = ImageFeature.create_from_label_image(binary_image, classification_names=expected_classification_name)
559 assert all(feature.classification.name == expected_classification_name for feature in features)
562def test_classification_when_created_from_binary_image_and_classification_dict_provided():
563 classification_dict = {
564 1: "name1"
565 }
566 expected_classification_names = classification_dict.values()
567 binary_image = np.array(
568 [[False, True, True, False, False],
569 [False, True, True, False, False],
570 [False, False, False, False, False],
571 [False, False, False, True, False],
572 [False, False, False, True, False],
573 [False, False, False, False, False],
574 [False, False, True, True, False],
575 [False, False, False, False, False],
576 [False, False, False, False, False],
577 [False, True, False, False, False],],
578 dtype=bool
579 )
581 features = ImageFeature.create_from_label_image(binary_image, classification_names=classification_dict)
583 assert all(feature.classification.name in expected_classification_names for feature in features)
586def test_classification_when_set_after_creation():
587 expected_classification = Classification("name", (1, 1, 1))
588 image_feature = ImageFeature(None)
589 image_feature.classification = {
590 "name": expected_classification.name,
591 "color": expected_classification.color
592 }
594 classification = image_feature.classification
596 assert classification == expected_classification
599def test_name_when_set_after_creation():
600 expected_name = "name"
601 image_feature = ImageFeature(None)
602 image_feature.name = expected_name
604 name = image_feature.name
606 assert name == expected_name
609def test_measurements_when_set_after_creation():
610 inital_measurements = {
611 "some_value": 0.324,
612 "nan_value": float('nan')
613 }
614 expected_measurements = {k: v for k, v in inital_measurements.items() if not math.isnan(v)} # NaN values are skipped
615 image_feature = ImageFeature(None)
616 image_feature.measurements = inital_measurements
618 measurements = image_feature.measurements
620 assert measurements == expected_measurements
623def test_object_type_when_set_after_creation():
624 expected_object_type = ObjectType.CELL
625 image_feature = ImageFeature(None)
626 image_feature.object_type = expected_object_type
628 object_type = image_feature.object_type
630 assert object_type == expected_object_type
633def test_object_type_name_when_set_after_creation():
634 expected_object_type = ObjectType.TILE
635 image_feature = ImageFeature(None)
636 image_feature.object_type = expected_object_type.name
638 object_type = image_feature.object_type
640 assert object_type == expected_object_type
643def test_color_when_set_after_creation():
644 expected_color = (4, 5, 6)
645 image_feature = ImageFeature(None)
646 image_feature.color = expected_color
648 color = image_feature.color
650 assert color == expected_color
653def test_nucleus_geometry_when_set_after_creation():
654 expected_nucleus_geometry = geojson.Point((-115.81, 37.24))
655 image_feature = ImageFeature(None)
656 image_feature.nucleus_geometry = expected_nucleus_geometry
658 nucleus_geometry = image_feature.nucleus_geometry
660 assert nucleus_geometry == expected_nucleus_geometry