Rhapso 0.1.92__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 (101) hide show
  1. Rhapso/__init__.py +1 -0
  2. Rhapso/data_prep/__init__.py +2 -0
  3. Rhapso/data_prep/n5_reader.py +188 -0
  4. Rhapso/data_prep/s3_big_stitcher_reader.py +55 -0
  5. Rhapso/data_prep/xml_to_dataframe.py +215 -0
  6. Rhapso/detection/__init__.py +5 -0
  7. Rhapso/detection/advanced_refinement.py +203 -0
  8. Rhapso/detection/difference_of_gaussian.py +324 -0
  9. Rhapso/detection/image_reader.py +117 -0
  10. Rhapso/detection/metadata_builder.py +130 -0
  11. Rhapso/detection/overlap_detection.py +327 -0
  12. Rhapso/detection/points_validation.py +49 -0
  13. Rhapso/detection/save_interest_points.py +265 -0
  14. Rhapso/detection/view_transform_models.py +67 -0
  15. Rhapso/fusion/__init__.py +0 -0
  16. Rhapso/fusion/affine_fusion/__init__.py +2 -0
  17. Rhapso/fusion/affine_fusion/blend.py +289 -0
  18. Rhapso/fusion/affine_fusion/fusion.py +601 -0
  19. Rhapso/fusion/affine_fusion/geometry.py +159 -0
  20. Rhapso/fusion/affine_fusion/io.py +546 -0
  21. Rhapso/fusion/affine_fusion/script_utils.py +111 -0
  22. Rhapso/fusion/affine_fusion/setup.py +4 -0
  23. Rhapso/fusion/affine_fusion_worker.py +234 -0
  24. Rhapso/fusion/multiscale/__init__.py +0 -0
  25. Rhapso/fusion/multiscale/aind_hcr_data_transformation/__init__.py +19 -0
  26. Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/__init__.py +3 -0
  27. Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/czi_to_zarr.py +698 -0
  28. Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/zarr_writer.py +265 -0
  29. Rhapso/fusion/multiscale/aind_hcr_data_transformation/models.py +81 -0
  30. Rhapso/fusion/multiscale/aind_hcr_data_transformation/utils/__init__.py +3 -0
  31. Rhapso/fusion/multiscale/aind_hcr_data_transformation/utils/utils.py +526 -0
  32. Rhapso/fusion/multiscale/aind_hcr_data_transformation/zeiss_job.py +249 -0
  33. Rhapso/fusion/multiscale/aind_z1_radial_correction/__init__.py +21 -0
  34. Rhapso/fusion/multiscale/aind_z1_radial_correction/array_to_zarr.py +257 -0
  35. Rhapso/fusion/multiscale/aind_z1_radial_correction/radial_correction.py +557 -0
  36. Rhapso/fusion/multiscale/aind_z1_radial_correction/run_capsule.py +98 -0
  37. Rhapso/fusion/multiscale/aind_z1_radial_correction/utils/__init__.py +3 -0
  38. Rhapso/fusion/multiscale/aind_z1_radial_correction/utils/utils.py +266 -0
  39. Rhapso/fusion/multiscale/aind_z1_radial_correction/worker.py +89 -0
  40. Rhapso/fusion/multiscale_worker.py +113 -0
  41. Rhapso/fusion/neuroglancer_link_gen/__init__.py +8 -0
  42. Rhapso/fusion/neuroglancer_link_gen/dispim_link.py +235 -0
  43. Rhapso/fusion/neuroglancer_link_gen/exaspim_link.py +127 -0
  44. Rhapso/fusion/neuroglancer_link_gen/hcr_link.py +368 -0
  45. Rhapso/fusion/neuroglancer_link_gen/iSPIM_top.py +47 -0
  46. Rhapso/fusion/neuroglancer_link_gen/link_utils.py +239 -0
  47. Rhapso/fusion/neuroglancer_link_gen/main.py +299 -0
  48. Rhapso/fusion/neuroglancer_link_gen/ng_layer.py +1434 -0
  49. Rhapso/fusion/neuroglancer_link_gen/ng_state.py +1123 -0
  50. Rhapso/fusion/neuroglancer_link_gen/parsers.py +336 -0
  51. Rhapso/fusion/neuroglancer_link_gen/raw_link.py +116 -0
  52. Rhapso/fusion/neuroglancer_link_gen/utils/__init__.py +4 -0
  53. Rhapso/fusion/neuroglancer_link_gen/utils/shader_utils.py +85 -0
  54. Rhapso/fusion/neuroglancer_link_gen/utils/transfer.py +43 -0
  55. Rhapso/fusion/neuroglancer_link_gen/utils/utils.py +303 -0
  56. Rhapso/fusion/neuroglancer_link_gen_worker.py +30 -0
  57. Rhapso/matching/__init__.py +0 -0
  58. Rhapso/matching/load_and_transform_points.py +458 -0
  59. Rhapso/matching/ransac_matching.py +544 -0
  60. Rhapso/matching/save_matches.py +120 -0
  61. Rhapso/matching/xml_parser.py +302 -0
  62. Rhapso/pipelines/__init__.py +0 -0
  63. Rhapso/pipelines/ray/__init__.py +0 -0
  64. Rhapso/pipelines/ray/aws/__init__.py +0 -0
  65. Rhapso/pipelines/ray/aws/alignment_pipeline.py +227 -0
  66. Rhapso/pipelines/ray/aws/config/__init__.py +0 -0
  67. Rhapso/pipelines/ray/evaluation.py +71 -0
  68. Rhapso/pipelines/ray/interest_point_detection.py +137 -0
  69. Rhapso/pipelines/ray/interest_point_matching.py +110 -0
  70. Rhapso/pipelines/ray/local/__init__.py +0 -0
  71. Rhapso/pipelines/ray/local/alignment_pipeline.py +167 -0
  72. Rhapso/pipelines/ray/matching_stats.py +104 -0
  73. Rhapso/pipelines/ray/param/__init__.py +0 -0
  74. Rhapso/pipelines/ray/solver.py +120 -0
  75. Rhapso/pipelines/ray/split_dataset.py +78 -0
  76. Rhapso/solver/__init__.py +0 -0
  77. Rhapso/solver/compute_tiles.py +562 -0
  78. Rhapso/solver/concatenate_models.py +116 -0
  79. Rhapso/solver/connected_graphs.py +111 -0
  80. Rhapso/solver/data_prep.py +181 -0
  81. Rhapso/solver/global_optimization.py +410 -0
  82. Rhapso/solver/model_and_tile_setup.py +109 -0
  83. Rhapso/solver/pre_align_tiles.py +323 -0
  84. Rhapso/solver/save_results.py +97 -0
  85. Rhapso/solver/view_transforms.py +75 -0
  86. Rhapso/solver/xml_to_dataframe_solver.py +213 -0
  87. Rhapso/split_dataset/__init__.py +0 -0
  88. Rhapso/split_dataset/compute_grid_rules.py +78 -0
  89. Rhapso/split_dataset/save_points.py +101 -0
  90. Rhapso/split_dataset/save_xml.py +377 -0
  91. Rhapso/split_dataset/split_images.py +537 -0
  92. Rhapso/split_dataset/xml_to_dataframe_split.py +219 -0
  93. rhapso-0.1.92.dist-info/METADATA +39 -0
  94. rhapso-0.1.92.dist-info/RECORD +101 -0
  95. rhapso-0.1.92.dist-info/WHEEL +5 -0
  96. rhapso-0.1.92.dist-info/licenses/LICENSE +21 -0
  97. rhapso-0.1.92.dist-info/top_level.txt +2 -0
  98. tests/__init__.py +1 -0
  99. tests/test_detection.py +17 -0
  100. tests/test_matching.py +21 -0
  101. tests/test_solving.py +21 -0
@@ -0,0 +1,213 @@
1
+ import pandas as pd
2
+ import xml.etree.ElementTree as ET
3
+
4
+ """
5
+ XML to Dataframe Solver is a Solver specific XML parsing tool
6
+ """
7
+
8
+ class XMLToDataFrameSolver:
9
+ def __init__(self, xml_file):
10
+ self.xml_content = xml_file
11
+
12
+ def parse_image_loader_zarr(self, root):
13
+ """
14
+ Parses image loader configuration from a Zarr file's XML structure and constructs a DataFrame containing the
15
+ metadata for each image group.
16
+ """
17
+ image_loader_data = []
18
+
19
+ for il in root.findall(".//ImageLoader/zgroups/zgroup"):
20
+ view_setup = il.get("setup")
21
+ timepoint = il.get("timepoint")
22
+ file_path = il.find("path").text if il.find("path") is not None else None
23
+
24
+ channel = file_path.split("_ch_", 1)[1].split(".ome.zarr", 1)[0]
25
+
26
+ image_loader_data.append(
27
+ {
28
+ "view_setup": view_setup,
29
+ "timepoint": timepoint,
30
+ "series": 1,
31
+ "channel": channel,
32
+ "file_path": file_path,
33
+ }
34
+ )
35
+
36
+ return pd.DataFrame(image_loader_data)
37
+
38
+ def parse_image_loader_tiff(self, root):
39
+ """
40
+ Parses image loader configuration from a TIFF file's XML structure and constructs a DataFrame containing
41
+ metadata for each image group.
42
+ """
43
+ image_loader_data = []
44
+
45
+ # Ensure that file mappings are present in the XML
46
+ if not root.findall(".//ImageLoader/files/FileMapping"):
47
+ raise Exception("There are no files in this XML")
48
+
49
+ # Check for required labels in the XML
50
+ if not self.check_labels(root):
51
+ raise Exception("Required labels do not exist")
52
+
53
+ # Validate that the lengths of view setups, registrations, and tiles match
54
+ if not self.check_length(root):
55
+ raise Exception(
56
+ "The amount of view setups, view registrations, and tiles do not match"
57
+ )
58
+
59
+ # Iterate over each file mapping in the XML
60
+ for fm in root.findall(".//ImageLoader/files/FileMapping"):
61
+ view_setup = fm.get("view_setup")
62
+ timepoint = fm.get("timepoint")
63
+ series = fm.get("series")
64
+ channel = fm.get("channel")
65
+ file_path = fm.find("file").text if fm.find("file") is not None else None
66
+
67
+ image_loader_data.append(
68
+ {
69
+ "view_setup": view_setup,
70
+ "timepoint": timepoint,
71
+ "series": series,
72
+ "channel": channel,
73
+ "file_path": file_path,
74
+ }
75
+ )
76
+
77
+ # Convert the list to a DataFrame and return
78
+ return pd.DataFrame(image_loader_data)
79
+
80
+ def parse_image_loader_split_zarr(self):
81
+ pass
82
+
83
+ def route_image_loader(self, root):
84
+ """
85
+ Directs the XML parsing process based on the image loader format specified in the XML.
86
+ """
87
+ format_node = root.find(".//ImageLoader")
88
+ format_type = format_node.get("format")
89
+
90
+ if "filemap" in format_type:
91
+ return self.parse_image_loader_tiff(root)
92
+ else:
93
+ return self.parse_image_loader_zarr(root)
94
+
95
+ def parse_view_setups(self, root):
96
+ """
97
+ Parses the view setups from an XML structure and constructs a DataFrame containing metadata for each view setup.
98
+ """
99
+ viewsetups_data = []
100
+
101
+ for vs in root.findall(".//ViewSetup"):
102
+ id_ = vs.find("id").text
103
+ # name = vs.find("name").text
104
+ name = vs.findtext("name")
105
+ size = vs.find("size").text
106
+ voxel_unit = vs.find(".//voxelSize/unit").text
107
+ voxel_size = " ".join(vs.find(".//voxelSize/size").text.split())
108
+ attributes = {attr.tag: attr.text for attr in vs.find("attributes")}
109
+ viewsetups_data.append(
110
+ {
111
+ "id": id_,
112
+ "name": name,
113
+ "size": size,
114
+ "voxel_unit": voxel_unit,
115
+ "voxel_size": voxel_size,
116
+ **attributes,
117
+ }
118
+ )
119
+ return pd.DataFrame(viewsetups_data)
120
+
121
+ def parse_view_registrations(self, root):
122
+ """
123
+ Parses view registrations from an XML structure and constructs a DataFrame containing registration metadata
124
+ for each view.
125
+ """
126
+ viewregistrations_data = []
127
+ for vr in root.findall(".//ViewRegistration"):
128
+ timepoint = vr.get("timepoint")
129
+ setup = vr.get("setup")
130
+
131
+ for vt in vr.findall(".//ViewTransform"):
132
+ affine_text = (
133
+ vt.find("affine").text.replace("\n", "").replace(" ", ", ")
134
+ )
135
+ viewregistrations_data.append(
136
+ {
137
+ "timepoint": timepoint,
138
+ "setup": setup,
139
+ "type": vt.get("type"),
140
+ "name": vt.find("Name").text.strip(),
141
+ "affine": affine_text,
142
+ }
143
+ )
144
+ return pd.DataFrame(viewregistrations_data)
145
+
146
+ def parse_view_interest_points(self, root):
147
+ """
148
+ Parses interest points data from an XML structure and constructs a DataFrame containing metadata and paths
149
+ for each set of interest points.
150
+ """
151
+ view_interest_points_data = []
152
+
153
+ for vip in root.findall(".//ViewInterestPointsFile"):
154
+ timepoint = vip.get("timepoint")
155
+ setup = vip.get("setup")
156
+ label = vip.get("label")
157
+ params = vip.get("params")
158
+ path = vip.text.strip() if vip.text is not None else None
159
+ view_interest_points_data.append(
160
+ {
161
+ "timepoint": timepoint,
162
+ "setup": setup,
163
+ "label": label,
164
+ "params": params,
165
+ "path": path,
166
+ }
167
+ )
168
+ return pd.DataFrame(view_interest_points_data)
169
+
170
+ def check_labels(self, root):
171
+ """
172
+ Verifies the presence of required XML labels including bounding boxes, point spread functions,
173
+ stitching results, and intensity adjustments.
174
+ """
175
+ labels = True
176
+ if root.find(".//BoundingBoxes") is None:
177
+ labels = False
178
+ if root.find(".//PointSpreadFunctions") is None:
179
+ labels = False
180
+ if root.find(".//StitchingResults") is None:
181
+ labels = False
182
+ if root.find(".//IntensityAdjustments") is None:
183
+ labels = False
184
+
185
+ return labels
186
+
187
+ def check_length(self, root):
188
+ """
189
+ Validates that the count of elements within the XML structure aligns with expected relationships
190
+ between file mappings, view setups, and view registrations.
191
+ """
192
+ length = True
193
+ if len(root.findall(".//ImageLoader/files/FileMapping")) != len(root.findall(".//ViewRegistration")) or \
194
+ len(root.findall(".//ViewSetup")) != len(root.findall(".//ViewRegistration")) * (1 / 2):
195
+ length = False
196
+ return length
197
+
198
+ def run(self):
199
+ """
200
+ Executes the entry point of the script.
201
+ """
202
+ root = ET.fromstring(self.xml_content)
203
+ image_loader_df = self.route_image_loader(root)
204
+ view_setups_df = self.parse_view_setups(root)
205
+ view_registrations_df = self.parse_view_registrations(root)
206
+ view_interest_points_df = self.parse_view_interest_points(root)
207
+
208
+ return {
209
+ "image_loader": image_loader_df,
210
+ "view_setups": view_setups_df,
211
+ "view_registrations": view_registrations_df,
212
+ "view_interest_points": view_interest_points_df,
213
+ }
File without changes
@@ -0,0 +1,78 @@
1
+ import math
2
+
3
+ """
4
+ Compute Grid Rules calculates grid-safe values that align with the datasets coarsest resolution. It computes a minimal per-axis step size,
5
+ validates that resolutions are effectively integers, then rounds each target size/overlap up to the nearest multiple of that step.
6
+ """
7
+
8
+ class ComputeGridRules:
9
+ def __init__(self, data_global, target_image_size, target_overlap):
10
+ self.view_setups_df = data_global['view_setups']
11
+ self.target_image_size = target_image_size
12
+ self.target_overlap = target_overlap
13
+
14
+ def closest_larger_long_divisible_by(self, a, b):
15
+ """
16
+ Find the smallest integer ≥ a that is divisible by b
17
+ """
18
+ if b <= 0:
19
+ raise ValueError("b must be > 0")
20
+
21
+ if a == b or a == 0 or a % b == 0:
22
+ return int(a)
23
+
24
+ return int(a + b - (a % b))
25
+
26
+ def find_min_step_size(self):
27
+ """
28
+ Compute the minimal integer step size per axis (X,Y,Z) that is compatible with the chosen lowest resolution
29
+ """
30
+ lowest_resolution=(64.0, 64.0, 64.0)
31
+ min_step_size = [1, 1, 1]
32
+
33
+ for d, r in enumerate(lowest_resolution):
34
+ frac = abs(r % 1.0)
35
+
36
+ if frac > 1e-3 and (1.0 - frac) > 1e-3:
37
+ raise RuntimeError("Downsampling has a fraction > 0.001; cannot split dataset.")
38
+
39
+ min_step_size[d] = math.lcm(min_step_size[d], int(round(r)))
40
+
41
+ return min_step_size
42
+
43
+ def collect_image_sizes(self):
44
+ """
45
+ Tally how many times each raw size string appears in view setups and compute the per-axis minimum dimensions
46
+ across all rows
47
+ """
48
+ sizes = {}
49
+ min_size = None
50
+
51
+ for _, row in self.view_setups_df.iterrows():
52
+ dims = row['size']
53
+ sizes[dims] = sizes.get(dims, 0) + 1
54
+ dims = [int(x) for x in dims.split()]
55
+ if min_size is None:
56
+ min_size = dims[:]
57
+ else:
58
+ for d in range(len(dims)):
59
+ min_size[d] = min(min_size[d], dims[d])
60
+
61
+ return (sizes, min_size)
62
+
63
+ def run(self):
64
+ """
65
+ Executes the entry point of the script.
66
+ """
67
+ # image_sizes, min_size = self.collect_image_sizes()
68
+ min_step_size = self.find_min_step_size()
69
+
70
+ sx = self.closest_larger_long_divisible_by(self.target_image_size[0], min_step_size[0])
71
+ sy = self.closest_larger_long_divisible_by(self.target_image_size[1], min_step_size[1])
72
+ sz = self.closest_larger_long_divisible_by(self.target_image_size[2], min_step_size[2])
73
+
74
+ ox = self.closest_larger_long_divisible_by(self.target_overlap[0], min_step_size[0])
75
+ oy = self.closest_larger_long_divisible_by(self.target_overlap[1], min_step_size[1])
76
+ oz = self.closest_larger_long_divisible_by(self.target_overlap[2], min_step_size[2])
77
+
78
+ return (sx, sy, sz), (ox, oy, oz), min_step_size
@@ -0,0 +1,101 @@
1
+ import zarr
2
+ import s3fs
3
+ import numpy as np
4
+ import boto3
5
+ import json
6
+
7
+ class SavePoints:
8
+ def __init__(self, label_entries, n5_prefix):
9
+ self.label_entries = label_entries
10
+ self.n5_prefix = n5_prefix
11
+ self.s3_filesystem = s3fs.S3FileSystem()
12
+ self.default_block_size = 300000
13
+
14
+ def write_json_to_s3(self, id_dataset_path, loc_dataset_path, attributes):
15
+ """
16
+ Write attributes file into both the ID and LOC dataset directories on S3
17
+ """
18
+ bucket, key = id_dataset_path.replace("s3://", "", 1).split("/", 1)
19
+ json_path = key + '/attributes.json'
20
+ json_bytes = json.dumps(attributes).encode('utf-8')
21
+ s3 = boto3.client('s3')
22
+ s3.put_object(Bucket=bucket, Key=json_path, Body=json_bytes)
23
+
24
+ bucket, key = loc_dataset_path.replace("s3://", "", 1).split("/", 1)
25
+ json_path = key + '/attributes.json'
26
+ json_bytes = json.dumps(attributes).encode('utf-8')
27
+ s3 = boto3.client('s3')
28
+ s3.put_object(Bucket=bucket, Key=json_path, Body=json_bytes)
29
+
30
+ def save_interest_points_to_n5(self):
31
+ for label_entry in self.label_entries:
32
+ n5_path = label_entry['ip_list']['n5_path']
33
+
34
+ if self.n5_prefix.startswith("s3://"):
35
+ output_path = self.n5_prefix + n5_path + "/interestpoints"
36
+ store = s3fs.S3Map(root=output_path, s3=self.s3_filesystem, check=False)
37
+ root = zarr.group(store=store, overwrite=False)
38
+ else:
39
+ output_path = self.n5_prefix + n5_path + "/interestpoints"
40
+ store = zarr.N5Store(output_path)
41
+ root = zarr.group(store, overwrite=False)
42
+
43
+ id_dataset = "id"
44
+ loc_dataset = "loc"
45
+
46
+ if self.n5_prefix.startswith("s3://"):
47
+ id_path = f"{output_path}/id"
48
+ loc_path = f"{output_path}/loc"
49
+ attrs_dict = dict(root.attrs)
50
+ self.write_json_to_s3(id_path, loc_path, attrs_dict)
51
+
52
+ interest_points = [point[1] for point in label_entry['ip_list']['interest_points']]
53
+ interest_point_ids = np.arange(len(interest_points), dtype=np.uint64).reshape(-1, 1)
54
+ n = 3
55
+
56
+ if len(interest_points) > 0:
57
+ if id_dataset in root:
58
+ del root[id_dataset]
59
+ root.create_dataset(
60
+ id_dataset,
61
+ data=interest_point_ids,
62
+ dtype='u8',
63
+ chunks=(self.default_block_size,),
64
+ compressor=zarr.GZip()
65
+ )
66
+
67
+ if loc_dataset in root:
68
+ del root[loc_dataset]
69
+ root.create_dataset(
70
+ loc_dataset,
71
+ data=interest_points,
72
+ dtype='f8',
73
+ chunks=(self.default_block_size, n),
74
+ compressor=zarr.GZip()
75
+ )
76
+
77
+ # save as empty lists
78
+ else:
79
+ if id_dataset in root:
80
+ del root[id_dataset]
81
+ root.create_dataset(
82
+ id_dataset,
83
+ shape=(0,),
84
+ dtype='u8',
85
+ chunks=(1,),
86
+ compressor=zarr.GZip()
87
+ )
88
+
89
+ if loc_dataset in root:
90
+ del root[loc_dataset]
91
+ root.create_dataset(
92
+ loc_dataset,
93
+ shape=(0,),
94
+ dtype='f8',
95
+ chunks=(1,),
96
+ compressor=zarr.GZip()
97
+ )
98
+
99
+ def run(self):
100
+ self.save_interest_points_to_n5()
101
+ return 1