microeye 2.4.62__tar.gz → 2.4.63__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 (279) hide show
  1. {microeye-2.4.62/src/microeye.egg-info → microeye-2.4.63}/PKG-INFO +24 -6
  2. {microeye-2.4.62 → microeye-2.4.63}/README.md +21 -5
  3. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/__init__.py +70 -8
  4. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/_version.py +1 -1
  5. microeye-2.4.62/src/microEye/analysis/clustering/dbscan.py → microeye-2.4.63/src/microEye/analysis/clustering/core.py +48 -69
  6. microeye-2.4.63/src/microEye/analysis/clustering/dataclasses.py +212 -0
  7. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/clustering/density.py +0 -45
  8. microeye-2.4.63/src/microEye/analysis/clustering/dialogs.py +715 -0
  9. microeye-2.4.63/src/microEye/analysis/clustering/grid.py +189 -0
  10. microeye-2.4.63/src/microEye/analysis/clustering/io.py +105 -0
  11. microeye-2.4.63/src/microEye/analysis/clustering/model.py +316 -0
  12. microeye-2.4.63/src/microEye/analysis/clustering/pipeline.py +161 -0
  13. microeye-2.4.63/src/microEye/analysis/clustering/plot.py +1013 -0
  14. microeye-2.4.63/src/microEye/analysis/clustering/rulers.py +181 -0
  15. microeye-2.4.63/src/microEye/analysis/clustering/viewer.py +530 -0
  16. microeye-2.4.63/src/microEye/analysis/drift/__init__.py +16 -0
  17. {microeye-2.4.62/src/microEye/analysis/fitting → microeye-2.4.63/src/microEye/analysis/drift}/aim.py +25 -71
  18. microeye-2.4.63/src/microEye/analysis/drift/comet.py +528 -0
  19. microeye-2.4.63/src/microEye/analysis/drift/core.py +62 -0
  20. microeye-2.4.63/src/microEye/analysis/drift/phase.py +22 -0
  21. microeye-2.4.63/src/microEye/analysis/drift/plot.py +92 -0
  22. {microeye-2.4.62/src/microEye/analysis/fitting → microeye-2.4.63/src/microEye/analysis/drift}/rcc.py +9 -0
  23. microeye-2.4.63/src/microEye/analysis/drift/segment.py +291 -0
  24. microeye-2.4.63/src/microEye/analysis/drift/widgets.py +260 -0
  25. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/processing.py +0 -117
  26. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/results.py +122 -22
  27. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/multi_viewer.py +10 -3
  28. microeye-2.4.63/src/microEye/analysis/processing/frc.py +363 -0
  29. microeye-2.4.63/src/microEye/analysis/rendering/__init__.py +9 -0
  30. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/rendering/base.py +101 -14
  31. microeye-2.4.63/src/microEye/analysis/rendering/navigator.py +48 -0
  32. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/batch.py +125 -101
  33. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/utils/__init__.py +4 -1
  34. microeye-2.4.63/src/microEye/analysis/utils/coordinates.py +40 -0
  35. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/localizations.py +502 -360
  36. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/mieye/miEye.py +1 -1
  37. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/misc/reglo.py +3 -1
  38. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/launcher.py +32 -7
  39. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/parameter_tree.py +5 -7
  40. {microeye-2.4.62 → microeye-2.4.63/src/microeye.egg-info}/PKG-INFO +24 -6
  41. {microeye-2.4.62 → microeye-2.4.63}/src/microeye.egg-info/SOURCES.txt +18 -3
  42. {microeye-2.4.62 → microeye-2.4.63}/src/microeye.egg-info/requires.txt +2 -0
  43. microeye-2.4.62/src/microEye/analysis/clustering/plot.py +0 -1401
  44. microeye-2.4.62/src/microEye/analysis/clustering/rulers.py +0 -425
  45. microeye-2.4.62/src/microEye/analysis/processing/frc.py +0 -203
  46. microeye-2.4.62/src/microEye/analysis/rendering/__init__.py +0 -3
  47. microeye-2.4.62/src/microEye/analysis/utils/coordinates.py +0 -18
  48. {microeye-2.4.62 → microeye-2.4.63}/LICENSE +0 -0
  49. {microeye-2.4.62 → microeye-2.4.63}/MANIFEST.in +0 -0
  50. {microeye-2.4.62 → microeye-2.4.63}/pyproject.toml +0 -0
  51. {microeye-2.4.62 → microeye-2.4.63}/setup.cfg +0 -0
  52. {microeye-2.4.62 → microeye-2.4.63}/setup.py +0 -0
  53. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/__init__.py +0 -0
  54. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/checklist_dialog.py +0 -0
  55. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/clustering/__init__.py +0 -0
  56. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/cmosMaps.py +0 -0
  57. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/file_system.py +0 -0
  58. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/filters/__init__.py +0 -0
  59. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/filters/base.py +0 -0
  60. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/filters/spatial.py +0 -0
  61. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/filters/temporal.py +0 -0
  62. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/__init__.py +0 -0
  63. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/fit.py +0 -0
  64. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/nena.py +0 -0
  65. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/phasor_fit.py +0 -0
  66. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/__init__.py +0 -0
  67. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/extract.py +0 -0
  68. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/rubost_mean.py +0 -0
  69. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/spline.py +0 -0
  70. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/stats/__init__.py +0 -0
  71. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/stats/core.py +0 -0
  72. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/stats/curve_fit.py +0 -0
  73. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/stats/io.py +0 -0
  74. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/stats/slope_fit.py +0 -0
  75. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/temp.py +0 -0
  76. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/psf/test.py +0 -0
  77. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +0 -0
  78. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +0 -0
  79. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +0 -0
  80. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +0 -0
  81. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +0 -0
  82. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +0 -0
  83. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +0 -0
  84. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +0 -0
  85. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +0 -0
  86. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/__init__.py +0 -0
  87. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/constants.py +0 -0
  88. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +0 -0
  89. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/results_stats.py +0 -0
  90. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/fitting/tardis.py +0 -0
  91. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/processing/__init__.py +0 -0
  92. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/rendering/cloud.py +0 -0
  93. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/rendering/core.py +0 -0
  94. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/rendering/volumetric.py +0 -0
  95. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/__init__.py +0 -0
  96. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/common.py +0 -0
  97. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/__init__.py +0 -0
  98. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/analysis.py +0 -0
  99. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/compare.py +0 -0
  100. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/constants.py +0 -0
  101. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/core.py +0 -0
  102. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/io.py +0 -0
  103. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/dark_cal/plotting.py +0 -0
  104. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/kymograms.py +0 -0
  105. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/__init__.py +0 -0
  106. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/analysis.py +0 -0
  107. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/compare.py +0 -0
  108. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/constants.py +0 -0
  109. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/core.py +0 -0
  110. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/io.py +0 -0
  111. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/models.py +0 -0
  112. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/plotting.py +0 -0
  113. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/photon_cal/table.py +0 -0
  114. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/registration.py +0 -0
  115. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/roi_selectors.py +0 -0
  116. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/tools/sphere_cal.py +0 -0
  117. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/utils/images.py +0 -0
  118. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/utils/windows.py +0 -0
  119. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/__init__.py +0 -0
  120. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/image_options_widget.py +0 -0
  121. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/images.py +0 -0
  122. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/layers_widget.py +0 -0
  123. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/psf.py +0 -0
  124. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/analysis/viewer/volume.py +0 -0
  125. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/__init__.py +0 -0
  126. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/__init__.py +0 -0
  127. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/basler/__init__.py +0 -0
  128. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/basler/basler_cam.py +0 -0
  129. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/basler/basler_panel.py +0 -0
  130. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/camera_calibration.py +0 -0
  131. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/camera_list.py +0 -0
  132. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/camera_options.py +0 -0
  133. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/camera_panel.py +0 -0
  134. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/__init__.py +0 -0
  135. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/dcam.py +0 -0
  136. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/dcam_panel.py +0 -0
  137. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/dcamapi4.py +0 -0
  138. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/funcs.py +0 -0
  139. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dcam/meta.py +0 -0
  140. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dummy/__init__.py +0 -0
  141. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/dummy/dummy_panel.py +0 -0
  142. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/jobs.py +0 -0
  143. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/line_profiler.py +0 -0
  144. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/linescan/IR_Cam.py +0 -0
  145. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/linescan/__init__.py +0 -0
  146. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/micam.py +0 -0
  147. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pco/__init__.py +0 -0
  148. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pco/enums.py +0 -0
  149. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pco/pco_cam.py +0 -0
  150. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pco/pco_panel.py +0 -0
  151. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/peak/__init__.py +0 -0
  152. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/peak/peak_camera.py +0 -0
  153. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/peak/peak_panel.py +0 -0
  154. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pycromanager/__init__.py +0 -0
  155. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pycromanager/pycro_cam.py +0 -0
  156. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/pycromanager/pycro_panel.py +0 -0
  157. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/shortcuts.py +0 -0
  158. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/thorlabs/__init__.py +0 -0
  159. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/thorlabs/thorlabs.py +0 -0
  160. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/thorlabs/thorlabs_panel.py +0 -0
  161. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/ueye/__init__.py +0 -0
  162. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/ueye/ueye_camera.py +0 -0
  163. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/ueye/ueye_panel.py +0 -0
  164. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/vimba/__init__.py +0 -0
  165. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/vimba/vimba_cam.py +0 -0
  166. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/cams/vimba/vimba_panel.py +0 -0
  167. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/device.py +0 -0
  168. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/lasers/__init__.py +0 -0
  169. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/lasers/io_matchbox.py +0 -0
  170. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/lasers/io_params.py +0 -0
  171. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/lasers/io_single_laser.py +0 -0
  172. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/lasers/laser_relay.py +0 -0
  173. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/mieye/__init__.py +0 -0
  174. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/mieye/acquisition_manager.py +0 -0
  175. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/mieye/devices_manager.py +0 -0
  176. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/misc/__init__.py +0 -0
  177. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/misc/acquisition_view.py +0 -0
  178. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/misc/temp.py +0 -0
  179. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/port_config.py +0 -0
  180. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/__init__.py +0 -0
  181. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/actions.py +0 -0
  182. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/actions_items.py +0 -0
  183. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/designer.py +0 -0
  184. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/scene_manager.py +0 -0
  185. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/protocols/serialization.py +0 -0
  186. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/__init__.py +0 -0
  187. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/core.py +0 -0
  188. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/devices.py +0 -0
  189. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/enums.py +0 -0
  190. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/headless.py +0 -0
  191. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/utils.py +0 -0
  192. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/widgets/__init__.py +0 -0
  193. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/widgets/bridges.py +0 -0
  194. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/widgets/headless_manager.py +0 -0
  195. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/pycromanager/widgets/headless_options.py +0 -0
  196. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/__init__.py +0 -0
  197. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/__init__.py +0 -0
  198. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/baseDevice.py +0 -0
  199. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/device.py +0 -0
  200. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/deviceID.py +0 -0
  201. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/devicePort.py +0 -0
  202. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/deviceStatus.py +0 -0
  203. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/devicesView.py +0 -0
  204. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/ellDevices.py +0 -0
  205. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/messageUpdater.py +0 -0
  206. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/motorInfo.py +0 -0
  207. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/stage.py +0 -0
  208. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/elliptec/test.py +0 -0
  209. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/__init__.py +0 -0
  210. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/kdc101/__init__.py +0 -0
  211. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/kdc101/enums.py +0 -0
  212. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/kdc101/factory.py +0 -0
  213. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/kdc101/kdc101.py +0 -0
  214. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/kinesis/kinesis.py +0 -0
  215. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/manager.py +0 -0
  216. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/piezon_concept/__init__.py +0 -0
  217. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/piezon_concept/foc.py +0 -0
  218. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/pycromanager/__init__.py +0 -0
  219. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/pycromanager/core.py +0 -0
  220. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/smaract/__init__.py +0 -0
  221. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/smaract/mcs2.py +0 -0
  222. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/stabilization/__init__.py +0 -0
  223. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/stabilization/controller.py +0 -0
  224. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/stabilization/methods.py +0 -0
  225. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/stabilizer.py +0 -0
  226. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/stage.py +0 -0
  227. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/stages/view.py +0 -0
  228. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/__init__.py +0 -0
  229. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/controller.py +0 -0
  230. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/devices.py +0 -0
  231. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/focusWidget.py +0 -0
  232. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/indicator.py +0 -0
  233. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/qlist_slider.py +0 -0
  234. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/scan_acquisition.py +0 -0
  235. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/hardware/widgets/slides.py +0 -0
  236. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/1024.png +0 -0
  237. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/128.png +0 -0
  238. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/16.png +0 -0
  239. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/24.png +0 -0
  240. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/256.png +0 -0
  241. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/32.png +0 -0
  242. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/48.png +0 -0
  243. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/512.png +0 -0
  244. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/64.png +0 -0
  245. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/__init__.py +0 -0
  246. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/close.svg +0 -0
  247. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/mieye.png +0 -0
  248. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/min.svg +0 -0
  249. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/icons/viewer.png +0 -0
  250. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/images/__init__.py +0 -0
  251. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/images/handlers.py +0 -0
  252. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/images/uImage.py +0 -0
  253. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/images/zarr.py +0 -0
  254. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/qt.py +0 -0
  255. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/tools/__init__.py +0 -0
  256. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/tools/color.py +0 -0
  257. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/tools/microscopy.py +0 -0
  258. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/__init__.py +0 -0
  259. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/display.py +0 -0
  260. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/enum_encoder.py +0 -0
  261. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/expandable_groupbox.py +0 -0
  262. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/gui_helper.py +0 -0
  263. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/hid_utils/__init__.py +0 -0
  264. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/hid_utils/controller.py +0 -0
  265. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/hid_utils/device.py +0 -0
  266. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/hid_utils/enums.py +0 -0
  267. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/hid_utils/utils.py +0 -0
  268. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/labelled_slider.py +0 -0
  269. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/metadata.py +0 -0
  270. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/metadata_tree.py +0 -0
  271. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/micro_launcher.py +0 -0
  272. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/pyqt2mplt.py +0 -0
  273. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/pyscripting.py +0 -0
  274. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/retry_exec.py +0 -0
  275. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/start_gui.py +0 -0
  276. {microeye-2.4.62 → microeye-2.4.63}/src/microEye/utils/thread_worker.py +0 -0
  277. {microeye-2.4.62 → microeye-2.4.63}/src/microeye.egg-info/dependency_links.txt +0 -0
  278. {microeye-2.4.62 → microeye-2.4.63}/src/microeye.egg-info/entry_points.txt +0 -0
  279. {microeye-2.4.62 → microeye-2.4.63}/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.62
3
+ Version: 2.4.63
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
@@ -14,6 +14,7 @@ Requires-Python: >=3.9.4, <3.12
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: autopep8==2.3.2
17
+ Requires-Dist: cupy-cuda13x==14.2.0
17
18
  Requires-Dist: dask==2025.9.1
18
19
  Requires-Dist: h5py==3.15.0
19
20
  Requires-Dist: hdbscan==0.8.44
@@ -22,6 +23,7 @@ Requires-Dist: ids-peak==1.11.1.0.5
22
23
  Requires-Dist: matplotlib==3.10.7
23
24
  Requires-Dist: mmpycorex==0.3.16
24
25
  Requires-Dist: numba==0.62.1
26
+ Requires-Dist: numba-cuda==0.30.4
25
27
  Requires-Dist: numpy==2.2.6
26
28
  Requires-Dist: ome_types==0.5.2
27
29
  Requires-Dist: opencv_python==4.12.0.88
@@ -69,11 +71,15 @@ Dynamic: summary
69
71
  <!-- ![microEye](https://github.com/samhitech/microEye/assets/89871015/37ca0859-0b4b-402d-8652-9a01d0bf0f29) -->
70
72
 
71
73
  ```bash
72
- __ ____ ____ ___ ____ ____ ___
73
- / |/ (_)__________ / __/_ _____ _ __|_ |/ / / / __/|_ |
74
- / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _// _ \/ __/
75
- /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)___/____/
76
- /___/
74
+ ╭─────────────────────────────────────────────────────────────────────╮
75
+ │ │
76
+ │ __ ____ ____ ___ ____ ____ ____ │
77
+ │ / |/ (_)__________ / __/_ _____ _ __|_ |/ / / / __/|_ / │
78
+ │ / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _// _ \_/_ < │
79
+ │ /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)___/____/ │
80
+ │ /___/ │
81
+ │ │
82
+ ╰─────────────────────────────────────────────────────────────────────╯
77
83
  ```
78
84
 
79
85
  The **`microEye`** is a Python toolkit for fluorescence microscopy that supports super-resolution single-molecule localization microscopy and single-particle tracking. It features hardware control, data analysis, and visualization.
@@ -597,6 +603,18 @@ Special thanks to the following projects and libraries that make this work possi
597
603
 
598
604
  > _Martens et al., “Temporal analysis of relative distances (TARDIS) is a robust, parameter-free alternative to single-particle tracking”, Nature Methods (2024). [Article](https://rdcu.be/dv1sr)_
599
605
 
606
+ - **RCC (Robust Cross-Correlation)**: We have implemented the RCC algorithm in Python for drift correction in SMLM data.
607
+
608
+ > _Y. Wang, J. Schnitzbauer, Z. Hu, X. Li, Y. Cheng, Z.-L. Huang, and B. Huang, “Localization events-based sample drift correction for localization microscopy with redundant cross-correlation algorithm,” Opt. Express 22(13), 15982 (2014)._ [Paper](https://doi.org/10.1364/OE.22.015982) [GitHub](https://github.com/yinawang28/RCC)
609
+
610
+ - **AIM (Adaptive Intersection Maximization)**: We have implemented the AIM algorithm in Python for drift correction in SMLM data.
611
+
612
+ > _H. Ma, M. Chen, P. Nguyen, and Y. Liu, “Toward drift-free high-throughput nanoscopy through adaptive intersection maximization,” Sci. Adv. 10(21), eadm7765 (2024)._ [Paper](www.science.org/doi/10.1126/sciadv.adm7765) [GitHub](https://github.com/YangLiuLab/AIM)
613
+
614
+ - **COMET (Cost-function Optimized Maximal overlap drift EsTimation)**: We have implemented the COMET algorithm in Python for drift correction in SMLM data.
615
+
616
+ > _L. Reinkensmeier, S. Aufmkolk, I. Farabella, A. Egner, and M. Bates, “Cost-function Optimized Maximal Overlap Drift Estimation for Single Molecule Localization Microscopy,” (2026)._ [Paper](https://doi.org/10.64898/2026.03.27.714864) [GitHub](https://github.com/gpufit/Comet/tree/master)
617
+
600
618
  > [!IMPORTANT]
601
619
  > **Disclaimer**
602
620
  > _I'm committed to maintaining an accurate acknowledgment list for our project. However, if I inadvertently miss acknowledging your work, please don't hesitate to reach out to us. I appreciate your understanding, and I'm doing my best to manage all acknowledgments amidst my other responsibilities._
@@ -3,11 +3,15 @@
3
3
  <!-- ![microEye](https://github.com/samhitech/microEye/assets/89871015/37ca0859-0b4b-402d-8652-9a01d0bf0f29) -->
4
4
 
5
5
  ```bash
6
- __ ____ ____ ___ ____ ____ ___
7
- / |/ (_)__________ / __/_ _____ _ __|_ |/ / / / __/|_ |
8
- / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _// _ \/ __/
9
- /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)___/____/
10
- /___/
6
+ ╭─────────────────────────────────────────────────────────────────────╮
7
+ │ │
8
+ │ __ ____ ____ ___ ____ ____ ____ │
9
+ │ / |/ (_)__________ / __/_ _____ _ __|_ |/ / / / __/|_ / │
10
+ │ / /|_/ / / __/ __/ _ \/ _// // / -_) | |/ / __//_ _// _ \_/_ < │
11
+ │ /_/ /_/_/\__/_/ \___/___/\_, /\__/ |___/____(_)_/(_)___/____/ │
12
+ │ /___/ │
13
+ │ │
14
+ ╰─────────────────────────────────────────────────────────────────────╯
11
15
  ```
12
16
 
13
17
  The **`microEye`** is a Python toolkit for fluorescence microscopy that supports super-resolution single-molecule localization microscopy and single-particle tracking. It features hardware control, data analysis, and visualization.
@@ -531,6 +535,18 @@ Special thanks to the following projects and libraries that make this work possi
531
535
 
532
536
  > _Martens et al., “Temporal analysis of relative distances (TARDIS) is a robust, parameter-free alternative to single-particle tracking”, Nature Methods (2024). [Article](https://rdcu.be/dv1sr)_
533
537
 
538
+ - **RCC (Robust Cross-Correlation)**: We have implemented the RCC algorithm in Python for drift correction in SMLM data.
539
+
540
+ > _Y. Wang, J. Schnitzbauer, Z. Hu, X. Li, Y. Cheng, Z.-L. Huang, and B. Huang, “Localization events-based sample drift correction for localization microscopy with redundant cross-correlation algorithm,” Opt. Express 22(13), 15982 (2014)._ [Paper](https://doi.org/10.1364/OE.22.015982) [GitHub](https://github.com/yinawang28/RCC)
541
+
542
+ - **AIM (Adaptive Intersection Maximization)**: We have implemented the AIM algorithm in Python for drift correction in SMLM data.
543
+
544
+ > _H. Ma, M. Chen, P. Nguyen, and Y. Liu, “Toward drift-free high-throughput nanoscopy through adaptive intersection maximization,” Sci. Adv. 10(21), eadm7765 (2024)._ [Paper](www.science.org/doi/10.1126/sciadv.adm7765) [GitHub](https://github.com/YangLiuLab/AIM)
545
+
546
+ - **COMET (Cost-function Optimized Maximal overlap drift EsTimation)**: We have implemented the COMET algorithm in Python for drift correction in SMLM data.
547
+
548
+ > _L. Reinkensmeier, S. Aufmkolk, I. Farabella, A. Egner, and M. Bates, “Cost-function Optimized Maximal Overlap Drift Estimation for Single Molecule Localization Microscopy,” (2026)._ [Paper](https://doi.org/10.64898/2026.03.27.714864) [GitHub](https://github.com/gpufit/Comet/tree/master)
549
+
534
550
  > [!IMPORTANT]
535
551
  > **Disclaimer**
536
552
  > _I'm committed to maintaining an accurate acknowledgment list for our project. However, if I inadvertently miss acknowledging your work, please don't hesitate to reach out to us. I appreciate your understanding, and I'm doing my best to manage all acknowledgments amidst my other responsibilities._
@@ -1,7 +1,12 @@
1
+ import argparse
1
2
  import importlib.util
2
3
  import logging
3
4
  import os
4
5
 
6
+ from rich.console import Console
7
+ from rich.logging import RichHandler
8
+ from rich.traceback import install
9
+
5
10
  from microEye._version import __version__, version_info
6
11
 
7
12
  # QT bindings affect camera libraries' DLL loading.
@@ -11,6 +16,8 @@ from microEye.hardware import cams
11
16
 
12
17
  PREFERRED_QT_APIS = ('PySide6', 'PyQt6', 'PyQt5')
13
18
 
19
+ HELP_MESSAGE = ''
20
+
14
21
 
15
22
  def _select_qt_api(cli_choice: str | None) -> str:
16
23
  candidates = (cli_choice,) + PREFERRED_QT_APIS if cli_choice else PREFERRED_QT_APIS
@@ -23,11 +30,16 @@ def _select_qt_api(cli_choice: str | None) -> str:
23
30
 
24
31
 
25
32
  def getArgs():
26
- import argparse
27
33
  import os
34
+ import re
28
35
  import sys
29
36
 
30
- parser = argparse.ArgumentParser()
37
+ parser = argparse.ArgumentParser(
38
+ add_help=False,
39
+ )
40
+ parser.add_argument(
41
+ '-h', '--help', action='store_true', help='Show this help message and exit.'
42
+ )
31
43
  parser.add_argument(
32
44
  '--module',
33
45
  help='The module to launch [mieye|viewer], '
@@ -63,6 +75,34 @@ def getArgs():
63
75
  action='store_true',
64
76
  )
65
77
 
78
+ lines = []
79
+ for line in parser.format_help().splitlines():
80
+ # format flags (cyan)
81
+ line = re.sub(r'(?<=\s|\[)(-\S+)', r'[bold #00d7ff]\1[/bold #00d7ff]', line)
82
+ line = re.sub(r'(?<=\s|\[)(--\S+)', r'[bold #00d7ff]\1[/bold #00d7ff]', line)
83
+
84
+ # format scripts & files (warm yellow)
85
+ line = re.sub(r'(?<=\s)(\S+\.py)', r'[#ffb86c]\1[/#ffb86c]', line)
86
+ line = re.sub(r'(?<=\s)(\S+\.exe)', r'[#ffb86c]\1[/#ffb86c]', line)
87
+ line = re.sub(r'(?<=\s)(\S+\.dll)', r'[#ffb86c]\1[/#ffb86c]', line)
88
+ line = re.sub(r'(?<=\s)(\S+\.so)', r'[#ffb86c]\1[/#ffb86c]', line)
89
+ line = re.sub(r'(?<=\s)(\S+\.dylib)', r'[#ffb86c]\1[/#ffb86c]', line)
90
+
91
+ # format curly braces (bright green)
92
+ line = re.sub(r'(?<=\s)(\{.*?\})', r'[bold #5af78e]\1[/bold #5af78e]', line)
93
+
94
+ # format flags within brackets (cyan)
95
+ line = re.sub(r'(?<=\[)(--\S+)', r'[bold #00d7ff]\1[/bold #00d7ff]', line)
96
+ line = re.sub(r'(?<=\[)(-\S+)', r'[bold #00d7ff]\1[/bold #00d7ff]', line)
97
+
98
+ line = line.replace('usage:', '[bold #dcdcaa]usage:[/bold #dcdcaa]')
99
+ line = line.replace('options:', '[bold #dcdcaa]options:[/bold #dcdcaa]')
100
+
101
+ lines.append(line)
102
+
103
+ global HELP_MESSAGE
104
+ HELP_MESSAGE = '\n'.join(lines) + '\n'
105
+
66
106
  args, _ = parser.parse_known_args(sys.argv)
67
107
 
68
108
  # access the parsed arguments
@@ -75,6 +115,7 @@ def getArgs():
75
115
 
76
116
  return args
77
117
 
118
+
78
119
  def configure_logger(args):
79
120
  import os
80
121
  import sys
@@ -93,15 +134,31 @@ def configure_logger(args):
93
134
  and args.module
94
135
  ):
95
136
  os.makedirs(os.path.dirname(log_filename))
137
+
138
+ if not args.no_log_file and args.module:
139
+ console = Console(
140
+ file=open(log_filename, 'a', encoding='utf-8') # noqa: SIM115, W291
141
+ )
142
+ file_handler = RichHandler(
143
+ console=console,
144
+ rich_tracebacks=True,
145
+ tracebacks_show_locals=True,
146
+ markup=True,
147
+ )
148
+ else:
149
+ file_handler = logging.NullHandler()
150
+
96
151
  # set the logging configuration with both file and console handlers
97
152
  logging.basicConfig(
98
153
  level=level,
99
- format='%(asctime)s | %(name)s | %(levelname)s | %(message)s',
154
+ format='%(message)s',
100
155
  handlers=[
101
- logging.FileHandler(log_filename)
102
- if not args.no_log_file and args.module
103
- else logging.NullHandler(),
104
- logging.StreamHandler(sys.stdout)
156
+ file_handler,
157
+ RichHandler(
158
+ rich_tracebacks=True,
159
+ tracebacks_show_locals=True,
160
+ markup=True,
161
+ )
105
162
  if not args.no_log_console
106
163
  else logging.NullHandler(),
107
164
  ],
@@ -110,5 +167,10 @@ def configure_logger(args):
110
167
  logging.getLogger().debug('Log level set to %s', level_name)
111
168
 
112
169
 
113
-
170
+ install(
171
+ show_locals=False,
172
+ code_width=150,
173
+ width=200,
174
+ extra_lines=2,
175
+ )
114
176
  ARGS = getArgs()
@@ -1,2 +1,2 @@
1
- version_info = (2, 4, 62)
1
+ version_info = (2, 4, 63)
2
2
  __version__ = '.'.join(map(str, version_info))
@@ -1,33 +1,17 @@
1
1
  import logging
2
- from dataclasses import dataclass
3
- from enum import Enum
4
2
 
5
3
  import numba as nb
6
4
  import numpy as np
7
5
  from hdbscan import HDBSCAN
8
- from scipy.spatial import ConvexHull, Delaunay, Voronoi
9
6
  from sklearn.cluster import DBSCAN, OPTICS, cluster_optics_dbscan
10
- from sklearn.preprocessing import StandardScaler
11
-
12
- logger = logging.getLogger(__name__)
13
7
 
8
+ from microEye.analysis.clustering.dataclasses import (
9
+ CenteringMode,
10
+ CenteringOptions,
11
+ ClusterCenterResult,
12
+ )
14
13
 
15
- @nb.njit(cache=True)
16
- def geometric_median(points, max_iter=128, tol=1e-6, eps=1e-12):
17
- guess = np.median(points, axis=0)
18
- for _ in range(max_iter):
19
- try:
20
- deltas = points - guess
21
- distances = np.linalg.norm(deltas, axis=1)
22
- weights = 1.0 / np.maximum(distances, eps)
23
- new_guess = np.sum(points * weights[:, None], axis=0) / np.sum(weights)
24
- if np.linalg.norm(new_guess - guess) < tol:
25
- return new_guess
26
- guess = new_guess
27
- except Exception as e:
28
- logger.error(f'Error in geometric_median: {e}')
29
- break
30
- return guess
14
+ logger = logging.getLogger(__name__)
31
15
 
32
16
 
33
17
  @nb.njit(fastmath=True, cache=True)
@@ -159,11 +143,16 @@ def huber_center(points, delta=2.0, max_iter=64, tol=1e-5):
159
143
  return guess
160
144
 
161
145
 
162
- class CenteringMode(Enum):
163
- CRLB_WEIGHTED_MEAN = 'crlb_weighted_mean'
164
- MEAN = 'mean'
165
- GEOMETRIC_MEDIAN = 'geometric_median'
166
- HUBER_CENTER = 'huber_center'
146
+ def scale_data(x, y, z, sigma_xy=10.0, sigma_z=10.0):
147
+ '''
148
+ Scales the input coordinates for clustering algorithms
149
+ that require isotropic distances.
150
+ '''
151
+ if z is None:
152
+ return np.column_stack((x, y)), 1.0
153
+ else:
154
+ scale = sigma_xy / sigma_z
155
+ return np.column_stack((x, y, z * scale)), scale
167
156
 
168
157
 
169
158
  def get_center(
@@ -178,13 +167,14 @@ def get_center(
178
167
  '''
179
168
  Computes the center of clusters based on the specified centering mode.
180
169
 
181
- *Methods*
170
+ **Methods**
182
171
 
183
- - CRLB_WEIGHTED_MEAN: Computes the center using a weighted mean based on the
172
+ - **_CRLB_WEIGHTED_MEAN_**: Computes the center using a weighted mean based on the
184
173
  Cramer-Rao Lower Bound (CRLB) variances for x and y coordinates.
185
- - GEOMETRIC_MEDIAN: Computes the center as the geometric median of the points.
186
- - HUBER_CENTER: Computes the center using M-estimation with a Huber loss function.
187
- - MEAN: Computes the center as the arithmetic mean of the points.
174
+ - **_GEOMETRIC_MEDIAN_**: Computes the center as the geometric median of the points.
175
+ - **_HUBER_CENTER_**: Computes the center using
176
+ M-estimation with a Huber loss function.
177
+ - **_MEAN_**: Computes the center as the arithmetic mean of the points.
188
178
 
189
179
  Parameters
190
180
  -----------
@@ -261,24 +251,6 @@ def get_center(
261
251
  return mu_x, mu_y, mu_z, sigma_com
262
252
 
263
253
 
264
- @dataclass
265
- class ClusterCenterResult:
266
- x: np.ndarray
267
- y: np.ndarray
268
- z: np.ndarray | None
269
- labels: np.ndarray
270
- intensity: np.ndarray | None
271
- crlb_x: np.ndarray | None
272
- crlb_y: np.ndarray | None
273
- cx: np.ndarray
274
- cy: np.ndarray
275
- cz: np.ndarray | None
276
- c_sigma: np.ndarray | None
277
- c_label: np.ndarray
278
- n_points: np.ndarray
279
- rejected: int
280
-
281
-
282
254
  def get_centers(
283
255
  x: np.ndarray,
284
256
  y: np.ndarray,
@@ -287,14 +259,19 @@ def get_centers(
287
259
  intensity: np.ndarray | None = None,
288
260
  crlb_x: np.ndarray | None = None,
289
261
  crlb_y: np.ndarray | None = None,
290
- center_mode: CenteringMode = CenteringMode.CRLB_WEIGHTED_MEAN,
291
- eps: float = 1e-12,
292
- rejection_threshold: float | None = None,
293
- min_points: int | None = None,
294
- max_points: int | None = None,
295
- subtract_com: bool = False,
296
- **kwargs,
262
+ options: CenteringOptions | None = None,
297
263
  ) -> ClusterCenterResult | None:
264
+ if options is None:
265
+ options = CenteringOptions()
266
+
267
+ rejection_threshold = options.rejection_threshold or None
268
+ min_points = (
269
+ options.min_points
270
+ or (options.min_points is not None and options.min_points > 1)
271
+ or None
272
+ )
273
+ max_points = options.max_points or None
274
+
298
275
  try:
299
276
  # Filter out noise (label < 0) safely
300
277
  valid_mask = labels >= 0
@@ -353,10 +330,10 @@ def get_centers(
353
330
  x_cluster,
354
331
  y_cluster,
355
332
  z_cluster,
356
- center_mode=center_mode,
333
+ center_mode=options.center_mode,
357
334
  crlb_x=crlb_x[s] if crlb_x is not None else None,
358
335
  crlb_y=crlb_y[s] if crlb_y is not None else None,
359
- eps=eps,
336
+ eps=options.eps,
360
337
  )
361
338
 
362
339
  # Reject clusters with large uncertainty
@@ -376,7 +353,7 @@ def get_centers(
376
353
  c_labels.append(i)
377
354
  n_points.append(len(x_cluster))
378
355
 
379
- if subtract_com:
356
+ if options.subtract_com:
380
357
  x[s] -= mu_x
381
358
  y[s] -= mu_y
382
359
  if z is not None:
@@ -419,14 +396,6 @@ def get_centers(
419
396
  return None
420
397
 
421
398
 
422
- def scale_data(x, y, z, sigma_xy=10.0, sigma_z=10.0):
423
- if z is None:
424
- return np.column_stack((x, y)), 1.0
425
- else:
426
- scale = sigma_xy / sigma_z
427
- return np.column_stack((x, y, z * scale)), scale
428
-
429
-
430
399
  def perform_dbscan_clustering(X, Y, Z, eps=50, min_samples=2, **kwargs):
431
400
  '''
432
401
  Performs DBSCAN clustering on 3D localization data.
@@ -456,7 +425,10 @@ def perform_dbscan_clustering(X, Y, Z, eps=50, min_samples=2, **kwargs):
456
425
  else:
457
426
  logger.warning('Standard deviation of data is zero. Skipping scaling.')
458
427
 
459
- dbscan_model = DBSCAN(eps=eps, min_samples=min_samples, **kwargs)
428
+ # remove any not need kwargs that are not relevant to DBSCAN
429
+ relevant_kwargs = {k: v for k, v in kwargs.items() if k in DBSCAN().get_params()}
430
+
431
+ dbscan_model = DBSCAN(eps=eps, min_samples=min_samples, **relevant_kwargs)
460
432
  dbscan_model.fit(VECTORS)
461
433
 
462
434
  labels = dbscan_model.labels_
@@ -483,6 +455,10 @@ def perform_hdbscan_clustering(X, Y, Z, **kwargs):
483
455
  '''
484
456
  sigma_xy = kwargs.pop('sigma_xy', 10.0)
485
457
  sigma_z = kwargs.pop('sigma_z', 10.0)
458
+ min_samples = kwargs.pop('min_samples', None)
459
+ min_samples = min_samples or (min_samples is not None and min_samples > 0) or None
460
+ if min_samples:
461
+ kwargs['min_samples'] = min_samples
486
462
 
487
463
  VECTORS, scale = scale_data(X, Y, Z, sigma_xy, sigma_z)
488
464
 
@@ -544,6 +520,9 @@ def perform_optics_clustering(X, Y, Z, min_samples=10, xi=0.05, **kwargs):
544
520
  '''
545
521
  sigma_xy = kwargs.pop('sigma_xy', 10.0)
546
522
  sigma_z = kwargs.pop('sigma_z', 10.0)
523
+ max_eps = kwargs.pop('max_eps', np.inf)
524
+ max_eps = max_eps or (max_eps is not None and max_eps > 0) or np.inf
525
+ kwargs['max_eps'] = max_eps
547
526
 
548
527
  VECTORS, scale = scale_data(X, Y, Z, sigma_xy, sigma_z)
549
528
 
@@ -0,0 +1,212 @@
1
+ from dataclasses import asdict, dataclass, field
2
+ from enum import StrEnum
3
+
4
+ import numpy as np
5
+ import pyqtgraph as pg
6
+
7
+ from microEye.analysis.rendering.base import RenderModes
8
+
9
+
10
+ class CenteringMode(StrEnum):
11
+ CRLB_WEIGHTED_MEAN = 'crlb_weighted_mean'
12
+ MEAN = 'mean'
13
+ GEOMETRIC_MEDIAN = 'geometric_median'
14
+ HUBER_CENTER = 'huber_center'
15
+
16
+
17
+ @dataclass
18
+ class CenteringOptions:
19
+ center_mode: CenteringMode = CenteringMode.CRLB_WEIGHTED_MEAN
20
+ eps: float = 1e-12
21
+ rejection_threshold: float | None = None
22
+ min_points: int | None = None
23
+ max_points: int | None = None
24
+ subtract_com: bool = False
25
+
26
+ def __post_init__(self):
27
+ if isinstance(self.center_mode, str):
28
+ self.center_mode = CenteringMode(self.center_mode)
29
+
30
+
31
+ @dataclass
32
+ class ClusterCenterResult:
33
+ x: np.ndarray
34
+ y: np.ndarray
35
+ z: np.ndarray | None
36
+ labels: np.ndarray
37
+ intensity: np.ndarray | None
38
+ crlb_x: np.ndarray | None
39
+ crlb_y: np.ndarray | None
40
+ cx: np.ndarray
41
+ cy: np.ndarray
42
+ cz: np.ndarray | None
43
+ c_sigma: np.ndarray | None
44
+ c_label: np.ndarray
45
+ n_points: np.ndarray
46
+ rejected: int
47
+
48
+
49
+ @dataclass
50
+ class ClusteringOptions:
51
+ grouping_method: str = 'dbscan'
52
+ distance_threshold: float | None = None
53
+ sigma_xy: float = 15.0
54
+ sigma_z: float = 45.0
55
+ scale_data: bool = False
56
+ xi : float = 0.05
57
+
58
+
59
+ @dataclass
60
+ class RenderOptions:
61
+ mode: str = 'superimposed'
62
+ rasterized: bool = True
63
+ render_mode: RenderModes = RenderModes.HISTOGRAM
64
+ pixel_size: float = 1.0
65
+ extent: float = 200.0
66
+ scalebar_length: float = 80.0
67
+
68
+ def __post_init__(self):
69
+ if isinstance(self.render_mode, int):
70
+ self.render_mode = RenderModes(self.render_mode)
71
+
72
+
73
+ @dataclass
74
+ class RunOptions:
75
+ clustering: ClusteringOptions
76
+ rendering: RenderOptions
77
+ model: dict
78
+ export: dict
79
+
80
+ def __post_init__(self):
81
+ if isinstance(self.clustering, dict):
82
+ self.clustering = ClusteringOptions(**self.clustering)
83
+ if isinstance(self.rendering, dict):
84
+ self.rendering = RenderOptions(**self.rendering)
85
+
86
+
87
+ @dataclass
88
+ class FitResult:
89
+ '''Generic outcome of fitting ANY geometric model to a candidate site set.'''
90
+
91
+ R: np.ndarray
92
+ '''
93
+ The rotation matrix that transforms the raw coordinates into the canonical frame.
94
+ '''
95
+ origin: np.ndarray
96
+ '''
97
+ The origin of the canonical frame, expressed in raw coordinates.
98
+ '''
99
+ inlier_mask: np.ndarray
100
+ '''
101
+ A boolean array (K,) indicating which input sites were used in the fit.
102
+ '''
103
+ indices: np.ndarray
104
+ '''
105
+ The integer indices (n_inliers, d) of the canonical positions
106
+ corresponding to each inlier site.
107
+
108
+ Examples:
109
+ - For a ruler, d=1 and the indices are the slot numbers.
110
+ - For a grid, d=2 and the indices are (row, col) pairs
111
+ - For a ring, d=1 and the indices are the angular slot numbers.
112
+ '''
113
+ n_matched: int
114
+ '''
115
+ The number of distinct canonical positions that were explained by the inlier sites.
116
+ '''
117
+ residual: float
118
+ '''
119
+ The fit residual.
120
+ '''
121
+ meta: dict = field(default_factory=dict)
122
+ '''
123
+ Model-specific metadata for logging and debugging purposes.
124
+ '''
125
+
126
+
127
+ @dataclass
128
+ class InstanceStats:
129
+ n_localizations: int
130
+ '''All input localizations, including noise.'''
131
+ n_input_clusters: int
132
+ '''Distinct non-negative labels in the original localization table.'''
133
+ n_input_cluster_localizations: int
134
+ '''Localizations belonging to those original clusters.'''
135
+
136
+ n_cluster_centers: int
137
+ '''Centers created by get_centers() after filtering.'''
138
+ n_rejected_centers: int
139
+ '''Centers rejected by center-level QC.'''
140
+
141
+ n_candidates: int
142
+ '''Candidate geometric groups produced by DBSCAN/HDBSCAN/OPTICS.'''
143
+ n_candidate_centers: int
144
+ '''Total site centers across all candidates.'''
145
+ n_rejected_candidates: int
146
+ '''Candidates where model.fit() returned None or residual QC failed.'''
147
+
148
+ n_passed_instances: int
149
+ '''Candidates that passed the geometric model.'''
150
+ n_passed_centers: int
151
+ '''Centers belonging to passed instances.'''
152
+ n_passed_localizations: int
153
+ '''Raw localizations belonging to passed instances.'''
154
+
155
+ candidate_log: list[dict] = field(default_factory=list)
156
+
157
+
158
+ @dataclass
159
+ class PlotResult:
160
+ '''
161
+ Unified return type for all ClustersScatter render methods.
162
+
163
+ raster : pg.ImageItem or None — 2D rasterized render
164
+ scatter : list[pg.ScatterPlotItem] — raw scatter layers (when not rasterized)
165
+ histogram_x : list[pg.GraphicsObject] — X-projection histogram + KDE items
166
+ histogram_y : list[pg.GraphicsObject] — Y-projection histogram + KDE items
167
+ title : str — plot title
168
+ pixel_size : float — nm per pixel (carried from rasterized_2d)
169
+ '''
170
+
171
+ raster: pg.ImageItem | None = None
172
+ scatter: list[pg.ScatterPlotItem | pg.PlotCurveItem] = field(default_factory=list)
173
+ histogram_x: list[pg.GraphicsObject] = field(default_factory=list)
174
+ histogram_y: list[pg.GraphicsObject] = field(default_factory=list)
175
+ title: str = ''
176
+ pixel_size: float = 1.0
177
+
178
+ histogram_angle: list[pg.GraphicsObject] = field(default_factory=list)
179
+ histogram_intensity: list[pg.GraphicsObject] = field(default_factory=list)
180
+ histogram_n_matched: list[pg.GraphicsObject] = field(default_factory=list)
181
+
182
+ @property
183
+ def is_rasterized(self) -> bool:
184
+ return self.raster is not None
185
+
186
+ @property
187
+ def has_histograms(self) -> bool:
188
+ return bool(self.histogram_x or self.histogram_y or self.histogram_angle)
189
+
190
+ @dataclass
191
+ class LabeledLocalizations:
192
+ x: np.ndarray
193
+ y: np.ndarray
194
+ z: np.ndarray | None
195
+ labels: np.ndarray
196
+ intensity: np.ndarray | None
197
+ crlb_x: np.ndarray | None
198
+ crlb_y: np.ndarray | None
199
+ crlb_z: np.ndarray | None
200
+
201
+ @dataclass
202
+ class ClusterRenderData:
203
+ tables: list[LabeledLocalizations]
204
+ run_meta: dict = field(default_factory=dict)
205
+
206
+ @dataclass
207
+ class PipelineResult:
208
+ stats: list[InstanceStats]
209
+ instances: list[dict]
210
+ model_meta: dict
211
+ run_meta: dict
212
+