Rhapso 0.1.991__tar.gz → 0.1.993__tar.gz

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 (108) hide show
  1. {rhapso-0.1.991 → rhapso-0.1.993}/PKG-INFO +16 -18
  2. {rhapso-0.1.991 → rhapso-0.1.993}/README.md +13 -12
  3. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/matching/ransac_matching.py +32 -36
  4. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/aws/alignment_pipeline.py +17 -14
  5. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/interest_point_matching.py +9 -9
  6. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/local/alignment_pipeline.py +20 -17
  7. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/solver.py +7 -3
  8. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/global_optimization.py +5 -5
  9. rhapso-0.1.993/Rhapso/split_dataset/save_xml.py +774 -0
  10. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso.egg-info/PKG-INFO +16 -18
  11. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso.egg-info/requires.txt +1 -0
  12. {rhapso-0.1.991 → rhapso-0.1.993}/setup.py +6 -7
  13. rhapso-0.1.991/Rhapso/split_dataset/save_xml.py +0 -377
  14. {rhapso-0.1.991 → rhapso-0.1.993}/LICENSE +0 -0
  15. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/__init__.py +0 -0
  16. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/data_prep/__init__.py +0 -0
  17. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/data_prep/n5_reader.py +0 -0
  18. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/data_prep/s3_big_stitcher_reader.py +0 -0
  19. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/data_prep/xml_to_dataframe.py +0 -0
  20. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/__init__.py +0 -0
  21. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/advanced_refinement.py +0 -0
  22. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/difference_of_gaussian.py +0 -0
  23. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/image_reader.py +0 -0
  24. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/metadata_builder.py +0 -0
  25. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/overlap_detection.py +0 -0
  26. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/points_validation.py +0 -0
  27. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/save_interest_points.py +0 -0
  28. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/detection/view_transform_models.py +0 -0
  29. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/__init__.py +0 -0
  30. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/__init__.py +0 -0
  31. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/blend.py +0 -0
  32. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/fusion.py +0 -0
  33. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/geometry.py +0 -0
  34. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/io.py +0 -0
  35. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/script_utils.py +0 -0
  36. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion/setup.py +0 -0
  37. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/affine_fusion_worker.py +0 -0
  38. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/__init__.py +0 -0
  39. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/__init__.py +0 -0
  40. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/__init__.py +0 -0
  41. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/czi_to_zarr.py +0 -0
  42. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/compress/zarr_writer.py +0 -0
  43. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/models.py +0 -0
  44. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/utils/__init__.py +0 -0
  45. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/utils/utils.py +0 -0
  46. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_hcr_data_transformation/zeiss_job.py +0 -0
  47. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/__init__.py +0 -0
  48. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/array_to_zarr.py +0 -0
  49. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/radial_correction.py +0 -0
  50. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/run_capsule.py +0 -0
  51. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/utils/__init__.py +0 -0
  52. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/utils/utils.py +0 -0
  53. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale/aind_z1_radial_correction/worker.py +0 -0
  54. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/multiscale_worker.py +0 -0
  55. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/__init__.py +0 -0
  56. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/dispim_link.py +0 -0
  57. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/exaspim_link.py +0 -0
  58. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/hcr_link.py +0 -0
  59. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/iSPIM_top.py +0 -0
  60. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/link_utils.py +0 -0
  61. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/main.py +0 -0
  62. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/ng_layer.py +0 -0
  63. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/ng_state.py +0 -0
  64. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/parsers.py +0 -0
  65. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/raw_link.py +0 -0
  66. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/utils/__init__.py +0 -0
  67. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/utils/shader_utils.py +0 -0
  68. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/utils/transfer.py +0 -0
  69. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen/utils/utils.py +0 -0
  70. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/fusion/neuroglancer_link_gen_worker.py +0 -0
  71. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/matching/__init__.py +0 -0
  72. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/matching/load_and_transform_points.py +0 -0
  73. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/matching/save_matches.py +0 -0
  74. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/matching/xml_parser.py +0 -0
  75. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/__init__.py +0 -0
  76. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/__init__.py +0 -0
  77. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/aws/__init__.py +0 -0
  78. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/aws/config/__init__.py +0 -0
  79. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/evaluation.py +0 -0
  80. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/interest_point_detection.py +0 -0
  81. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/local/__init__.py +0 -0
  82. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/matching_stats.py +0 -0
  83. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/param/__init__.py +0 -0
  84. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/pipelines/ray/split_dataset.py +0 -0
  85. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/__init__.py +0 -0
  86. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/compute_tiles.py +0 -0
  87. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/concatenate_models.py +0 -0
  88. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/connected_graphs.py +0 -0
  89. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/data_prep.py +0 -0
  90. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/model_and_tile_setup.py +0 -0
  91. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/pre_align_tiles.py +0 -0
  92. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/save_results.py +0 -0
  93. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/view_transforms.py +0 -0
  94. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/solver/xml_to_dataframe_solver.py +0 -0
  95. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/split_dataset/__init__.py +0 -0
  96. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/split_dataset/compute_grid_rules.py +0 -0
  97. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/split_dataset/save_points.py +0 -0
  98. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/split_dataset/split_images.py +0 -0
  99. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso/split_dataset/xml_to_dataframe_split.py +0 -0
  100. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso.egg-info/SOURCES.txt +0 -0
  101. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso.egg-info/dependency_links.txt +0 -0
  102. {rhapso-0.1.991 → rhapso-0.1.993}/Rhapso.egg-info/top_level.txt +0 -0
  103. {rhapso-0.1.991 → rhapso-0.1.993}/pyproject.toml +0 -0
  104. {rhapso-0.1.991 → rhapso-0.1.993}/setup.cfg +0 -0
  105. {rhapso-0.1.991 → rhapso-0.1.993}/tests/__init__.py +0 -0
  106. {rhapso-0.1.991 → rhapso-0.1.993}/tests/test_detection.py +0 -0
  107. {rhapso-0.1.991 → rhapso-0.1.993}/tests/test_matching.py +0 -0
  108. {rhapso-0.1.991 → rhapso-0.1.993}/tests/test_solving.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Rhapso
3
- Version: 0.1.991
3
+ Version: 0.1.993
4
4
  Summary: A python package for aligning and stitching light sheet fluorescence microscopy images
5
5
  Author: ND
6
6
  Author-email: sean.fite@alleninstitute.org
@@ -9,13 +9,9 @@ Project-URL: Roadmap, https://github.com/AllenNeuralDynamics/Rhapso/issues
9
9
  Classifier: Development Status :: 3 - Alpha
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Natural Language :: English
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.7
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
12
  Classifier: Programming Language :: Python :: 3.10
17
13
  Classifier: Operating System :: OS Independent
18
- Requires-Python: >=3.7
14
+ Requires-Python: >=3.10
19
15
  Description-Content-Type: text/markdown
20
16
  License-File: LICENSE
21
17
  Requires-Dist: pandas
@@ -33,6 +29,7 @@ Requires-Dist: matplotlib==3.10.0
33
29
  Requires-Dist: memory-profiler==0.61.0
34
30
  Requires-Dist: s3fs==2024.12.0
35
31
  Requires-Dist: scikit-learn
32
+ Requires-Dist: click==8.2.1
36
33
  Dynamic: author
37
34
  Dynamic: author-email
38
35
  Dynamic: classifier
@@ -414,25 +411,26 @@ python Rhapso/pipelines/ray/aws/alignment_pipeline.py
414
411
  | Parameter | Feature / step | What it does | Typical range |
415
412
  | :---------------------------- | :------------------- | :---------------------------------------------------------------- | :------------- |
416
413
  | `model_min_matches` | RANSAC | Minimum correspondences to estimate a rigid transform | 18 – 32 |
417
- | `inlier_factor` | RANSAC | Inlier tolerance scaling; larger = looser inlier threshold | 30 – 100 |
418
- | `lambda_value` | RANSAC | Regularization strength during model fitting | 0.1 – 0.05 |
414
+ | `inlier_threshold` | RANSAC | Inlier tolerance scaling; larger = looser inlier threshold | 50 – 100 |
415
+ | `min_inlier_ratio` | RANSAC | Regularization strength during model fitting | 0.1 – 0.05 |
419
416
  | `num_iterations` | RANSAC | Number of RANSAC trials; higher = more robust, slower | 10,0000 |
420
- | `regularization_weight` | RANSAC | Weight applied to the regularization term | 1.0 |
417
+ | `regularization_weight` | RANSAC | Weight applied to the regularization term | 0.05 - 1.0 |
421
418
 
422
419
  ```
423
420
  <br>
424
421
 
425
422
  ### Solver
426
423
  ```
427
- | Parameter | Feature / step | What it does | Typical range |
428
- | :------------------- | :------------- | :----------------------------------------------------------------- | :------------------ |
429
- | `relative_threshold` | Graph pruning | Reject edges with residuals above dataset-relative cutoff | 3.5 |
430
- | `absolute_threshold` | Graph pruning | Reject edges above an absolute error bound (detection-space units) | 7.0 |
431
- | `min_matches` | Graph pruning | Minimum matches required to retain an edge between tiles | 3 |
432
- | `damp` | Optimization | Damping for iterative solver; higher can stabilize tough cases | 1.0 |
433
- | `max_iterations` | Optimization | Upper bound on solver iterations | 10,0000 |
434
- | `max_allowed_error` | Optimization | Overall error cap; `inf` disables hard stop by error | `inf` |
435
- | `max_plateauwidth` | Early stopping | Stagnation window before stopping on no improvement | 200 |
424
+ | Parameter | Feature / step | What it does | Typical range |
425
+ | :----------------------- | :------------- | :----------------------------------------------------------------- | :------------------ |
426
+ | `relative_threshold` | Graph pruning | Reject edges with residuals above dataset-relative cutoff | 3.5 |
427
+ | `absolute_threshold` | Graph pruning | Reject edges above an absolute error bound (detection-space units) | 7.0 |
428
+ | `min_matches` | Graph pruning | Minimum matches required to retain an edge between tiles | 3 |
429
+ | `damp` | Optimization | Damping for iterative solver; higher can stabilize tough cases | 1.0 |
430
+ | `max_iterations` | Optimization | Upper bound on solver iterations | 10,0000 |
431
+ | `max_allowed_error` | Optimization | Overall error cap; `inf` disables hard stop by error | `inf` |
432
+ | `max_plateauwidth` | Early stopping | Stagnation window before stopping on no improvement | 200 |
433
+ | `regularization_weight` | RANSAC | Weight applied to the regularization term | 0.05 - 1.0 |
436
434
 
437
435
  ```
438
436
 
@@ -368,25 +368,26 @@ python Rhapso/pipelines/ray/aws/alignment_pipeline.py
368
368
  | Parameter | Feature / step | What it does | Typical range |
369
369
  | :---------------------------- | :------------------- | :---------------------------------------------------------------- | :------------- |
370
370
  | `model_min_matches` | RANSAC | Minimum correspondences to estimate a rigid transform | 18 – 32 |
371
- | `inlier_factor` | RANSAC | Inlier tolerance scaling; larger = looser inlier threshold | 30 – 100 |
372
- | `lambda_value` | RANSAC | Regularization strength during model fitting | 0.1 – 0.05 |
371
+ | `inlier_threshold` | RANSAC | Inlier tolerance scaling; larger = looser inlier threshold | 50 – 100 |
372
+ | `min_inlier_ratio` | RANSAC | Regularization strength during model fitting | 0.1 – 0.05 |
373
373
  | `num_iterations` | RANSAC | Number of RANSAC trials; higher = more robust, slower | 10,0000 |
374
- | `regularization_weight` | RANSAC | Weight applied to the regularization term | 1.0 |
374
+ | `regularization_weight` | RANSAC | Weight applied to the regularization term | 0.05 - 1.0 |
375
375
 
376
376
  ```
377
377
  <br>
378
378
 
379
379
  ### Solver
380
380
  ```
381
- | Parameter | Feature / step | What it does | Typical range |
382
- | :------------------- | :------------- | :----------------------------------------------------------------- | :------------------ |
383
- | `relative_threshold` | Graph pruning | Reject edges with residuals above dataset-relative cutoff | 3.5 |
384
- | `absolute_threshold` | Graph pruning | Reject edges above an absolute error bound (detection-space units) | 7.0 |
385
- | `min_matches` | Graph pruning | Minimum matches required to retain an edge between tiles | 3 |
386
- | `damp` | Optimization | Damping for iterative solver; higher can stabilize tough cases | 1.0 |
387
- | `max_iterations` | Optimization | Upper bound on solver iterations | 10,0000 |
388
- | `max_allowed_error` | Optimization | Overall error cap; `inf` disables hard stop by error | `inf` |
389
- | `max_plateauwidth` | Early stopping | Stagnation window before stopping on no improvement | 200 |
381
+ | Parameter | Feature / step | What it does | Typical range |
382
+ | :----------------------- | :------------- | :----------------------------------------------------------------- | :------------------ |
383
+ | `relative_threshold` | Graph pruning | Reject edges with residuals above dataset-relative cutoff | 3.5 |
384
+ | `absolute_threshold` | Graph pruning | Reject edges above an absolute error bound (detection-space units) | 7.0 |
385
+ | `min_matches` | Graph pruning | Minimum matches required to retain an edge between tiles | 3 |
386
+ | `damp` | Optimization | Damping for iterative solver; higher can stabilize tough cases | 1.0 |
387
+ | `max_iterations` | Optimization | Upper bound on solver iterations | 10,0000 |
388
+ | `max_allowed_error` | Optimization | Overall error cap; `inf` disables hard stop by error | `inf` |
389
+ | `max_plateauwidth` | Early stopping | Stagnation window before stopping on no improvement | 200 |
390
+ | `regularization_weight` | RANSAC | Weight applied to the regularization term | 0.05 - 1.0 |
390
391
 
391
392
  ```
392
393
 
@@ -27,7 +27,7 @@ class CustomBioImage(BioImage):
27
27
 
28
28
  class RansacMatching:
29
29
  def __init__(self, data_global, num_neighbors, redundancy, significance, num_required_neighbors, match_type,
30
- max_epsilon, min_inlier_ratio, num_iterations, model_min_matches, regularization_weight,
30
+ inlier_threshold, min_inlier_ratio, num_iterations, model_min_matches, regularization_weight,
31
31
  search_radius, view_registrations, input_type, image_file_prefix):
32
32
  self.data_global = data_global
33
33
  self.num_neighbors = num_neighbors
@@ -35,7 +35,7 @@ class RansacMatching:
35
35
  self.significance = significance
36
36
  self.num_required_neighbors = num_required_neighbors
37
37
  self.match_type = match_type
38
- self.max_epsilon = max_epsilon
38
+ self.inlier_threshold = inlier_threshold
39
39
  self.min_inlier_ratio = min_inlier_ratio
40
40
  self.num_iterations = num_iterations
41
41
  self.model_min_matches = model_min_matches
@@ -66,7 +66,7 @@ class RansacMatching:
66
66
  errors = []
67
67
  for match in temp:
68
68
  p1 = np.array(match[1])
69
- p2 = np.array(match[4])
69
+ p2 = np.array(match[5])
70
70
  p1_h = np.append(p1, 1.0)
71
71
  p1_trans = model_copy @ p1_h
72
72
  error = np.linalg.norm(p1_trans[:3] - p2)
@@ -137,60 +137,55 @@ class RansacMatching:
137
137
  rigid_matrix[:3, 3] = t
138
138
 
139
139
  return rigid_matrix
140
-
140
+
141
141
  def fit_affine_model(self, matches):
142
- matches = np.array(matches) # shape (N, 2, 3)
143
- P = matches[:, 0] # source points
144
- Q = matches[:, 1] # target points
145
- weights = np.ones(P.shape[0]) # uniform weights
142
+ """
143
+ Fit a 3x4 affine transform such that:
144
+ Q M @ P + t
145
+ where P, Q are 3D column vectors (but stored here as row vectors).
146
+ """
147
+ matches = np.asarray(matches) # shape (N, 2, 3)
148
+ P = matches[:, 0] # source points, shape (N, 3)
149
+ Q = matches[:, 1] # target points, shape (N, 3)
146
150
 
147
- ws = np.sum(weights)
151
+ # Uniform weights for now (kept in case you add non-uniform later)
152
+ weights = np.ones(P.shape[0], dtype=float)
148
153
 
154
+ # Weighted centroids
149
155
  pc = np.average(P, axis=0, weights=weights)
150
156
  qc = np.average(Q, axis=0, weights=weights)
151
157
 
158
+ # Centered coordinates
152
159
  P_centered = P - pc
153
160
  Q_centered = Q - qc
154
161
 
155
- A = np.zeros((3, 3))
156
- B = np.zeros((3, 3))
157
-
158
- for i in range(P.shape[0]):
159
- w = weights[i]
160
- p = P_centered[i]
161
- q = Q_centered[i]
162
-
163
- A += w * np.outer(p, p)
164
- B += w * np.outer(p, q)
162
+ # Weighted least squares: scale rows by sqrt(weight)
163
+ sqrt_w = np.sqrt(weights)[:, None] # (N, 1)
164
+ P_w = P_centered * sqrt_w # (N, 3)
165
+ Q_w = Q_centered * sqrt_w # (N, 3)
165
166
 
166
- det = np.linalg.det(A)
167
- if det == 0:
168
- raise ValueError("Ill-defined data points (det=0)")
167
+ # Solve P_w @ M^T ≈ Q_w → M_T is 3x3, then transpose
168
+ M_T, *_ = np.linalg.lstsq(P_w, Q_w, rcond=None)
169
+ M = M_T.T
169
170
 
170
- try:
171
- A_inv = np.linalg.inv(A)
172
- except np.linalg.LinAlgError:
173
- # If A is not invertible, use the pseudo-inverse
174
- A_inv = np.linalg.pinv(A)
171
+ # Translation so that M @ pc ≈ qc
172
+ t = qc - M @ pc
175
173
 
176
- M = A_inv @ B # 3x3 transformation matrix
177
-
178
- t = qc - M @ pc # translation
179
-
180
- affine_matrix = np.eye(4)
174
+ # Pack into 4x4 affine matrix
175
+ affine_matrix = np.eye(4, dtype=float)
181
176
  affine_matrix[:3, :3] = M
182
177
  affine_matrix[:3, 3] = t
183
178
 
184
179
  return affine_matrix
185
180
 
186
- def test(self, candidates, model, max_epsilon, min_inlier_ratio, min_num_inliers):
181
+ def test(self, candidates, model, inlier_threshold, min_inlier_ratio, min_num_inliers):
187
182
  inliers = []
188
183
  for idxA, pointA, view_a, label_a, idxB, pointB, view_b, label_b in candidates:
189
184
  p1_hom = np.append(pointA, 1.0)
190
185
  transformed = model @ p1_hom
191
186
  distance = np.linalg.norm(transformed[:3] - pointB)
192
187
 
193
- if distance < max_epsilon:
188
+ if distance < inlier_threshold:
194
189
  inliers.append((idxA, pointA, view_a, label_a, idxB, pointB, view_b, label_b))
195
190
 
196
191
  ir = len(inliers) / len(candidates)
@@ -251,15 +246,16 @@ class RansacMatching:
251
246
  regularized_model = self.model_regularization(point_pairs)
252
247
  except Exception as e:
253
248
  print(e)
249
+ continue
254
250
 
255
251
  num_inliers = 0
256
- is_good, tmp_inliers = self.test(candidates, regularized_model, self.max_epsilon, self.min_inlier_ratio, self.model_min_matches)
252
+ is_good, tmp_inliers = self.test(candidates, regularized_model, self.inlier_threshold, self.min_inlier_ratio, self.model_min_matches)
257
253
 
258
254
  while is_good and num_inliers < len(tmp_inliers):
259
255
  num_inliers = len(tmp_inliers)
260
256
  point_pairs = [(i[1], i[5]) for i in tmp_inliers]
261
257
  regularized_model = self.model_regularization(point_pairs)
262
- is_good, tmp_inliers = self.test(candidates, regularized_model, self.max_epsilon, self.min_inlier_ratio, self.model_min_matches)
258
+ is_good, tmp_inliers = self.test(candidates, regularized_model, self.inlier_threshold, self.min_inlier_ratio, self.model_min_matches)
263
259
 
264
260
  if len(tmp_inliers) > max_inliers:
265
261
  best_inliers = tmp_inliers
@@ -5,7 +5,7 @@ import json
5
5
  import base64, json
6
6
  from pathlib import Path
7
7
 
8
- with open("Rhapso/pipelines/ray/param/dev/zarr_s3_sean.yml", "r") as file:
8
+ with open("Rhapso/pipelines/ray/param/exaSPIM_802450.yml", "r") as file:
9
9
  config = yaml.safe_load(file)
10
10
 
11
11
  serialized_config = base64.b64encode(json.dumps(config).encode()).decode()
@@ -51,10 +51,10 @@ matching_cmd_rigid = (
51
51
  " search_radius=cfg[\\\"search_radius_rigid\\\"],\n"
52
52
  " num_required_neighbors=cfg[\\\"num_required_neighbors_rigid\\\"],\n"
53
53
  " model_min_matches=cfg[\\\"model_min_matches_rigid\\\"],\n"
54
- " inlier_factor=cfg[\\\"inlier_factor_rigid\\\"],\n"
55
- " lambda_value=cfg[\\\"lambda_value_rigid\\\"],\n"
54
+ " inlier_threshold=cfg[\\\"inlier_threshold_rigid\\\"],\n"
55
+ " min_inlier_ratio=cfg[\\\"min_inlier_ratio_rigid\\\"],\n"
56
56
  " num_iterations=cfg[\\\"num_iterations_rigid\\\"],\n"
57
- " regularization_weight=cfg[\\\"regularization_weight_rigid\\\"],\n"
57
+ " regularization_weight=cfg[\\\"regularization_weight_matching_rigid\\\"],\n"
58
58
  " image_file_prefix=cfg[\\\"image_file_prefix\\\"]\n"
59
59
  ")\n"
60
60
  "ipm.run()\n"
@@ -80,10 +80,10 @@ matching_cmd_affine = (
80
80
  " search_radius=cfg[\\\"search_radius_affine\\\"],\n"
81
81
  " num_required_neighbors=cfg[\\\"num_required_neighbors_affine\\\"],\n"
82
82
  " model_min_matches=cfg[\\\"model_min_matches_affine\\\"],\n"
83
- " inlier_factor=cfg[\\\"inlier_factor_affine\\\"],\n"
84
- " lambda_value=cfg[\\\"lambda_value_affine\\\"],\n"
83
+ " inlier_threshold=cfg[\\\"inlier_threshold_affine\\\"],\n"
84
+ " min_inlier_ratio=cfg[\\\"min_inlier_ratio_affine\\\"],\n"
85
85
  " num_iterations=cfg[\\\"num_iterations_affine\\\"],\n"
86
- " regularization_weight=cfg[\\\"regularization_weight_affine\\\"],\n"
86
+ " regularization_weight=cfg[\\\"regularization_weight_matching_affine\\\"],\n"
87
87
  " image_file_prefix=cfg[\\\"image_file_prefix\\\"]\n"
88
88
  ")\n"
89
89
  "ipm.run()\n"
@@ -109,10 +109,10 @@ matching_cmd_split_affine = (
109
109
  " search_radius=cfg[\\\"search_radius_split_affine\\\"],\n"
110
110
  " num_required_neighbors=cfg[\\\"num_required_neighbors_split_affine\\\"],\n"
111
111
  " model_min_matches=cfg[\\\"model_min_matches_split_affine\\\"],\n"
112
- " inlier_factor=cfg[\\\"inlier_factor_split_affine\\\"],\n"
113
- " lambda_value=cfg[\\\"lambda_value_split_affine\\\"],\n"
112
+ " inlier_threshold=cfg[\\\"inlier_threshold_split_affine\\\"],\n"
113
+ " min_inlier_ratio=cfg[\\\"min_inlier_ratio_split_affine\\\"],\n"
114
114
  " num_iterations=cfg[\\\"num_iterations_split_affine\\\"],\n"
115
- " regularization_weight=cfg[\\\"regularization_weight_split_affine\\\"],\n"
115
+ " regularization_weight=cfg[\\\"regularization_weight_matching_split_affine\\\"],\n"
116
116
  " image_file_prefix=cfg[\\\"image_file_prefix\\\"]\n"
117
117
  ")\n"
118
118
  "ipm.run()\n"
@@ -151,6 +151,7 @@ solver_rigid = Solver(
151
151
  absolute_threshold=config['absolute_threshold'],
152
152
  min_matches=config['min_matches'],
153
153
  damp=config['damp'],
154
+ regularization_weight=config['regularization_weight_solver_rigid'],
154
155
  max_iterations=config['max_iterations'],
155
156
  max_allowed_error=config['max_allowed_error'],
156
157
  max_plateauwidth=config['max_plateauwidth'],
@@ -168,6 +169,7 @@ solver_affine = Solver(
168
169
  absolute_threshold=config['absolute_threshold'],
169
170
  min_matches=config['min_matches'],
170
171
  damp=config['damp'],
172
+ regularization_weight=config['regularization_weight_solver_affine'],
171
173
  max_iterations=config['max_iterations'],
172
174
  max_allowed_error=config['max_allowed_error'],
173
175
  max_plateauwidth=config['max_plateauwidth'],
@@ -185,6 +187,7 @@ solver_split_affine = Solver(
185
187
  absolute_threshold=config['absolute_threshold'],
186
188
  min_matches=config['min_matches'],
187
189
  damp=config['damp'],
190
+ regularization_weight=config['regularization_weight_solver_split_affine'],
188
191
  max_iterations=config['max_iterations'],
189
192
  max_allowed_error=config['max_allowed_error'],
190
193
  max_plateauwidth=config['max_plateauwidth'],
@@ -210,10 +213,10 @@ try:
210
213
  solver_rigid.run()
211
214
  exec_on_cluster("Matching (affine)", unified_yml, matching_cmd_affine, prefix)
212
215
  solver_affine.run()
213
- exec_on_cluster("Split Dataset", unified_yml, split_cmd, prefix)
214
- exec_on_cluster("Matching (split_affine)", unified_yml, matching_cmd_split_affine, prefix)
215
- solver_split_affine.run()
216
- print("\n✅ Pipeline complete.")
216
+ # exec_on_cluster("Split Dataset", unified_yml, split_cmd, prefix)
217
+ # exec_on_cluster("Matching (split_affine)", unified_yml, matching_cmd_split_affine, prefix)
218
+ # solver_split_affine.run()
219
+ # print("\n✅ Pipeline complete.")
217
220
 
218
221
  except subprocess.CalledProcessError as e:
219
222
  print(f"❌ Pipeline error: {e}")
@@ -6,7 +6,7 @@ import ray
6
6
 
7
7
  class InterestPointMatching:
8
8
  def __init__(self, xml_input_path, n5_output_path, input_type, match_type, num_neighbors, redundancy, significance,
9
- search_radius, num_required_neighbors, model_min_matches, inlier_factor, lambda_value, num_iterations,
9
+ search_radius, num_required_neighbors, model_min_matches, inlier_threshold, min_inlier_ratio, num_iterations,
10
10
  regularization_weight, image_file_prefix):
11
11
  self.xml_input_path = xml_input_path
12
12
  self.n5_output_path = n5_output_path
@@ -18,8 +18,8 @@ class InterestPointMatching:
18
18
  self.search_radius = search_radius
19
19
  self.num_required_neighbors = num_required_neighbors
20
20
  self.model_min_matches = model_min_matches
21
- self.inlier_factor = inlier_factor
22
- self.lambda_value = lambda_value
21
+ self.inlier_threshold = inlier_threshold
22
+ self.min_inlier_ratio = min_inlier_ratio
23
23
  self.num_iterations = num_iterations
24
24
  self.regularization_weight = regularization_weight
25
25
  self.image_file_prefix = image_file_prefix
@@ -38,11 +38,11 @@ class InterestPointMatching:
38
38
  # Distribute interest point matching with Ray
39
39
  @ray.remote
40
40
  def match_pair(pointsA, pointsB, viewA_str, viewB_str, label, num_neighbors, redundancy, significance, num_required_neighbors,
41
- match_type, inlier_factor, lambda_value, num_iterations, model_min_matches, regularization_weight, search_radius,
41
+ match_type, inlier_threshold, min_inlier_ratio, num_iterations, model_min_matches, regularization_weight, search_radius,
42
42
  view_registrations, input_type, image_file_prefix):
43
43
 
44
- matcher = RansacMatching(data_global, num_neighbors, redundancy, significance, num_required_neighbors, match_type, inlier_factor,
45
- lambda_value, num_iterations, model_min_matches, regularization_weight, search_radius, view_registrations,
44
+ matcher = RansacMatching(data_global, num_neighbors, redundancy, significance, num_required_neighbors, match_type, inlier_threshold,
45
+ min_inlier_ratio, num_iterations, model_min_matches, regularization_weight, search_radius, view_registrations,
46
46
  input_type, image_file_prefix)
47
47
 
48
48
  pointsA, pointsB = matcher.filter_for_overlapping_points(pointsA, pointsB, viewA_str, viewB_str)
@@ -65,7 +65,7 @@ class InterestPointMatching:
65
65
  # --- Distribute ---
66
66
  futures = [
67
67
  match_pair.remote(pointsA, pointsB, viewA_str, viewB_str, label, self.num_neighbors, self.redundancy, self.significance, self.num_required_neighbors,
68
- self.match_type, self.inlier_factor, self.lambda_value, self.num_iterations, self.model_min_matches, self.regularization_weight,
68
+ self.match_type, self.inlier_threshold, self.min_inlier_ratio, self.num_iterations, self.model_min_matches, self.regularization_weight,
69
69
  self.search_radius, view_registrations, self.input_type, self.image_file_prefix)
70
70
  for pointsA, pointsB, viewA_str, viewB_str, label in process_pairs
71
71
  ]
@@ -88,8 +88,8 @@ class InterestPointMatching:
88
88
  # DEBUG MATCHING
89
89
  # all_results = []
90
90
  # for pointsA, pointsB, viewA_str, viewB_str, label in process_pairs:
91
- # matcher = RansacMatching(data_global, self.num_neighbors, self.redundancy, self.significance, self.num_required_neighbors, self.match_type, self.inlier_factor,
92
- # self.lambda_value, self.num_iterations, self.model_min_matches, self.regularization_weight, self.search_radius, view_registrations,
91
+ # matcher = RansacMatching(data_global, self.num_neighbors, self.redundancy, self.significance, self.num_required_neighbors, self.match_type, self.inlier_threshold,
92
+ # self.min_inlier_ratio, self.num_iterations, self.model_min_matches, self.regularization_weight, self.search_radius, view_registrations,
93
93
  # self.input_type, self.image_file_prefix)
94
94
 
95
95
  # pointsA, pointsB = matcher.filter_for_overlapping_points(pointsA, pointsB, viewA_str, viewB_str)
@@ -9,7 +9,7 @@ import ray
9
9
  ray.init()
10
10
 
11
11
  # Point to param file
12
- with open("Rhapso/pipelines/ray/param/dev/zarr_s3_sean.yml", "r") as file:
12
+ with open("Rhapso/pipelines/ray/param/exaSPIM_802450.yml", "r") as file:
13
13
  config = yaml.safe_load(file)
14
14
 
15
15
  # -- INITIALIZE EACH COMPONENT --
@@ -46,10 +46,10 @@ interest_point_matching_rigid = InterestPointMatching(
46
46
  search_radius=config['search_radius_rigid'],
47
47
  num_required_neighbors=config['num_required_neighbors_rigid'],
48
48
  model_min_matches=config['model_min_matches_rigid'],
49
- inlier_factor=config['inlier_factor_rigid'],
50
- lambda_value=config['lambda_value_rigid'],
49
+ inlier_threshold=config['inlier_threshold_rigid'],
50
+ min_inlier_ratio=config['min_inlier_ratio_rigid'],
51
51
  num_iterations=config['num_iterations_rigid'],
52
- regularization_weight=config['regularization_weight_rigid'],
52
+ regularization_weight=config['regularization_weight_matching_rigid'],
53
53
  image_file_prefix=config['image_file_prefix'],
54
54
  )
55
55
 
@@ -65,10 +65,10 @@ interest_point_matching_affine = InterestPointMatching(
65
65
  search_radius=config['search_radius_affine'],
66
66
  num_required_neighbors=config['num_required_neighbors_affine'],
67
67
  model_min_matches=config['model_min_matches_affine'],
68
- inlier_factor=config['inlier_factor_affine'],
69
- lambda_value=config['lambda_value_affine'],
68
+ inlier_threshold=config['inlier_threshold_affine'],
69
+ min_inlier_ratio=config['min_inlier_ratio_affine'],
70
70
  num_iterations=config['num_iterations_affine'],
71
- regularization_weight=config['regularization_weight_affine'],
71
+ regularization_weight=config['regularization_weight_matching_affine'],
72
72
  image_file_prefix=config['image_file_prefix'],
73
73
  )
74
74
 
@@ -84,10 +84,10 @@ interest_point_matching_split_affine = InterestPointMatching(
84
84
  search_radius=config['search_radius_split_affine'],
85
85
  num_required_neighbors=config['num_required_neighbors_split_affine'],
86
86
  model_min_matches=config['model_min_matches_split_affine'],
87
- inlier_factor=config['inlier_factor_split_affine'],
88
- lambda_value=config['lambda_value_split_affine'],
87
+ inlier_threshold=config['inlier_threshold_split_affine'],
88
+ min_inlier_ratio=config['min_inlier_ratio_split_affine'],
89
89
  num_iterations=config['num_iterations_split_affine'],
90
- regularization_weight=config['regularization_weight_split_affine'],
90
+ regularization_weight=config['regularization_weight_matching_split_affine'],
91
91
  image_file_prefix=config['image_file_prefix'],
92
92
  )
93
93
 
@@ -101,6 +101,7 @@ solver_rigid = Solver(
101
101
  absolute_threshold=config['absolute_threshold'],
102
102
  min_matches=config['min_matches'],
103
103
  damp=config['damp'],
104
+ regularization_weight=config['regularization_weight_solver_rigid'],
104
105
  max_iterations=config['max_iterations'],
105
106
  max_allowed_error=config['max_allowed_error'],
106
107
  max_plateauwidth=config['max_plateauwidth'],
@@ -118,6 +119,7 @@ solver_affine = Solver(
118
119
  absolute_threshold=config['absolute_threshold'],
119
120
  min_matches=config['min_matches'],
120
121
  damp=config['damp'],
122
+ regularization_weight=config['regularization_weight_solver_affine'],
121
123
  max_iterations=config['max_iterations'],
122
124
  max_allowed_error=config['max_allowed_error'],
123
125
  max_plateauwidth=config['max_plateauwidth'],
@@ -135,6 +137,7 @@ solver_split_affine = Solver(
135
137
  absolute_threshold=config['absolute_threshold'],
136
138
  min_matches=config['min_matches'],
137
139
  damp=config['damp'],
140
+ regularization_weight=config['regularization_weight_solver_split_affine'],
138
141
  max_iterations=config['max_iterations'],
139
142
  max_allowed_error=config['max_allowed_error'],
140
143
  max_plateauwidth=config['max_plateauwidth'],
@@ -157,11 +160,11 @@ split_dataset = SplitDataset(
157
160
  )
158
161
 
159
162
  # -- ALIGNMENT PIPELINE --
160
- # interest_point_detection.run()
161
- # interest_point_matching_rigid.run()
163
+ interest_point_detection.run()
164
+ interest_point_matching_rigid.run()
162
165
  solver_rigid.run()
163
- # interest_point_matching_affine.run()
164
- # solver_affine.run()
165
- # split_dataset.run()
166
- # interest_point_matching_split_affine.run()
167
- # solver_split_affine.run()
166
+ interest_point_matching_affine.run()
167
+ solver_affine.run()
168
+ split_dataset.run()
169
+ interest_point_matching_split_affine.run()
170
+ solver_split_affine.run()
@@ -16,7 +16,8 @@ This class implements the Solver pipeline for rigid, affine, and split-affine op
16
16
 
17
17
  class Solver:
18
18
  def __init__(self, xml_file_path_output, n5_input_path, xml_file_path, run_type, relative_threshold, absolute_threshold,
19
- min_matches, damp, max_iterations, max_allowed_error, max_plateauwidth, metrics_output_path, fixed_tile):
19
+ min_matches, damp, regularization_weight, max_iterations, max_allowed_error, max_plateauwidth, metrics_output_path,
20
+ fixed_tile):
20
21
  self.xml_file_path_output = xml_file_path_output
21
22
  self.n5_input_path = n5_input_path
22
23
  self.xml_file_path = xml_file_path
@@ -25,6 +26,7 @@ class Solver:
25
26
  self.absolute_threshold = absolute_threshold
26
27
  self.min_matches = min_matches
27
28
  self.damp = damp
29
+ self.regularization_weight = regularization_weight
28
30
  self.max_iterations = max_iterations
29
31
  self.max_allowed_error = max_allowed_error
30
32
  self.max_plateauwidth = max_plateauwidth
@@ -79,7 +81,8 @@ class Solver:
79
81
 
80
82
  # Update all points with transform models and iterate through all tiles (views) and optimize alignment
81
83
  global_optimization = GlobalOptimization(tc, self.relative_threshold, self.absolute_threshold, self.min_matches, self.damp,
82
- self.max_iterations, self.max_allowed_error, self.max_plateauwidth, self.run_type, self.metrics_output_path)
84
+ self.regularization_weight, self.max_iterations, self.max_allowed_error,
85
+ self.max_plateauwidth, self.run_type, self.metrics_output_path)
83
86
  tiles, validation_stats = global_optimization.run()
84
87
  print("Global optimization complete")
85
88
 
@@ -102,7 +105,8 @@ class Solver:
102
105
 
103
106
  # Update all points with transform models and iterate through all tiles (views) and optimize alignment
104
107
  global_optimization = GlobalOptimization(tc, self.relative_threshold, self.absolute_threshold, self.min_matches, self.damp,
105
- self.max_iterations, self.max_allowed_error, self.max_plateauwidth, self.run_type, self.metrics_output_path)
108
+ self.regularization_weight, self.max_iterations, self.max_allowed_error,
109
+ self.max_plateauwidth, self.run_type, self.metrics_output_path)
106
110
  tiles_round_2, validation_stats_round_2 = global_optimization.run()
107
111
  print("Global optimization complete")
108
112
 
@@ -7,13 +7,14 @@ GlobalOptimization iteratively refines per-tile transforms to achieve sub-pixel
7
7
  """
8
8
 
9
9
  class GlobalOptimization:
10
- def __init__(self, tiles, relative_threshold, absolute_threshold, min_matches,
11
- damp, max_iterations, max_allowed_error, max_plateauwidth, run_type, metrics_output_path):
10
+ def __init__(self, tiles, relative_threshold, absolute_threshold, min_matches, damp, regularization_weight,
11
+ max_iterations, max_allowed_error, max_plateauwidth, run_type, metrics_output_path):
12
12
  self.tiles = tiles
13
13
  self.relative_threshold = relative_threshold
14
14
  self.absolute_threshold = absolute_threshold
15
15
  self.min_matches = min_matches
16
16
  self.damp = damp
17
+ self.regularization_weight = regularization_weight
17
18
  self.max_iterations = max_iterations
18
19
  self.max_allowed_error = max_allowed_error
19
20
  self.max_plateauwidth = max_plateauwidth
@@ -307,8 +308,7 @@ class GlobalOptimization:
307
308
  return affine_model
308
309
 
309
310
  def regularize_models(self, affine, rigid):
310
- alpha=0.1
311
- l1 = 1.0 - alpha
311
+ l1 = 1.0 - self.regularization_weight
312
312
 
313
313
  def to_array(model):
314
314
  return [
@@ -320,7 +320,7 @@ class GlobalOptimization:
320
320
  afs = to_array(affine)
321
321
  bfs = to_array(rigid)
322
322
 
323
- rfs = [l1 * a + alpha * b for a, b in zip(afs, bfs)]
323
+ rfs = [l1 * a + self.regularization_weight * b for a, b in zip(afs, bfs)]
324
324
 
325
325
  keys = [
326
326
  'm00', 'm01', 'm02', 'm03',