microeye 2.4.3__tar.gz → 2.4.4__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 (254) hide show
  1. {microeye-2.4.3/src/microeye.egg-info → microeye-2.4.4}/PKG-INFO +6 -6
  2. {microeye-2.4.3 → microeye-2.4.4}/README.md +5 -5
  3. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/_version.py +1 -1
  4. microeye-2.4.4/src/microEye/analysis/clustering/dbscan.py +459 -0
  5. microeye-2.4.4/src/microEye/analysis/clustering/plot.py +1141 -0
  6. microeye-2.4.4/src/microEye/analysis/clustering/rulers.py +361 -0
  7. microeye-2.4.4/src/microEye/analysis/file_system.py +394 -0
  8. microeye-2.4.4/src/microEye/analysis/fitting/aim.py +836 -0
  9. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/processing.py +60 -34
  10. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/spline.py +1 -1
  11. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/results.py +141 -81
  12. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/multi_viewer.py +85 -272
  13. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/processing/frc.py +1 -1
  14. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/rendering/base.py +20 -13
  15. microeye-2.4.4/src/microEye/analysis/tools/batch.py +420 -0
  16. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/registration.py +17 -5
  17. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/utils/__init__.py +1 -1
  18. microeye-2.4.4/src/microEye/analysis/utils/windows.py +40 -0
  19. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/images.py +119 -80
  20. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/localizations.py +288 -351
  21. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/psf.py +2 -0
  22. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/camera_options.py +15 -1
  23. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/camera_panel.py +1 -1
  24. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/mieye/acquisition_manager.py +133 -119
  25. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/mieye/miEye.py +10 -0
  26. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/devices.py +1 -0
  27. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/scan_acquisition.py +24 -8
  28. microeye-2.4.4/src/microEye/tools/__init__.py +0 -0
  29. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/display.py +27 -1
  30. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/expandable_groupbox.py +10 -2
  31. {microeye-2.4.3 → microeye-2.4.4/src/microeye.egg-info}/PKG-INFO +6 -6
  32. {microeye-2.4.3 → microeye-2.4.4}/src/microeye.egg-info/SOURCES.txt +7 -1
  33. microeye-2.4.3/src/microEye/analysis/fitting/clustering.py +0 -50
  34. microeye-2.4.3/src/microEye/analysis/utils/windows.py +0 -19
  35. {microeye-2.4.3 → microeye-2.4.4}/LICENSE +0 -0
  36. {microeye-2.4.3 → microeye-2.4.4}/MANIFEST.in +0 -0
  37. {microeye-2.4.3 → microeye-2.4.4}/pyproject.toml +0 -0
  38. {microeye-2.4.3 → microeye-2.4.4}/setup.cfg +0 -0
  39. {microeye-2.4.3 → microeye-2.4.4}/setup.py +0 -0
  40. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/__init__.py +0 -0
  41. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/__init__.py +0 -0
  42. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/checklist_dialog.py +0 -0
  43. {microeye-2.4.3/src/microEye/analysis/fitting → microeye-2.4.4/src/microEye/analysis/clustering}/__init__.py +0 -0
  44. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/cmosMaps.py +0 -0
  45. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/filters/__init__.py +0 -0
  46. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/filters/base.py +0 -0
  47. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/filters/spatial.py +0 -0
  48. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/filters/temporal.py +0 -0
  49. {microeye-2.4.3/src/microEye/analysis/tools → microeye-2.4.4/src/microEye/analysis/fitting}/__init__.py +0 -0
  50. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/fit.py +0 -0
  51. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/nena.py +0 -0
  52. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/phasor_fit.py +0 -0
  53. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/__init__.py +0 -0
  54. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/extract.py +0 -0
  55. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/rubost_mean.py +0 -0
  56. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/stats/__init__.py +0 -0
  57. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/stats/core.py +0 -0
  58. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/stats/curve_fit.py +0 -0
  59. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/stats/io.py +0 -0
  60. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/stats/slope_fit.py +0 -0
  61. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/temp.py +0 -0
  62. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/psf/test.py +0 -0
  63. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +0 -0
  64. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +0 -0
  65. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +0 -0
  66. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +0 -0
  67. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +0 -0
  68. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +0 -0
  69. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +0 -0
  70. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +0 -0
  71. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +0 -0
  72. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/__init__.py +0 -0
  73. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/constants.py +0 -0
  74. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +0 -0
  75. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/rcc.py +0 -0
  76. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/results_stats.py +0 -0
  77. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/fitting/tardis.py +0 -0
  78. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/processing/__init__.py +0 -0
  79. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/rendering/__init__.py +0 -0
  80. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/rendering/cloud.py +0 -0
  81. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/rendering/core.py +0 -0
  82. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/rendering/volumetric.py +0 -0
  83. {microeye-2.4.3/src/microEye/hardware/cams/basler → microeye-2.4.4/src/microEye/analysis/tools}/__init__.py +0 -0
  84. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/common.py +0 -0
  85. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/__init__.py +0 -0
  86. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/analysis.py +0 -0
  87. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/compare.py +0 -0
  88. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/constants.py +0 -0
  89. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/core.py +0 -0
  90. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/io.py +0 -0
  91. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/dark_cal/plotting.py +0 -0
  92. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/kymograms.py +0 -0
  93. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/__init__.py +0 -0
  94. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/analysis.py +0 -0
  95. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/compare.py +0 -0
  96. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/constants.py +0 -0
  97. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/core.py +0 -0
  98. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/io.py +0 -0
  99. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/models.py +0 -0
  100. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/plotting.py +0 -0
  101. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/photon_cal/table.py +0 -0
  102. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/roi_selectors.py +0 -0
  103. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/tools/sphere_cal.py +0 -0
  104. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/utils/coordinates.py +0 -0
  105. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/utils/images.py +0 -0
  106. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/__init__.py +0 -0
  107. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/image_options_widget.py +0 -0
  108. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/layers_widget.py +0 -0
  109. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/analysis/viewer/volume.py +0 -0
  110. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/__init__.py +0 -0
  111. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/__init__.py +0 -0
  112. {microeye-2.4.3/src/microEye/hardware/misc → microeye-2.4.4/src/microEye/hardware/cams/basler}/__init__.py +0 -0
  113. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/basler/basler_cam.py +0 -0
  114. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/basler/basler_panel.py +0 -0
  115. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/camera_calibration.py +0 -0
  116. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/camera_list.py +0 -0
  117. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/dummy/__init__.py +0 -0
  118. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/dummy/dummy_panel.py +0 -0
  119. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/jobs.py +0 -0
  120. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/line_profiler.py +0 -0
  121. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/linescan/IR_Cam.py +0 -0
  122. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/linescan/__init__.py +0 -0
  123. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/micam.py +0 -0
  124. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pco/__init__.py +0 -0
  125. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pco/enums.py +0 -0
  126. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pco/pco_cam.py +0 -0
  127. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pco/pco_panel.py +0 -0
  128. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/peak/__init__.py +0 -0
  129. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/peak/peak_camera.py +0 -0
  130. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/peak/peak_panel.py +0 -0
  131. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pycromanager/__init__.py +0 -0
  132. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pycromanager/pycro_cam.py +0 -0
  133. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/pycromanager/pycro_panel.py +0 -0
  134. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/shortcuts.py +0 -0
  135. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/thorlabs/__init__.py +0 -0
  136. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/thorlabs/thorlabs.py +0 -0
  137. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/thorlabs/thorlabs_panel.py +0 -0
  138. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/ueye/__init__.py +0 -0
  139. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/ueye/ueye_camera.py +0 -0
  140. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/ueye/ueye_panel.py +0 -0
  141. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/vimba/__init__.py +0 -0
  142. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/vimba/vimba_cam.py +0 -0
  143. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/cams/vimba/vimba_panel.py +0 -0
  144. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/device.py +0 -0
  145. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/lasers/__init__.py +0 -0
  146. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/lasers/io_matchbox.py +0 -0
  147. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/lasers/io_params.py +0 -0
  148. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/lasers/io_single_laser.py +0 -0
  149. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/lasers/laser_relay.py +0 -0
  150. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/mieye/__init__.py +0 -0
  151. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/mieye/devices_manager.py +0 -0
  152. {microeye-2.4.3/src/microEye/hardware/protocols → microeye-2.4.4/src/microEye/hardware/misc}/__init__.py +0 -0
  153. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/misc/acquisition_view.py +0 -0
  154. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/misc/reglo.py +0 -0
  155. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/misc/temp.py +0 -0
  156. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/port_config.py +0 -0
  157. {microeye-2.4.3/src/microEye/hardware/stages → microeye-2.4.4/src/microEye/hardware/protocols}/__init__.py +0 -0
  158. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/protocols/actions.py +0 -0
  159. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/protocols/actions_items.py +0 -0
  160. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/protocols/designer.py +0 -0
  161. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/protocols/scene_manager.py +0 -0
  162. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/protocols/serialization.py +0 -0
  163. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/__init__.py +0 -0
  164. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/core.py +0 -0
  165. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/enums.py +0 -0
  166. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/headless.py +0 -0
  167. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/utils.py +0 -0
  168. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/widgets/__init__.py +0 -0
  169. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/widgets/bridges.py +0 -0
  170. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/widgets/headless_manager.py +0 -0
  171. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/pycromanager/widgets/headless_options.py +0 -0
  172. {microeye-2.4.3/src/microEye/hardware/stages/stabilization → microeye-2.4.4/src/microEye/hardware/stages}/__init__.py +0 -0
  173. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/__init__.py +0 -0
  174. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/baseDevice.py +0 -0
  175. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/device.py +0 -0
  176. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/deviceID.py +0 -0
  177. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/devicePort.py +0 -0
  178. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/deviceStatus.py +0 -0
  179. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/devicesView.py +0 -0
  180. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/ellDevices.py +0 -0
  181. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/messageUpdater.py +0 -0
  182. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/motorInfo.py +0 -0
  183. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/stage.py +0 -0
  184. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/elliptec/test.py +0 -0
  185. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/__init__.py +0 -0
  186. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/kdc101/__init__.py +0 -0
  187. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/kdc101/enums.py +0 -0
  188. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/kdc101/factory.py +0 -0
  189. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/kdc101/kdc101.py +0 -0
  190. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/kinesis/kinesis.py +0 -0
  191. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/manager.py +0 -0
  192. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/piezon_concept/__init__.py +0 -0
  193. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/piezon_concept/foc.py +0 -0
  194. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/pycromanager/__init__.py +0 -0
  195. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/pycromanager/core.py +0 -0
  196. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/smaract/__init__.py +0 -0
  197. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/smaract/mcs2.py +0 -0
  198. {microeye-2.4.3/src/microEye/icons → microeye-2.4.4/src/microEye/hardware/stages/stabilization}/__init__.py +0 -0
  199. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/stabilization/controller.py +0 -0
  200. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/stabilization/methods.py +0 -0
  201. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/stabilizer.py +0 -0
  202. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/stage.py +0 -0
  203. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/stages/view.py +0 -0
  204. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/__init__.py +0 -0
  205. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/controller.py +0 -0
  206. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/devices.py +0 -0
  207. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/focusWidget.py +0 -0
  208. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/indicator.py +0 -0
  209. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/qlist_slider.py +0 -0
  210. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/hardware/widgets/slides.py +0 -0
  211. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/1024.png +0 -0
  212. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/128.png +0 -0
  213. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/16.png +0 -0
  214. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/24.png +0 -0
  215. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/256.png +0 -0
  216. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/32.png +0 -0
  217. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/48.png +0 -0
  218. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/512.png +0 -0
  219. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/64.png +0 -0
  220. {microeye-2.4.3/src/microEye/tools → microeye-2.4.4/src/microEye/icons}/__init__.py +0 -0
  221. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/close.svg +0 -0
  222. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/mieye.png +0 -0
  223. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/min.svg +0 -0
  224. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/icons/viewer.png +0 -0
  225. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/images/__init__.py +0 -0
  226. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/images/handlers.py +0 -0
  227. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/images/uImage.py +0 -0
  228. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/images/zarr.py +0 -0
  229. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/launcher.py +0 -0
  230. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/qt.py +0 -0
  231. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/tools/color.py +0 -0
  232. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/tools/microscopy.py +0 -0
  233. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/__init__.py +0 -0
  234. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/enum_encoder.py +0 -0
  235. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/gui_helper.py +0 -0
  236. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/hid_utils/__init__.py +0 -0
  237. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/hid_utils/controller.py +0 -0
  238. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/hid_utils/device.py +0 -0
  239. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/hid_utils/enums.py +0 -0
  240. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/hid_utils/utils.py +0 -0
  241. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/labelled_slider.py +0 -0
  242. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/metadata.py +0 -0
  243. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/metadata_tree.py +0 -0
  244. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/micro_launcher.py +0 -0
  245. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/parameter_tree.py +0 -0
  246. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/pyqt2mplt.py +0 -0
  247. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/pyscripting.py +0 -0
  248. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/retry_exec.py +0 -0
  249. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/start_gui.py +0 -0
  250. {microeye-2.4.3 → microeye-2.4.4}/src/microEye/utils/thread_worker.py +0 -0
  251. {microeye-2.4.3 → microeye-2.4.4}/src/microeye.egg-info/dependency_links.txt +0 -0
  252. {microeye-2.4.3 → microeye-2.4.4}/src/microeye.egg-info/entry_points.txt +0 -0
  253. {microeye-2.4.3 → microeye-2.4.4}/src/microeye.egg-info/requires.txt +0 -0
  254. {microeye-2.4.3 → microeye-2.4.4}/src/microeye.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microeye
3
- Version: 2.4.3
3
+ Version: 2.4.4
4
4
  Summary: A python toolkit for fluorescence microscopy that features hardware control, data analysis and vizualization for super-resolution single-molecule localization microscopy and single-partical tracking.
5
5
  Home-page: https://github.com/samhitech/microEye
6
6
  Author: Mohammad Nour Alsamsam
@@ -72,11 +72,11 @@ The **`microEye`** is a Python toolkit for fluorescence microscopy that supports
72
72
  This toolkit is compatible with the [hardware](#hardware) used in our microscope. For further details, refer to the [miEye microscope paper](https://doi.org/10.1016/j.ohx.2022.e00368) and [OSF project](http://doi.org/10.17605/osf.io/j2fqy).
73
73
 
74
74
  ```bash
75
- __ ____ ____ ___ ____ ____
76
- / |/ (_)__________ / __/_ _____ _ __|_ |/ / / |_ /
77
- / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _/ _/_ <
78
- /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)____/
79
- /___/
75
+ __ ____ ____ ___ ____ ____
76
+ / |/ (_)__________ / __/_ _____ _ __|_ |/ / // / /
77
+ / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _/_ _/
78
+ /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)_/
79
+ /___/
80
80
  ```
81
81
 
82
82
  <!-- ![Package Health](https://snyk.io/advisor/python/microEye/badge.svg) -->
@@ -7,11 +7,11 @@ The **`microEye`** is a Python toolkit for fluorescence microscopy that supports
7
7
  This toolkit is compatible with the [hardware](#hardware) used in our microscope. For further details, refer to the [miEye microscope paper](https://doi.org/10.1016/j.ohx.2022.e00368) and [OSF project](http://doi.org/10.17605/osf.io/j2fqy).
8
8
 
9
9
  ```bash
10
- __ ____ ____ ___ ____ ____
11
- / |/ (_)__________ / __/_ _____ _ __|_ |/ / / |_ /
12
- / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _/ _/_ <
13
- /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)____/
14
- /___/
10
+ __ ____ ____ ___ ____ ____
11
+ / |/ (_)__________ / __/_ _____ _ __|_ |/ / // / /
12
+ / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _/_ _/
13
+ /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)_/
14
+ /___/
15
15
  ```
16
16
 
17
17
  <!-- ![Package Health](https://snyk.io/advisor/python/microEye/badge.svg) -->
@@ -1,2 +1,2 @@
1
- version_info = (2, 4, 3)
1
+ version_info = (2, 4, 4)
2
2
  __version__ = '.'.join(map(str, version_info))
@@ -0,0 +1,459 @@
1
+ import logging
2
+ from dataclasses import dataclass
3
+ from enum import Enum
4
+
5
+ import numba as nb
6
+ import numpy as np
7
+ from sklearn.cluster import DBSCAN
8
+ from sklearn.preprocessing import StandardScaler
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ @nb.njit(cache=True)
14
+ def geometric_median(points, max_iter=128, tol=1e-6, eps=1e-12):
15
+ guess = np.median(points, axis=0)
16
+ for _ in range(max_iter):
17
+ try:
18
+ deltas = points - guess
19
+ distances = np.linalg.norm(deltas, axis=1)
20
+ weights = 1.0 / np.maximum(distances, eps)
21
+ new_guess = np.sum(points * weights[:, None], axis=0) / np.sum(weights)
22
+ if np.linalg.norm(new_guess - guess) < tol:
23
+ return new_guess
24
+ guess = new_guess
25
+ except Exception as e:
26
+ logger.error(f'Error in geometric_median: {e}')
27
+ break
28
+ return guess
29
+
30
+
31
+ @nb.njit(fastmath=True, cache=True)
32
+ def geometric_median_numba(points, max_iter=128, tol=1e-6, eps=1e-12):
33
+ n_points, n_dims = points.shape
34
+
35
+ guess = np.empty(n_dims, dtype=points.dtype)
36
+ for d in range(n_dims):
37
+ guess[d] = np.median(points[:, d])
38
+
39
+ new_guess = np.empty(n_dims, dtype=points.dtype)
40
+
41
+ # Iterative Weiszfeld's algorithm loop
42
+ for _ in range(max_iter):
43
+ total_weight = 0.0
44
+
45
+ # Reset new_guess array to zero for current iteration accumulation
46
+ for d in range(n_dims):
47
+ new_guess[d] = 0.0
48
+
49
+ # Compute weights and accumulate values using flat loops
50
+ for i in range(n_points):
51
+ dist_sq = 0.0
52
+ for d in range(n_dims):
53
+ diff = points[i, d] - guess[d]
54
+ dist_sq += diff * diff
55
+
56
+ dist = np.sqrt(dist_sq)
57
+ if dist < eps:
58
+ dist = eps
59
+
60
+ weight = 1.0 / dist
61
+ total_weight += weight
62
+
63
+ for d in range(n_dims):
64
+ new_guess[d] += points[i, d] * weight
65
+
66
+ if total_weight == 0.0:
67
+ break
68
+
69
+ # Finalize the step calculation
70
+ for d in range(n_dims):
71
+ new_guess[d] /= total_weight
72
+
73
+ # Check convergence using Euclidean distance
74
+ delta_sq = 0.0
75
+ for d in range(n_dims):
76
+ diff = new_guess[d] - guess[d]
77
+ delta_sq += diff * diff
78
+
79
+ if np.sqrt(delta_sq) < tol:
80
+ return new_guess
81
+
82
+ # Update current guess manually via value copies
83
+ for d in range(n_dims):
84
+ guess[d] = new_guess[d]
85
+
86
+ return guess
87
+
88
+
89
+ @nb.njit(fastmath=True, cache=True)
90
+ def huber_center(points, delta=2.0, max_iter=64, tol=1e-5):
91
+ '''
92
+ Calculates the spatial robust center using M-estimation with a Huber loss.
93
+ Eliminates the single-pixel snapping artifact of the geometric median.
94
+
95
+ delta: Distance threshold (in nm) where the loss switches from L2 to L1.
96
+ Set this to roughly 1 to 2 times your expected cluster precision.
97
+ '''
98
+ n_points, n_dims = points.shape
99
+
100
+ # Safety Check: If empty array or corrupted inputs passed, exit early
101
+ if n_points == 0:
102
+ return np.zeros(n_dims, dtype=points.dtype)
103
+
104
+ # Initialize guess with standard mean
105
+ guess = np.empty(n_dims, dtype=points.dtype)
106
+ for d in range(n_dims):
107
+ guess[d] = np.mean(points[:, d])
108
+
109
+ new_guess = np.empty(n_dims, dtype=points.dtype)
110
+
111
+ for _ in range(max_iter):
112
+ total_weight = 0.0
113
+ for d in range(n_dims):
114
+ new_guess[d] = 0.0
115
+
116
+ for i in range(n_points):
117
+ dist_sq = 0.0
118
+ for d in range(n_dims):
119
+ diff = points[i, d] - guess[d]
120
+ dist_sq += diff * diff
121
+ dist = np.sqrt(dist_sq)
122
+
123
+ if dist <= delta:
124
+ weight = 1.0
125
+ else:
126
+ # Numba friendly approach: ensure dist can NEVER be zero here
127
+ # since dist > delta and delta > 0, dist is physically > 0,
128
+ # but we add an explicit fallback for safety.
129
+ safe_dist = dist if dist > 1e-12 else 1e-12
130
+ weight = delta / safe_dist
131
+
132
+ total_weight += weight
133
+ for d in range(n_dims):
134
+ new_guess[d] += points[i, d] * weight
135
+
136
+ # FIXED: Wrap the division completely inside a strict safety check
137
+ # to prevent Numba from pre-optimizing the division pathway.
138
+ if total_weight > 1e-12:
139
+ for d in range(n_dims):
140
+ new_guess[d] /= total_weight
141
+ else:
142
+ # Fallback if weights collapse to zero (e.g. all points are NaN/Inf)
143
+ break
144
+
145
+ # Check Convergence
146
+ delta_sq = 0.0
147
+ for d in range(n_dims):
148
+ diff = new_guess[d] - guess[d]
149
+ delta_sq += diff * diff
150
+
151
+ if np.sqrt(delta_sq) < tol:
152
+ return new_guess
153
+
154
+ for d in range(n_dims):
155
+ guess[d] = new_guess[d]
156
+
157
+ return guess
158
+
159
+
160
+ class CenteringMode(Enum):
161
+ CRLB_WEIGHTED_MEAN = 'crlb_weighted_mean'
162
+ MEAN = 'mean'
163
+ GEOMETRIC_MEDIAN = 'geometric_median'
164
+ HUBER_CENTER = 'huber_center'
165
+
166
+
167
+ def get_center(
168
+ x: np.ndarray,
169
+ y: np.ndarray,
170
+ z: np.ndarray | None,
171
+ center_mode: CenteringMode = CenteringMode.CRLB_WEIGHTED_MEAN,
172
+ crlb_x: np.ndarray | None = None,
173
+ crlb_y: np.ndarray | None = None,
174
+ eps: float = 1e-12,
175
+ ):
176
+ '''
177
+ Computes the center of clusters based on the specified centering mode.
178
+
179
+ *Methods*
180
+
181
+ - CRLB_WEIGHTED_MEAN: Computes the center using a weighted mean based on the
182
+ Cramer-Rao Lower Bound (CRLB) variances for x and y coordinates.
183
+ - GEOMETRIC_MEDIAN: Computes the center as the geometric median of the points.
184
+ - HUBER_CENTER: Computes the center using M-estimation with a Huber loss function.
185
+ - MEAN: Computes the center as the arithmetic mean of the points.
186
+
187
+ Parameters
188
+ -----------
189
+ x, y, z: np.ndarray
190
+ Arrays of x, y, z coordinates of localizations.
191
+ center_mode: CenteringMode
192
+ The method to compute the center of clusters.
193
+ crlb_x, crlb_y: np.ndarray, optional
194
+ Arrays of CRLB variance values for x and y coordinates.
195
+ Required if center_mode is CenteringMode.CRLB_WEIGHTED_MEAN.
196
+ eps: float
197
+ A small value to avoid division by zero when computing weights.
198
+ (default: 1e-12)
199
+
200
+ Returns
201
+ --------
202
+ mu_x, mu_y, mu_z: np.ndarray
203
+ '''
204
+ if (
205
+ center_mode == CenteringMode.CRLB_WEIGHTED_MEAN
206
+ and crlb_x is not None
207
+ and crlb_y is not None
208
+ ):
209
+ wx = 1.0 / np.maximum(crlb_x, eps)
210
+ wy = 1.0 / np.maximum(crlb_y, eps)
211
+ mu_x = np.sum(wx * x) / np.sum(wx)
212
+ mu_y = np.sum(wy * y) / np.sum(wy)
213
+ mu_z = np.mean(z) if z is not None else None
214
+
215
+ # 1. Calculate variance (squared error) for X and Y independently
216
+ var_x_com = 1.0 / np.sum(1.0 / np.maximum(crlb_x, eps))
217
+ var_y_com = 1.0 / np.sum(1.0 / np.maximum(crlb_y, eps))
218
+
219
+ # 2. Combine them in quadrature to get true 2D radial uncertainty
220
+ sigma_com = np.sqrt(var_x_com + var_y_com)
221
+
222
+ elif center_mode == CenteringMode.GEOMETRIC_MEDIAN:
223
+ if z is not None:
224
+ mu_x, mu_y, mu_z = geometric_median_numba(np.column_stack((x, y, z)))
225
+ else:
226
+ mu_x, mu_y = geometric_median_numba(np.column_stack((x, y)))
227
+ mu_z = None
228
+
229
+ var_x = np.mean((x - mu_x) ** 2)
230
+ var_y = np.mean((y - mu_y) ** 2)
231
+
232
+ # sqrt(pi/2) factor converts from 1D std to 2D radial std
233
+ sigma_com = 1.253 * np.sqrt(var_x + var_y) / np.sqrt(len(x))
234
+
235
+ elif center_mode == CenteringMode.HUBER_CENTER:
236
+ if z is not None:
237
+ mu_x, mu_y, mu_z = huber_center(np.column_stack((x, y, z)))
238
+ else:
239
+ mu_x, mu_y = huber_center(np.column_stack((x, y)))
240
+ mu_z = None
241
+
242
+ # Median Absolute Deviation (MAD)
243
+ mad_x = 1.4826 * np.median(np.abs(x - mu_x)) / np.sqrt(len(x))
244
+ mad_y = 1.4826 * np.median(np.abs(y - mu_y)) / np.sqrt(len(y))
245
+
246
+ sigma_com = np.sqrt(mad_x**2 + mad_y**2)
247
+
248
+ else:
249
+ mu_x = np.mean(x)
250
+ mu_y = np.mean(y)
251
+ mu_z = np.mean(z) if z is not None else None
252
+
253
+ var_x = np.mean((x - mu_x) ** 2)
254
+ var_y = np.mean((y - mu_y) ** 2)
255
+
256
+ # Standard error of the mean
257
+ sigma_com = np.sqrt(var_x + var_y) / np.sqrt(len(x))
258
+
259
+ return mu_x, mu_y, mu_z, sigma_com
260
+
261
+
262
+ @dataclass
263
+ class ClusterCenterResult:
264
+ x: np.ndarray
265
+ y: np.ndarray
266
+ z: np.ndarray | None
267
+ labels: np.ndarray
268
+ intensity: np.ndarray | None
269
+ crlb_x: np.ndarray | None
270
+ crlb_y: np.ndarray | None
271
+ cx: np.ndarray
272
+ cy: np.ndarray
273
+ cz: np.ndarray | None
274
+ c_sigma: np.ndarray | None
275
+ c_label: np.ndarray
276
+ n_points: np.ndarray
277
+ rejected: int
278
+
279
+
280
+ def get_centers(
281
+ x: np.ndarray,
282
+ y: np.ndarray,
283
+ z: np.ndarray | None,
284
+ labels: np.ndarray,
285
+ intensity: np.ndarray | None = None,
286
+ crlb_x: np.ndarray | None = None,
287
+ crlb_y: np.ndarray | None = None,
288
+ center_mode: CenteringMode = CenteringMode.CRLB_WEIGHTED_MEAN,
289
+ eps: float = 1e-12,
290
+ rejection_threshold: float | None = None,
291
+ min_points: int | None = None,
292
+ max_points: int | None = None,
293
+ subtract_com: bool = False,
294
+ **kwargs,
295
+ ) -> ClusterCenterResult | None:
296
+ try:
297
+ # Filter out noise (label < 0) safely
298
+ valid_mask = labels >= 0
299
+ x = x[valid_mask]
300
+ y = y[valid_mask]
301
+ z = z[valid_mask] if z is not None else None
302
+ intensity = intensity[valid_mask] if intensity is not None else None
303
+ labels = labels[valid_mask]
304
+
305
+ if crlb_x is not None:
306
+ crlb_x = np.asarray(crlb_x, dtype=np.float64)[valid_mask]
307
+ if crlb_y is not None:
308
+ crlb_y = np.asarray(crlb_y, dtype=np.float64)[valid_mask]
309
+
310
+ if len(labels) == 0:
311
+ return None
312
+
313
+ _arg_sort = np.argsort(labels)
314
+ x = x[_arg_sort]
315
+ y = y[_arg_sort]
316
+ z = z[_arg_sort] if z is not None else None
317
+ intensity = intensity[_arg_sort] if intensity is not None else None
318
+ if crlb_x is not None:
319
+ crlb_x = crlb_x[_arg_sort]
320
+ if crlb_y is not None:
321
+ crlb_y = crlb_y[_arg_sort]
322
+
323
+ labels = labels[_arg_sort]
324
+
325
+ label_ids = np.cumsum(np.bincount(labels.astype(np.int64)))
326
+
327
+ unique_labels = np.unique(labels.astype(np.int64))
328
+
329
+ # array to hold the computed centers for each cluster
330
+ cx = []
331
+ cy = []
332
+ cz = [] if z is not None else None
333
+ c_sigma = []
334
+ c_labels = []
335
+ n_points = []
336
+
337
+ rejected = []
338
+
339
+ for i in unique_labels:
340
+ s = slice(label_ids[i - 1] if i > 0 else 0, label_ids[i])
341
+
342
+ if len(x[s]) == 0:
343
+ logger.debug('Cluster %d has no points. Skipping.', i)
344
+ continue
345
+
346
+ x_cluster = x[s]
347
+ y_cluster = y[s]
348
+ z_cluster = z[s] if z is not None else None
349
+
350
+ mu_x, mu_y, mu_z, sigma_com = get_center(
351
+ x_cluster,
352
+ y_cluster,
353
+ z_cluster,
354
+ center_mode=center_mode,
355
+ crlb_x=crlb_x[s] if crlb_x is not None else None,
356
+ crlb_y=crlb_y[s] if crlb_y is not None else None,
357
+ eps=eps,
358
+ )
359
+
360
+ # Reject clusters with large uncertainty
361
+ if (
362
+ (rejection_threshold is not None and sigma_com > rejection_threshold)
363
+ or (min_points is not None and len(x_cluster) < min_points)
364
+ or (max_points is not None and len(x_cluster) > max_points)
365
+ ):
366
+ rejected.append(i)
367
+ continue
368
+
369
+ cx.append(mu_x)
370
+ cy.append(mu_y)
371
+ if cz is not None:
372
+ cz.append(mu_z)
373
+ c_sigma.append(sigma_com)
374
+ c_labels.append(i)
375
+ n_points.append(len(x_cluster))
376
+
377
+ if subtract_com:
378
+ x[s] -= mu_x
379
+ y[s] -= mu_y
380
+ if z is not None:
381
+ z[s] -= mu_z
382
+
383
+ if len(rejected) > 0:
384
+ logger.warning(
385
+ 'Rejected %d clusters due to uncertainty or insufficient points.',
386
+ len(rejected),
387
+ )
388
+
389
+ valid_mask = np.isin(labels, rejected, invert=True)
390
+ x = x[valid_mask]
391
+ y = y[valid_mask]
392
+ intensity = intensity[valid_mask] if intensity is not None else None
393
+ labels = labels[valid_mask]
394
+ crlb_x = crlb_x[valid_mask] if crlb_x is not None else None
395
+ crlb_y = crlb_y[valid_mask] if crlb_y is not None else None
396
+ if z is not None:
397
+ z = z[valid_mask]
398
+
399
+ return ClusterCenterResult(
400
+ x=x,
401
+ y=y,
402
+ z=z,
403
+ labels=labels,
404
+ intensity=intensity,
405
+ crlb_x=crlb_x,
406
+ crlb_y=crlb_y,
407
+ cx=np.array(cx),
408
+ cy=np.array(cy),
409
+ cz=np.array(cz) if cz is not None else None,
410
+ c_sigma=np.array(c_sigma),
411
+ c_label=np.array(c_labels),
412
+ n_points=np.array(n_points),
413
+ rejected=len(rejected),
414
+ )
415
+ except Exception as e:
416
+ logger.error(f'Error in get_centers: {e}')
417
+ return None
418
+
419
+
420
+ def perform_dbscan_clustering(X, Y, Z, eps=50, min_samples=2, **kwargs):
421
+ '''
422
+ Performs DBSCAN clustering on 3D localization data.
423
+
424
+ Parameters:
425
+ ------------
426
+ X, Y, Z: array-like
427
+ Arrays of x, y, z coordinates of localizations.
428
+ eps: float, optional
429
+ The maximum distance between two samples for them to be considered
430
+ as in the same neighborhood. Default is 50.
431
+ min_samples: int, optional
432
+ The number of samples in a neighborhood for a point to be considered
433
+ a core point. Default is 2.
434
+ **kwargs: additional keyword arguments for DBSCAN.
435
+ '''
436
+ VECTORS = np.column_stack((X, Y, Z) if Z is not None else (X, Y))
437
+
438
+ if kwargs.pop('scale_data', False):
439
+ std = np.std(VECTORS)
440
+ if std > 0:
441
+ VECTORS /= std
442
+ eps /= std # Scale eps accordingly
443
+ else:
444
+ logger.warning('Standard deviation of data is zero. Skipping scaling.')
445
+
446
+ # 2. Initialize and fit DBSCAN
447
+ # eps: maximum distance between two samples
448
+ # min_samples: number of samples in a neighborhood for a point to be a core point
449
+ dbscan_model = DBSCAN(eps=eps, min_samples=min_samples, **kwargs)
450
+ dbscan_model.fit(VECTORS)
451
+
452
+ # 3. Retrieve labels (-1 indicates noise/outliers)
453
+ labels = dbscan_model.labels_
454
+ unique = np.unique(labels).shape[0] - 1
455
+ logger.info(
456
+ 'Cluster labels: %d (%d Points)', unique, labels[labels != -1].shape[0]
457
+ ) # Log only non-noise labels
458
+
459
+ return labels