pathology-image-features 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. pathology_image_features/__init__.py +0 -0
  2. pathology_image_features/ann_io/README.md +51 -0
  3. pathology_image_features/ann_io/__init__.py +33 -0
  4. pathology_image_features/ann_io/convert.py +340 -0
  5. pathology_image_features/ann_io/read.py +123 -0
  6. pathology_image_features/ann_io/write.py +108 -0
  7. pathology_image_features/ontology/__init__.py +372 -0
  8. pathology_image_features/ontology/classes/Resource/Container/Container.py +39 -0
  9. pathology_image_features/ontology/classes/Resource/Container/SpatialObjectCollection/SpatialObjectCollection.py +307 -0
  10. pathology_image_features/ontology/classes/Resource/Container/SpatialObjectCollection/__init__.py +11 -0
  11. pathology_image_features/ontology/classes/Resource/Container/__init__.py +3 -0
  12. pathology_image_features/ontology/classes/Resource/Container/network_graph/__init__.py +3 -0
  13. pathology_image_features/ontology/classes/Resource/Container/network_graph/directed_graph/__init__.py +7 -0
  14. pathology_image_features/ontology/classes/Resource/Container/network_graph/directed_graph/directed_graph.py +42 -0
  15. pathology_image_features/ontology/classes/Resource/Container/network_graph/directed_graph/nearest_neighbor_graph/__init__.py +3 -0
  16. pathology_image_features/ontology/classes/Resource/Container/network_graph/directed_graph/nearest_neighbor_graph/nearest_neighbor_graph.py +59 -0
  17. pathology_image_features/ontology/classes/Resource/Container/network_graph/network_graph.py +22 -0
  18. pathology_image_features/ontology/classes/Resource/Container/network_graph/undirected_graph/__init__.py +3 -0
  19. pathology_image_features/ontology/classes/Resource/Container/network_graph/undirected_graph/undirected_acyclic_graph/__init__.py +3 -0
  20. pathology_image_features/ontology/classes/Resource/Container/network_graph/undirected_graph/undirected_acyclic_graph/undirected_acyclic_graph.py +60 -0
  21. pathology_image_features/ontology/classes/Resource/Container/network_graph/undirected_graph/undirected_graph.py +32 -0
  22. pathology_image_features/ontology/classes/Resource/Container/tessellation/__init__.py +3 -0
  23. pathology_image_features/ontology/classes/Resource/Container/tessellation/tessellation.py +244 -0
  24. pathology_image_features/ontology/classes/Resource/Resource.py +23 -0
  25. pathology_image_features/ontology/classes/Resource/__init__.py +3 -0
  26. pathology_image_features/ontology/classes/SpatialObject/SpatialObject.py +252 -0
  27. pathology_image_features/ontology/classes/SpatialObject/__init__.py +7 -0
  28. pathology_image_features/ontology/classes/__init__.py +308 -0
  29. pathology_image_features/ontology/classes/information_content_entity/__init__.py +5 -0
  30. pathology_image_features/ontology/classes/information_content_entity/data_entity/__init__.py +3 -0
  31. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_entity.py +19 -0
  32. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/__init__.py +9 -0
  33. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/data_transformation_parameter.py +36 -0
  34. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/numeric_parameter/__init__.py +7 -0
  35. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/numeric_parameter/numeric_parameter.py +42 -0
  36. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/spatial_predicate/__init__.py +23 -0
  37. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/spatial_predicate/spatial_predicate.py +182 -0
  38. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/string_parameter/__init__.py +7 -0
  39. pathology_image_features/ontology/classes/information_content_entity/data_entity/data_transformation_parameter/string_parameter/string_parameter.py +89 -0
  40. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/__init__.py +5 -0
  41. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/data_set_of_features/__init__.py +3 -0
  42. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/data_set_of_features/data_set_of_features.py +130 -0
  43. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/homogenous_data_collection.py +31 -0
  44. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/image_data_set/__init__.py +7 -0
  45. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/image_data_set/annotated_image_data_set/__init__.py +11 -0
  46. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/image_data_set/annotated_image_data_set/annotated_image_data_set.py +211 -0
  47. pathology_image_features/ontology/classes/information_content_entity/data_entity/homogenous_data_collection/image_data_set/image_data_set.py +358 -0
  48. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/__init__.py +11 -0
  49. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/image_data_set_annotation.py +192 -0
  50. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/image_data_set_pixel_mask/__init__.py +9 -0
  51. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/image_data_set_pixel_mask/image_data_set_heat_map/__init__.py +0 -0
  52. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/image_data_set_pixel_mask/image_data_set_heat_map/image_data_set_heat_map.py +32 -0
  53. pathology_image_features/ontology/classes/information_content_entity/data_entity/image_data_set_annotation/image_data_set_pixel_mask/image_data_set_pixel_mask.py +262 -0
  54. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/__init__.py +3 -0
  55. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/directive_information_entity.py +22 -0
  56. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/__init__.py +7 -0
  57. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/algorithm/__init__.py +3 -0
  58. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/algorithm/algorithm.py +22 -0
  59. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/algorithm/machine_learning_algorithm/__init__.py +6 -0
  60. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/algorithm/machine_learning_algorithm/machine_learning_algorithm.py +27 -0
  61. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/plan_specification.py +44 -0
  62. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/software/__init__.py +17 -0
  63. pathology_image_features/ontology/classes/information_content_entity/directive_information_entity/plan_specification/software/software.py +80 -0
  64. pathology_image_features/ontology/classes/information_content_entity/information_content_entity.py +33 -0
  65. pathology_image_features/ontology/classes/information_content_entity/value_specification/__init__.py +3 -0
  66. pathology_image_features/ontology/classes/information_content_entity/value_specification/scalar_value_specification/__init__.py +3 -0
  67. pathology_image_features/ontology/classes/information_content_entity/value_specification/scalar_value_specification/scalar_value_specification.py +35 -0
  68. pathology_image_features/ontology/classes/information_content_entity/value_specification/value_specification.py +22 -0
  69. pathology_image_features/ontology/classes/process/__init__.py +5 -0
  70. pathology_image_features/ontology/classes/process/planned_process/__init__.py +6 -0
  71. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/__init__.py +7 -0
  72. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/completely_executed_planned_process.py +19 -0
  73. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_annotation/__init__.py +7 -0
  74. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_annotation/data_annotation.py +60 -0
  75. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/__init__.py +5 -0
  76. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_aggregation_process/__init__.py +6 -0
  77. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_aggregation_process/data_aggregation_process.py +47 -0
  78. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_aggregation_process/descriptive_statistical_calculation_data_transformation/__init__.py +31 -0
  79. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_aggregation_process/descriptive_statistical_calculation_data_transformation/descriptive_statistical_calculation_data_transformation.py +248 -0
  80. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_filtration_process/__init__.py +0 -0
  81. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_filtration_process/data_filtration_process.py +61 -0
  82. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/data_transformation.py +285 -0
  83. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/__init__.py +7 -0
  84. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_based_machine_learning_algorithm_execution/__init__.py +5 -0
  85. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_based_machine_learning_algorithm_execution/image_based_machine_learning_algorithm_execution.py +34 -0
  86. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_based_machine_learning_algorithm_execution/image_based_machine_learning_algorithm_inference/__init__.py +11 -0
  87. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_based_machine_learning_algorithm_execution/image_based_machine_learning_algorithm_inference/image_based_machine_learning_algorithm_inference.py +85 -0
  88. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/__init__.py +7 -0
  89. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_characteristic_quantification_process.py +76 -0
  90. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_intensity_characteristic_quantification_process/__init__.py +13 -0
  91. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_intensity_characteristic_quantification_process/image_intensity_characteristic_quantification_process.py +95 -0
  92. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/__init__.py +7 -0
  93. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/image_morphological_characteristic_quantification_process.py +33 -0
  94. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/image_shape_characteristic_quantification_process/__init__.py +21 -0
  95. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/image_shape_characteristic_quantification_process/image_shape_characteristic_quantification_process.py +567 -0
  96. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/image_size_characteristic_quantification_process/__init__.py +17 -0
  97. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_morphological_characteristic_quantification_process/image_size_characteristic_quantification_process/image_size_characteristic_quantification_process.py +283 -0
  98. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_spatial_position_characteristic_quantification_process/__init__.py +15 -0
  99. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_spatial_position_characteristic_quantification_process/image_spatial_position_characteristic_quantification_process.py +224 -0
  100. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_spatial_position_characteristic_quantification_process/image_spatial_relationship_characteristic_quantification_process/__init__.py +23 -0
  101. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_characteristic_quantification_process/image_spatial_position_characteristic_quantification_process/image_spatial_relationship_characteristic_quantification_process/image_spatial_relationship_characteristic_quantification_process.py +206 -0
  102. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_data_set_analysis.py +35 -0
  103. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_data_set_annotation_transformation/__init__.py +13 -0
  104. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/image_data_set_analysis/image_data_set_annotation_transformation/image_data_set_annotation_transformation.py +303 -0
  105. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/__init__.py +13 -0
  106. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/network_analysis.py +52 -0
  107. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/network_graph_construction/__init__.py +6 -0
  108. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/network_graph_construction/nearest_neighbor_graph_construction/__init__.py +9 -0
  109. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/network_graph_construction/nearest_neighbor_graph_construction/nearest_neighbor_graph_construction.py +156 -0
  110. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/network_analysis/network_graph_construction/network_graph_construction.py +71 -0
  111. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/tessellation_analysis/__init__.py +5 -0
  112. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/tessellation_analysis/tessellation_analysis.py +60 -0
  113. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/tessellation_analysis/tessellation_construction/__init__.py +11 -0
  114. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/data_transformation/tessellation_analysis/tessellation_construction/tessellation_construction.py +121 -0
  115. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/machine_learning/__init__.py +13 -0
  116. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/machine_learning/machine_learning.py +101 -0
  117. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/machine_learning/machine_learning_model_inference/__init__.py +11 -0
  118. pathology_image_features/ontology/classes/process/planned_process/completely_executed_planned_process/machine_learning/machine_learning_model_inference/machine_learning_model_inference.py +83 -0
  119. pathology_image_features/ontology/classes/process/planned_process/planned_process.py +19 -0
  120. pathology_image_features/ontology/classes/process/process.py +19 -0
  121. pathology_image_features/ontology/classes/unit/__init__.py +5 -0
  122. pathology_image_features/ontology/classes/unit/angle_unit/__init__.py +5 -0
  123. pathology_image_features/ontology/classes/unit/angle_unit/angle_unit.py +21 -0
  124. pathology_image_features/ontology/classes/unit/angle_unit/plane_angle_unit/__init__.py +7 -0
  125. pathology_image_features/ontology/classes/unit/angle_unit/plane_angle_unit/plane_angle_unit.py +47 -0
  126. pathology_image_features/ontology/classes/unit/area_unit/__init__.py +15 -0
  127. pathology_image_features/ontology/classes/unit/area_unit/area_unit.py +52 -0
  128. pathology_image_features/ontology/classes/unit/dimensionless_unit/__init__.py +6 -0
  129. pathology_image_features/ontology/classes/unit/dimensionless_unit/dimensionless_unit.py +30 -0
  130. pathology_image_features/ontology/classes/unit/dimensionless_unit/ratio_unit/__init__.py +8 -0
  131. pathology_image_features/ontology/classes/unit/dimensionless_unit/ratio_unit/ratio_unit.py +32 -0
  132. pathology_image_features/ontology/classes/unit/information_unit/__init__.py +7 -0
  133. pathology_image_features/ontology/classes/unit/information_unit/image_resolution_unit/__init__.py +4 -0
  134. pathology_image_features/ontology/classes/unit/information_unit/image_resolution_unit/image_resolution_unit.py +19 -0
  135. pathology_image_features/ontology/classes/unit/information_unit/image_resolution_unit/spatial_resolution_unit/__init__.py +5 -0
  136. pathology_image_features/ontology/classes/unit/information_unit/image_resolution_unit/spatial_resolution_unit/spatial_resolution_unit.py +27 -0
  137. pathology_image_features/ontology/classes/unit/information_unit/information_unit.py +35 -0
  138. pathology_image_features/ontology/classes/unit/length_unit/__init__.py +7 -0
  139. pathology_image_features/ontology/classes/unit/length_unit/length_unit.py +36 -0
  140. pathology_image_features/ontology/classes/unit/unit.py +19 -0
  141. pathology_image_features/ontology/datatypes.py +20 -0
  142. pathology_image_features/ontology/individuals/__init__.py +0 -0
  143. pathology_image_features/ontology/individuals/unit.py +86 -0
  144. pathology_image_features/ontology/pifo.owl +5615 -0
  145. pathology_image_features/ontology/properties/__init__.py +304 -0
  146. pathology_image_features/ontology/restrictions/__init__.py +582 -0
  147. pathology_image_features/utils/README.md +9 -0
  148. pathology_image_features/utils/__init__.py +0 -0
  149. pathology_image_features/utils/general.py +268 -0
  150. pathology_image_features/utils/visualization.py +251 -0
  151. pathology_image_features-0.2.0.dist-info/METADATA +65 -0
  152. pathology_image_features-0.2.0.dist-info/RECORD +154 -0
  153. pathology_image_features-0.2.0.dist-info/WHEEL +4 -0
  154. pathology_image_features-0.2.0.dist-info/licenses/LICENSE +21 -0
File without changes
@@ -0,0 +1,51 @@
1
+ # *pathology-image-features* I/O module
2
+
3
+ This module contains methods for input and output of various annotation types.
4
+
5
+ Supported datatypes include:
6
+
7
+ - *large-image*
8
+ - Extensions: .json
9
+ - Schema: https://girder.github.io/large_image/annotations.html
10
+ - Functions:
11
+ - Read: `read_histomics`
12
+ - Convert: `histomics_to_geojson`, `histomics_to_aperio`
13
+ - Write: `write_histomics`
14
+
15
+ - *GeoJSON*
16
+ - Extensions: .json, .geojson
17
+ - Schema: https://geojson.org/
18
+ - Functions:
19
+ - Read: `read_geojson`
20
+ - Convert: `geojson_to_histomics`, `geojson_to_aperio`
21
+ - Write: `write_geojson`
22
+
23
+ - *Aperio ImageScope*
24
+ - Extensions: .xml
25
+ - Schema: (None provided)
26
+ - Functions:
27
+ - Read: `read_aperio`
28
+ - Convert: `aperio_to_geojson`, `aperio_to_histomics`
29
+ - Write: `write_aperio`
30
+
31
+ - *Parquet*
32
+ - Extensions: .csv
33
+ - Schema: (see GeoJSON)
34
+ - Functions:
35
+ - Read: (In Progress)
36
+ - Convert: (In Progress)
37
+ - Write: (In Progress)
38
+
39
+ - *Image annotation types*: (see below)
40
+ - Extensions: .jpg, .png, .tiff, .ome.tiff, ...
41
+ - Schema: (see below)
42
+ - Functions:
43
+ - Read: (In Progress)
44
+ - Convert: (In Progress)
45
+ - Write: (In Progress)
46
+
47
+ Image annotation types
48
+ - "label-mask"
49
+ - 2D mask with a single "class" where each unique index above zero corresponds to one "object"
50
+ - "one-hot"
51
+ - ND mask where each channel is a "class" and each unique index above zero corresponds to one "object" for that "class"
@@ -0,0 +1,33 @@
1
+ from .convert import (
2
+ aperio_to_geojson,
3
+ aperio_to_histomics,
4
+ geojson_to_aperio,
5
+ geojson_to_histomics,
6
+ histomics_to_aperio,
7
+ histomics_to_geojson,
8
+ )
9
+ from .read import (
10
+ read_aperio,
11
+ read_geojson,
12
+ read_histomics,
13
+ )
14
+ from .write import (
15
+ write_aperio,
16
+ write_geojson,
17
+ write_histomics,
18
+ )
19
+
20
+ __all__ = [
21
+ "aperio_to_geojson",
22
+ "aperio_to_histomics",
23
+ "geojson_to_aperio",
24
+ "geojson_to_histomics",
25
+ "histomics_to_aperio",
26
+ "histomics_to_geojson",
27
+ "read_aperio",
28
+ "read_geojson",
29
+ "read_histomics",
30
+ "write_aperio",
31
+ "write_geojson",
32
+ "write_histomics",
33
+ ]
@@ -0,0 +1,340 @@
1
+ """
2
+
3
+ Functions for converting from one annotation format to another
4
+
5
+ Steps:
6
+ - Convert to GeoJSON (if not already GeoJSON)
7
+ - Convert GeoJSON to desired format
8
+
9
+ """
10
+
11
+ import json
12
+ import os
13
+
14
+ import pandas as pd
15
+ from lxml import etree as ET
16
+ from typing_extensions import Union
17
+
18
+ from ..utils.general import unique_id
19
+
20
+
21
+ def histomics_to_geojson(anns: Union[list[dict], dict]) -> Union[list[dict], dict]:
22
+ """Converting Histomics/large-image formatted annotations to GeoJSON
23
+
24
+ :param anns: One or more Histomics/large-image formatted annotations
25
+ :type anns: Union[list,dict]
26
+ :return: Same annotations but in GeoJSON format
27
+ :rtype: Union[list,dict]
28
+ """
29
+
30
+ if type(anns) is dict:
31
+ anns = [anns]
32
+
33
+ geojson_anns = []
34
+ for ann in anns:
35
+ ann_id = ann.get("_id", unique_id())
36
+
37
+ geojson_ann = {
38
+ "type": "FeatureCollection",
39
+ "properties": {"name": ann["annotation"].get("name", ""), "_id": ann_id},
40
+ "features": [],
41
+ }
42
+
43
+ for el_idx, el in enumerate(ann["annotation"].get("elements", [])):
44
+ if el.get("type") == "polyline":
45
+ coords = [el["points"]]
46
+ elif el.get("type") == "rectangle":
47
+ coords = [
48
+ [
49
+ [
50
+ el["center"][0] - (el["width"] / 2),
51
+ el["center"][1] - (el["height"] / 2),
52
+ ],
53
+ [
54
+ el["center"][0] + (el["width"] / 2),
55
+ el["center"][1] - (el["height"] / 2),
56
+ ],
57
+ [
58
+ el["center"][0] + (el["width"] / 2),
59
+ el["center"][1] + (el["height"] / 2),
60
+ ],
61
+ [
62
+ el["center"][0] - (el["width"] / 2),
63
+ el["center"][1] + (el["height"] / 2),
64
+ ],
65
+ [
66
+ el["center"][0] - (el["width"] / 2),
67
+ el["center"][1] + (el["height"] / 2),
68
+ ],
69
+ ]
70
+ ]
71
+ else:
72
+ print(f"element type: {el.get('type')} is not implemented")
73
+
74
+ # Checking if there are holes
75
+ holes_list = el.get("holes", [])
76
+
77
+ props_dict = {
78
+ "name": ann["annotation"].get("name", ""),
79
+ "_id": unique_id(),
80
+ "_index": el_idx,
81
+ }
82
+
83
+ if "user" in el:
84
+ props_dict = el["user"] | props_dict
85
+
86
+ geojson_ann["features"].append(
87
+ {
88
+ "type": "Feature",
89
+ "geometry": {
90
+ "type": "Polygon",
91
+ "coordinates": coords
92
+ if len(holes_list) == 0
93
+ else coords + holes_list,
94
+ },
95
+ "properties": props_dict,
96
+ }
97
+ )
98
+
99
+ geojson_anns.append(geojson_ann)
100
+
101
+ if len(anns) == 1:
102
+ geojson_anns = geojson_anns[0]
103
+
104
+ return geojson_anns
105
+
106
+
107
+ def aperio_to_geojson(xml_tree) -> Union[list, dict]:
108
+ """Converting Aperio ImageScope formatted annotations to GeoJSON
109
+
110
+ :param xml_tree: An XML E-Tree that is the output of read_aperio
111
+ :type xml_tree: None
112
+ :return: A list of GeoJSON FeatureCollections for each Annotation
113
+ :rtype: Union[list,dict]
114
+ """
115
+
116
+ layers_in_xml = xml_tree.findall("Annotation")
117
+ geojson_anns = []
118
+ for ann_idx in range(0, len(layers_in_xml)):
119
+ layer_name = (
120
+ f"Layer {ann_idx + 1}",
121
+ ) # TODO: Get the XML layer's "Label" property here
122
+
123
+ geojson_ann = {
124
+ "type": "FeatureCollection",
125
+ "features": [],
126
+ "properties": {"name": layer_name, "_id": unique_id()},
127
+ }
128
+
129
+ layer_regions = xml_tree.findall(
130
+ f'Annotation[@Id="{ann_idx + 1}"]/Regions/Region'
131
+ )
132
+ # Iterating through Regions
133
+ for r_idx, r in enumerate(layer_regions):
134
+ vertices = r.findall("./Vertices/Vertex")
135
+ coords = []
136
+ for vert in vertices:
137
+ coords.append(
138
+ [int(float(vert.attrib["X"])), int(float(vert.attrib["Y"]))]
139
+ )
140
+
141
+ geojson_ann["features"].append(
142
+ {
143
+ "type": "Feature",
144
+ "geometry": {"type": "Polygon", "coordinates": [coords]},
145
+ "properties": {
146
+ "name": layer_name,
147
+ "_id": unique_id(),
148
+ "_index": r_idx,
149
+ },
150
+ }
151
+ )
152
+
153
+ geojson_anns.append(geojson_ann)
154
+
155
+ if len(geojson_anns) == 1:
156
+ geojson_anns = geojson_anns[0]
157
+
158
+ return geojson_anns
159
+
160
+
161
+ def geojson_to_histomics(anns: Union[list, dict]) -> Union[list, dict]:
162
+ """Converting GeoJSON formatted annotations to Histomics/large-image
163
+
164
+ :param anns: One or more GeoJSON formatted annotations
165
+ :type anns: Union[list,dict]
166
+ :return: List or single Histomics formatted annotation
167
+ :rtype: Union[list,dict]
168
+ """
169
+
170
+ if type(anns) is dict:
171
+ anns = [anns]
172
+
173
+ histomics_anns = []
174
+ for g in anns:
175
+ g_name = g.get("properties", {}).get("name", "")
176
+
177
+ histomics_ann = {
178
+ "annotation": {
179
+ "name": g_name,
180
+ "elements": [
181
+ {
182
+ "type": "polyline",
183
+ "user": f.get("properties", {}),
184
+ "closed": True,
185
+ "points": [
186
+ list(i) + [0] if type(i) is tuple else i + [0]
187
+ for i in f["geometry"]["coordinates"][0]
188
+ ],
189
+ "holes": [
190
+ list(i) + [0] if type(i) is tuple else i + [0]
191
+ for i in f["geometry"]["coordinates"][1]
192
+ ]
193
+ if len(f["geometry"]["coordinates"]) == 2
194
+ else [],
195
+ }
196
+ if all([len(i) == 2 for i in f["geometry"]["coordinates"][0]])
197
+ else {
198
+ "type": "polyline",
199
+ "user": f.get("properties", {}),
200
+ "closed": True,
201
+ "points": [
202
+ list(i) if type(i) is tuple else i
203
+ for i in f["geometry"]["coordinates"][0]
204
+ ],
205
+ "holes": [
206
+ list(i) if type(i) is tuple else i
207
+ for i in f["geometry"]["coordinates"][1]
208
+ ]
209
+ if len(f["geometry"]["coordinates"]) == 2
210
+ else [],
211
+ }
212
+ for f in g["features"]
213
+ ],
214
+ }
215
+ }
216
+ histomics_anns.append(histomics_ann)
217
+
218
+ if len(histomics_anns) == 1:
219
+ histomics_anns = histomics_anns[0]
220
+
221
+ return histomics_anns
222
+
223
+
224
+ def geojson_to_aperio(anns: Union[list, dict]) -> ET._ElementTree:
225
+ """Converting GeoJSON formatted annotations to Aperio ImageScope format
226
+
227
+ :param anns: One or more GeoJSON annotations
228
+ :type anns: Union[list,dict]
229
+ """
230
+ output_xml = ET.Element("Annotations")
231
+
232
+ if type(anns) is dict:
233
+ anns = [anns]
234
+
235
+ for a_idx, a in enumerate(anns):
236
+ a_layer = ET.SubElement(
237
+ output_xml,
238
+ "Annotation",
239
+ attrib={
240
+ "Type": "4",
241
+ "Visible": "1",
242
+ "ReadOnly": "0",
243
+ "Incremental": "0",
244
+ "LineColorReadOnly": "0",
245
+ "Id": "1",
246
+ "NameReadOnly": "0",
247
+ "LayerName": a.get("properties", {}).get("name", f"Layer {a_idx + 1}"),
248
+ },
249
+ )
250
+
251
+ a_regions = ET.SubElement(a_layer, "Regions")
252
+
253
+ for g_idx, g in enumerate(a.get("features", [])):
254
+ region = ET.SubElement(
255
+ a_regions,
256
+ "Region",
257
+ attrib={
258
+ "NegativeROA": "0",
259
+ "ImageFocus": "-1",
260
+ "DisplayId": str(g_idx + 1),
261
+ "InputRegionId": "0",
262
+ "Analyze": "0",
263
+ "Type": "0",
264
+ "Id": str(g_idx + 1),
265
+ },
266
+ )
267
+
268
+ vertices = ET.SubElement(region, "Vertices")
269
+
270
+ for vert in g["geometry"]["coordinates"]:
271
+ if len(vert) == 2:
272
+ ET.SubElement(
273
+ vertices,
274
+ "Vertex",
275
+ attrib={"X": str(vert[0]), "Y": str(vert[1]), "Z": "0"},
276
+ )
277
+ elif len(vert) == 3:
278
+ ET.SubElement(
279
+ vertices,
280
+ "Vertex",
281
+ attrib={
282
+ "X": str(vert[0]),
283
+ "Y": str(vert[1]),
284
+ "Z": str(vert[2]),
285
+ },
286
+ )
287
+
288
+ if len(g["geometry"]["coordinates"][0]) == 2:
289
+ ET.SubElement(
290
+ vertices,
291
+ "Vertex",
292
+ attrib={
293
+ "X": str(g["geometry"]["coordinates"][0][0]),
294
+ "Y": str(g["geometry"]["coordinates"][0][1]),
295
+ "Z": "0",
296
+ },
297
+ )
298
+ elif len(g["geometry"]["coordinates"][0]) == 3:
299
+ ET.SubElement(
300
+ vertices,
301
+ "Vertex",
302
+ attrib={
303
+ "X": str(g["geometry"]["coordinates"][0][0]),
304
+ "Y": str(g["geometry"]["coordinates"][0][1]),
305
+ "Z": str(g["geometry"]["coordinates"][0][2]),
306
+ },
307
+ )
308
+
309
+ output_xml_tree = output_xml.getroottree()
310
+
311
+ return output_xml_tree
312
+
313
+
314
+ # These are the "lazy" implementations of converters
315
+ def aperio_to_histomics(xml_tree: ET._ElementTree) -> Union[list, dict]:
316
+ """Converting Aperio ImageScope formatted annotations to Histomics
317
+
318
+ :param xml_tree: XML ETree object containing annotations
319
+ :type xml_tree: _type_
320
+ :return: One or more Histomics formatted annotation layers
321
+ :rtype: Union[list,dict]
322
+ """
323
+
324
+ geojson_anns = aperio_to_geojson(xml_tree)
325
+ histomics_anns = geojson_to_histomics(geojson_anns)
326
+
327
+ return histomics_anns
328
+
329
+
330
+ def histomics_to_aperio(anns: Union[list, dict]) -> ET._ElementTree:
331
+ """Converting Histomics/large-image formatted annotations to Aperio ImageScope
332
+
333
+ :param anns: One or more annotations in Histomics/large-image format
334
+ :type anns: Union[list,dict]
335
+ """
336
+
337
+ geojson_anns = histomics_to_geojson(anns)
338
+ aperio_anns = geojson_to_aperio(geojson_anns)
339
+
340
+ return aperio_anns
@@ -0,0 +1,123 @@
1
+ """
2
+
3
+ Functions for reading different annotation types
4
+
5
+ """
6
+
7
+ import functools
8
+ import json
9
+ import os
10
+
11
+ import pandas as pd
12
+ from lxml import etree as ET
13
+ from numpy.random.mtrand import f
14
+ from typing_extensions import Union
15
+
16
+
17
+ def check_path(func):
18
+ """Decorator function for checking if input file paths in a function exist
19
+
20
+ :param func: Function which takes a filepath as input
21
+ :type func: None
22
+ """
23
+
24
+ @functools.wraps(func)
25
+ def reader(path):
26
+ assert os.path.exists(path), f"File: {path} not found!"
27
+
28
+ return func(path)
29
+
30
+ return reader
31
+
32
+
33
+ def check_ext(allowed_exts: Union[list, None] = None):
34
+ """Decorator function for checking if input file has correct extensions for a given reader.
35
+
36
+ :param allowed_exts: List of allowed file extensions, defaults to None
37
+ :type allowed_exts: Union[list,None], optional
38
+ """
39
+
40
+ def wrapper(func):
41
+ @functools.wraps(func)
42
+ def reader(path):
43
+ if allowed_exts is not None:
44
+ assert path.split(".")[-1] in allowed_exts, (
45
+ f"File: {path} has incorrect extension ({path.split('.')[-1]}). Allowed extensions for this reader are: {allowed_exts}"
46
+ )
47
+ else:
48
+ pass
49
+ return func(path)
50
+
51
+ return reader
52
+
53
+ return wrapper
54
+
55
+
56
+ @check_ext(["json"])
57
+ @check_path
58
+ def read_histomics(ann_path: str) -> Union[list, dict]:
59
+ """Read annotations in Histomics/large-image format
60
+
61
+ For more details on this schema, see: https://girder.github.io/large_image/annotations.html
62
+
63
+ :param ann_path: Path to annotations file (.json extension)
64
+ :type ann_path: str
65
+ :return: Read annotations in original format
66
+ :rtype: Union[list,dict]
67
+ """
68
+
69
+ with open(ann_path, "r") as f:
70
+ read_anns = json.load(f)
71
+ f.close()
72
+
73
+ return read_anns
74
+
75
+
76
+ @check_ext(["json", "geojson"])
77
+ @check_path
78
+ def read_geojson(ann_path: str) -> Union[list, dict]:
79
+ """Read annotations in GeoJSON format
80
+
81
+ For more details on this schema, see: https://geojson.org/
82
+
83
+ :param ann_path: Path to annotation file
84
+ :type ann_path: str
85
+ :return: Read annotations in original format
86
+ :rtype: Union[list,dict]
87
+ """
88
+
89
+ with open(ann_path, "r") as f:
90
+ read_anns = json.load(f)
91
+ f.close()
92
+
93
+ return read_anns
94
+
95
+
96
+ @check_ext(["xml"])
97
+ @check_path
98
+ def read_aperio(ann_path: str) -> ET._ElementTree:
99
+ """Read annotations in Aperio ImageScope format
100
+
101
+ :param ann_path: Path to annotation file
102
+ :type ann_path: str
103
+ :return: Read annotations in original format
104
+ :rtype: Union[list,dict]
105
+ """
106
+
107
+ read_anns = ET.parse(ann_path)
108
+
109
+ return read_anns
110
+
111
+
112
+ # TODO: Same as with read_aperio, "Original format" for Parquet annotations could be different from list/dict
113
+ @check_path
114
+ @check_ext(["csv", "parquet"])
115
+ def read_parquet(ann_path: str) -> Union[list, dict]:
116
+ """Read annotations in Parquet format
117
+
118
+ :param ann_path: Path to annotation file
119
+ :type ann_path: str
120
+ :return: Read annotations in original format
121
+ :rtype: Union[list,dict]
122
+ """
123
+ raise NotImplementedError
@@ -0,0 +1,108 @@
1
+ """
2
+
3
+ Functions for writing different annotation types
4
+
5
+ """
6
+
7
+ import functools
8
+ import json
9
+ import os
10
+
11
+ import pandas as pd
12
+ from lxml import etree as ET
13
+ from typing_extensions import Union
14
+
15
+
16
+ def check_path(func):
17
+ """Decorator function for checking if output path exists.
18
+
19
+ :param func: Function which takes a filepath as input argument for writing a file to
20
+ :type func: None
21
+ """
22
+
23
+ @functools.wraps(func)
24
+ def writer(anns, path):
25
+ assert os.path.isdir(f"{os.sep}".join(path.split(os.sep)[:-1])), (
26
+ f"Folder: {f'{os.sep}'.join(path.split(os.sep)[:-1])} not found!"
27
+ )
28
+
29
+ return func(anns, path)
30
+
31
+ return writer
32
+
33
+
34
+ def check_ext(allowed_exts: Union[list, None] = None):
35
+ """Decorator function for checking if output file path has correct file extension
36
+
37
+ :param allowed_exts: List of allowed file extensions, defaults to None
38
+ :type allowed_exts: Union[list,None], optional
39
+ """
40
+
41
+ def wrapper(func):
42
+ @functools.wraps(func)
43
+ def writer(anns, path):
44
+ if allowed_exts is not None:
45
+ assert path.split(".")[-1] in allowed_exts, (
46
+ f"Output file: {path.split(os.sep)[-1]} has incorrect extension ({path.split('.')[-1]}), Allowed extensions for this writer are: {allowed_exts}"
47
+ )
48
+ else:
49
+ pass
50
+
51
+ return func(anns, path)
52
+
53
+ return writer
54
+
55
+ return wrapper
56
+
57
+
58
+ @check_ext(["json"])
59
+ @check_path
60
+ def write_histomics(anns: Union[list, dict], output_path: str):
61
+ """Write annotations to Histomics/large-image format
62
+
63
+ For more details on this schema, see: https://girder.github.io/large_image/annotations.html
64
+
65
+ :param anns: One or more Histomics-formatted annotations
66
+ :type anns: Union[list,dict]
67
+ :param output_path: File path to save annotations to
68
+ :type output_path: str
69
+ """
70
+
71
+ with open(output_path, "w") as f:
72
+ json.dump(anns, f)
73
+ f.close()
74
+
75
+
76
+ @check_ext(["json", "geojson"])
77
+ @check_path
78
+ def write_geojson(anns: Union[list, dict], output_path: str):
79
+ """Write annotations to GeoJSON format
80
+
81
+ for more details on this schema, see: https://geojson.org
82
+
83
+ :param anns: One or more GeoJSON-formatted annotations
84
+ :type anns: Union[list,dict]
85
+ :param output_path: File path to save annotations to
86
+ :type output_path: str
87
+ """
88
+
89
+ with open(output_path, "w") as f:
90
+ json.dump(anns, f)
91
+ f.close()
92
+
93
+
94
+ @check_ext(["xml"])
95
+ @check_path
96
+ def write_aperio(anns: ET._ElementTree, output_path: str):
97
+ """Write annotations to Aperio ImageScope format
98
+
99
+ :param anns: Annotations in Aperio (XML) format
100
+ :type anns: ET._ElementTree
101
+ :param output_path: File path to save annotations to
102
+ :type output_path: str
103
+ """
104
+
105
+ xml_string = ET.tostring(anns, encoding="unicode", pretty_print=True)
106
+ with open(output_path, "w") as f:
107
+ f.write(xml_string)
108
+ f.close()