microeye 2.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. microEye/__init__.py +47 -0
  2. microEye/_version.py +2 -0
  3. microEye/analysis/__init__.py +1 -0
  4. microEye/analysis/checklist_dialog.py +143 -0
  5. microEye/analysis/cmosMaps.py +228 -0
  6. microEye/analysis/filters/__init__.py +9 -0
  7. microEye/analysis/filters/base.py +21 -0
  8. microEye/analysis/filters/spatial.py +338 -0
  9. microEye/analysis/filters/temporal.py +76 -0
  10. microEye/analysis/fitting/__init__.py +0 -0
  11. microEye/analysis/fitting/fit.py +680 -0
  12. microEye/analysis/fitting/nena.py +375 -0
  13. microEye/analysis/fitting/phasor_fit.py +90 -0
  14. microEye/analysis/fitting/processing.py +317 -0
  15. microEye/analysis/fitting/psf/__init__.py +6 -0
  16. microEye/analysis/fitting/psf/extract.py +1129 -0
  17. microEye/analysis/fitting/psf/rubost_mean.py +150 -0
  18. microEye/analysis/fitting/psf/spline.py +167 -0
  19. microEye/analysis/fitting/psf/stats/__init__.py +12 -0
  20. microEye/analysis/fitting/psf/stats/core.py +295 -0
  21. microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
  22. microEye/analysis/fitting/psf/stats/io.py +104 -0
  23. microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
  24. microEye/analysis/fitting/psf/temp.py +147 -0
  25. microEye/analysis/fitting/psf/test.py +47 -0
  26. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
  27. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
  28. microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
  29. microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
  30. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
  31. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
  32. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
  33. microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
  34. microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
  35. microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
  36. microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
  37. microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
  38. microEye/analysis/fitting/results.py +917 -0
  39. microEye/analysis/fitting/results_stats.py +251 -0
  40. microEye/analysis/fitting/tardis.py +209 -0
  41. microEye/analysis/multi_viewer.py +568 -0
  42. microEye/analysis/processing/__init__.py +1 -0
  43. microEye/analysis/processing/frc.py +120 -0
  44. microEye/analysis/rendering/__init__.py +3 -0
  45. microEye/analysis/rendering/base.py +452 -0
  46. microEye/analysis/rendering/cloud.py +215 -0
  47. microEye/analysis/rendering/core.py +47 -0
  48. microEye/analysis/rendering/volumetric.py +198 -0
  49. microEye/analysis/tools/__init__.py +0 -0
  50. microEye/analysis/tools/kymograms.py +1079 -0
  51. microEye/analysis/tools/roi_selectors.py +556 -0
  52. microEye/analysis/utils/__init__.py +3 -0
  53. microEye/analysis/utils/coordinates.py +18 -0
  54. microEye/analysis/utils/images.py +68 -0
  55. microEye/analysis/utils/windows.py +19 -0
  56. microEye/analysis/viewer/__init__.py +3 -0
  57. microEye/analysis/viewer/image_options_widget.py +615 -0
  58. microEye/analysis/viewer/images.py +1225 -0
  59. microEye/analysis/viewer/layers_widget.py +415 -0
  60. microEye/analysis/viewer/localizations.py +1304 -0
  61. microEye/analysis/viewer/psf.py +1003 -0
  62. microEye/analysis/viewer/volume.py +456 -0
  63. microEye/hardware/__init__.py +3 -0
  64. microEye/hardware/cams/__init__.py +29 -0
  65. microEye/hardware/cams/camera_calibration.py +99 -0
  66. microEye/hardware/cams/camera_list.py +528 -0
  67. microEye/hardware/cams/camera_options.py +694 -0
  68. microEye/hardware/cams/camera_panel.py +941 -0
  69. microEye/hardware/cams/dummy/__init__.py +1 -0
  70. microEye/hardware/cams/dummy/dummy_panel.py +759 -0
  71. microEye/hardware/cams/jobs.py +497 -0
  72. microEye/hardware/cams/line_profiler.py +99 -0
  73. microEye/hardware/cams/linescan/IR_Cam.py +474 -0
  74. microEye/hardware/cams/linescan/__init__.py +1 -0
  75. microEye/hardware/cams/micam.py +524 -0
  76. microEye/hardware/cams/pco/__init__.py +58 -0
  77. microEye/hardware/cams/pco/enums.py +382 -0
  78. microEye/hardware/cams/pco/pco_cam.py +761 -0
  79. microEye/hardware/cams/pco/pco_panel.py +477 -0
  80. microEye/hardware/cams/shortcuts.py +312 -0
  81. microEye/hardware/cams/thorlabs/__init__.py +1 -0
  82. microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
  83. microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
  84. microEye/hardware/cams/ueye/__init__.py +1 -0
  85. microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
  86. microEye/hardware/cams/ueye/ueye_panel.py +861 -0
  87. microEye/hardware/cams/vimba/__init__.py +1 -0
  88. microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
  89. microEye/hardware/cams/vimba/vimba_panel.py +813 -0
  90. microEye/hardware/device.py +60 -0
  91. microEye/hardware/lasers/__init__.py +13 -0
  92. microEye/hardware/lasers/io_matchbox.py +791 -0
  93. microEye/hardware/lasers/io_params.py +85 -0
  94. microEye/hardware/lasers/io_single_laser.py +742 -0
  95. microEye/hardware/lasers/laser_relay.py +594 -0
  96. microEye/hardware/mieye/__init__.py +1 -0
  97. microEye/hardware/mieye/acquisition_manager.py +467 -0
  98. microEye/hardware/mieye/devices_manager.py +533 -0
  99. microEye/hardware/mieye/miEye.py +659 -0
  100. microEye/hardware/misc/__init__.py +0 -0
  101. microEye/hardware/misc/acquisition_view.py +71 -0
  102. microEye/hardware/misc/reglo.py +761 -0
  103. microEye/hardware/misc/temp.py +188 -0
  104. microEye/hardware/port_config.py +59 -0
  105. microEye/hardware/protocols/__init__.py +2 -0
  106. microEye/hardware/protocols/actions.py +402 -0
  107. microEye/hardware/protocols/actions_items.py +703 -0
  108. microEye/hardware/protocols/designer.py +244 -0
  109. microEye/hardware/protocols/scene_manager.py +191 -0
  110. microEye/hardware/protocols/serialization.py +97 -0
  111. microEye/hardware/pycromanager/__init__.py +16 -0
  112. microEye/hardware/pycromanager/core.py +1433 -0
  113. microEye/hardware/pycromanager/devices.py +461 -0
  114. microEye/hardware/pycromanager/enums.py +107 -0
  115. microEye/hardware/pycromanager/headless.py +153 -0
  116. microEye/hardware/pycromanager/utils.py +34 -0
  117. microEye/hardware/pycromanager/widgets/__init__.py +5 -0
  118. microEye/hardware/pycromanager/widgets/bridges.py +407 -0
  119. microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
  120. microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
  121. microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
  122. microEye/hardware/stages/__init__.py +18 -0
  123. microEye/hardware/stages/elliptec/__init__.py +5 -0
  124. microEye/hardware/stages/elliptec/baseDevice.py +314 -0
  125. microEye/hardware/stages/elliptec/device.py +384 -0
  126. microEye/hardware/stages/elliptec/deviceID.py +212 -0
  127. microEye/hardware/stages/elliptec/devicePort.py +379 -0
  128. microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
  129. microEye/hardware/stages/elliptec/devicesView.py +706 -0
  130. microEye/hardware/stages/elliptec/ellDevices.py +134 -0
  131. microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
  132. microEye/hardware/stages/elliptec/motorInfo.py +153 -0
  133. microEye/hardware/stages/elliptec/stage.py +62 -0
  134. microEye/hardware/stages/elliptec/test.py +139 -0
  135. microEye/hardware/stages/kinesis/__init__.py +1 -0
  136. microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
  137. microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
  138. microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
  139. microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
  140. microEye/hardware/stages/kinesis/kinesis.py +776 -0
  141. microEye/hardware/stages/piezo_concept.py +607 -0
  142. microEye/hardware/stages/stabilizer.py +785 -0
  143. microEye/hardware/stages/stage.py +89 -0
  144. microEye/hardware/widgets/__init__.py +10 -0
  145. microEye/hardware/widgets/controller.py +246 -0
  146. microEye/hardware/widgets/devices.py +133 -0
  147. microEye/hardware/widgets/focusWidget.py +264 -0
  148. microEye/hardware/widgets/qlist_slider.py +113 -0
  149. microEye/hardware/widgets/scan_acquisition.py +424 -0
  150. microEye/icons/1024.png +0 -0
  151. microEye/icons/128.png +0 -0
  152. microEye/icons/16.png +0 -0
  153. microEye/icons/24.png +0 -0
  154. microEye/icons/256.png +0 -0
  155. microEye/icons/32.png +0 -0
  156. microEye/icons/48.png +0 -0
  157. microEye/icons/512.png +0 -0
  158. microEye/icons/64.png +0 -0
  159. microEye/icons/__init__.py +0 -0
  160. microEye/icons/close.svg +88 -0
  161. microEye/icons/mieye.png +0 -0
  162. microEye/icons/min.svg +83 -0
  163. microEye/icons/viewer.png +0 -0
  164. microEye/launcher.py +42 -0
  165. microEye/qt.py +181 -0
  166. microEye/utils/__init__.py +2 -0
  167. microEye/utils/enum_encoder.py +10 -0
  168. microEye/utils/expandable_groupbox.py +93 -0
  169. microEye/utils/gui_helper.py +457 -0
  170. microEye/utils/hid/__init__.py +8 -0
  171. microEye/utils/hid/controller.py +153 -0
  172. microEye/utils/hid/device.py +63 -0
  173. microEye/utils/hid/enums.py +140 -0
  174. microEye/utils/hid/utils.py +58 -0
  175. microEye/utils/labelled_slider.py +134 -0
  176. microEye/utils/metadata.py +599 -0
  177. microEye/utils/metadata_tree.py +718 -0
  178. microEye/utils/micro_launcher.py +245 -0
  179. microEye/utils/parameter_tree.py +325 -0
  180. microEye/utils/pyscripting.py +444 -0
  181. microEye/utils/retry_exec.py +35 -0
  182. microEye/utils/start_gui.py +112 -0
  183. microEye/utils/thread_worker.py +115 -0
  184. microEye/utils/uImage.py +1512 -0
  185. microeye-2.3.2.dist-info/METADATA +401 -0
  186. microeye-2.3.2.dist-info/RECORD +190 -0
  187. microeye-2.3.2.dist-info/WHEEL +5 -0
  188. microeye-2.3.2.dist-info/entry_points.txt +2 -0
  189. microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
  190. microeye-2.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,104 @@
1
+ import json
2
+ import os
3
+ from dataclasses import asdict
4
+ from typing import Optional, Union
5
+
6
+ from microEye.analysis.fitting.psf.stats.curve_fit import CurveFitMethod, CurveResult
7
+ from microEye.analysis.fitting.psf.stats.slope_fit import SlopeResult
8
+ from microEye.qt import QtWidgets, getOpenFileName, getSaveFileName
9
+ from microEye.utils.enum_encoder import EnumEncoder
10
+
11
+
12
+ def import_fit_curve(parent: Optional[QtWidgets.QWidget] = None, curDir: str = ''):
13
+ '''
14
+ Import curve fit results from a JSON file.
15
+
16
+ Parameters
17
+ ----------
18
+ parent : Optional[QtWidgets.QWidget]
19
+ Parent widget for file dialog
20
+ curDir : str
21
+ Initial directory for file dialog
22
+
23
+ Notes
24
+ -----
25
+ This function requires a QApplication instance to be running
26
+
27
+ Returns
28
+ -------
29
+ Tuple[Union[SlopeResult, CurveResult], str]
30
+ The curve fit results and the
31
+ filename from which they were imported
32
+ '''
33
+ filename, _ = getOpenFileName(
34
+ parent,
35
+ 'Open Curve Fit',
36
+ curDir,
37
+ 'JSON Files (*.json)',
38
+ )
39
+
40
+ if not filename:
41
+ return None, None
42
+
43
+ with open(filename) as file:
44
+ data = json.load(file)
45
+
46
+ if 'method' not in data:
47
+ return None, None
48
+
49
+ try:
50
+ data['method'] = CurveFitMethod(data['method'])
51
+
52
+ if data['method'] == CurveFitMethod.LINEAR:
53
+ return SlopeResult(**data), filename
54
+ else:
55
+ return CurveResult(**data), filename
56
+ except ValueError:
57
+ # Value is not a valid enum member; keep as is
58
+ return None, None
59
+
60
+
61
+ def export_fit_curve(
62
+ results: Union[SlopeResult, CurveResult],
63
+ parent: Optional[QtWidgets.QWidget] = None,
64
+ curDir: str = '',
65
+ ):
66
+ '''
67
+ Export curve fit results to a JSON file.
68
+
69
+ Parameters
70
+ ----------
71
+ results : Union[SlopeResult, CurveResult]
72
+ The curve fit results to export
73
+ parent : Optional[QtWidgets.QWidget]
74
+ Parent widget for file dialog
75
+ curDir : str
76
+ Initial directory for file dialog
77
+
78
+ Notes
79
+ -----
80
+ This function requires a QApplication instance to be running
81
+
82
+ Returns
83
+ -------
84
+ bool
85
+ True if the results were successfully exported
86
+ '''
87
+ if results is None:
88
+ return False
89
+
90
+ filename, _ = getSaveFileName(
91
+ parent,
92
+ 'Save Curve Fit',
93
+ curDir,
94
+ 'JSON Files (*.json)',
95
+ )
96
+
97
+ if not filename:
98
+ return False
99
+
100
+ if isinstance(results, (SlopeResult, CurveResult)):
101
+ with open(filename, 'w') as file:
102
+ json.dump(asdict(results), file, cls=EnumEncoder)
103
+
104
+ return True
@@ -0,0 +1,171 @@
1
+ from dataclasses import dataclass
2
+ from typing import Callable, Optional
3
+
4
+ import numpy as np
5
+ from scipy import stats
6
+
7
+ from microEye.analysis.fitting.psf.stats.core import ConfidenceMethod
8
+ from microEye.analysis.fitting.psf.stats.curve_fit import CurveFitMethod
9
+
10
+
11
+ @dataclass
12
+ class SlopeResult:
13
+ '''Results from slope analysis'''
14
+ slope: float
15
+ intercept: float
16
+ r_squared: float
17
+ p_value: float
18
+ slope_ci: tuple[float, float]
19
+ data : dict
20
+ zero_crossing: Optional[float] = None
21
+ zero_crossing_ci: Optional[tuple[float, float]] = None
22
+ method: CurveFitMethod = CurveFitMethod.LINEAR
23
+
24
+ class SlopeAnalyzer:
25
+ @staticmethod
26
+ def fit_stat_slope(
27
+ selected_stat: str,
28
+ get_stats: Callable,
29
+ region: tuple[int, int],
30
+ confidence_method: ConfidenceMethod = ConfidenceMethod.T_DIST,
31
+ confidence_level: float = 0.95,
32
+ ) -> Optional[SlopeResult]:
33
+ '''
34
+ Calculate slope and related statistics for the
35
+ selected statistic in the given region.
36
+
37
+ Parameters
38
+ ----------
39
+ selected_stat : str
40
+ The statistic to analyze
41
+ stats_calculator : StatsCalculator
42
+ Calculator for the statistic
43
+ region : tuple[int, int]
44
+ The z-region to analyze (start, end)
45
+ confidence_method : ConfidenceMethod
46
+ Method to calculate confidence intervals
47
+ confidence_level : float
48
+ Confidence level for intervals (0-1)
49
+
50
+ Returns
51
+ -------
52
+ Optional[SlopeResult]
53
+ Slope analysis results including confidence intervals and zero crossing
54
+ '''
55
+ if selected_stat not in ['Sigma (diff)', 'Sigma (x/y)', 'Sigma² (diff)']:
56
+ return None
57
+
58
+
59
+ # Get z values and stat data
60
+ z_indices, stat_data, lower_bounds, upper_bounds = get_stats()
61
+
62
+ # Select data within region
63
+ mask = (z_indices >= region[0]) & (z_indices <= region[1])
64
+ x_data = z_indices[mask]
65
+ y_data = stat_data[mask]
66
+
67
+ # Remove any NaN values
68
+ valid_mask = ~np.isnan(y_data)
69
+ x_data = x_data[valid_mask]
70
+ y_data = y_data[valid_mask]
71
+
72
+ if len(x_data) < 2:
73
+ return None
74
+
75
+ # Perform linear regression
76
+ slope, intercept, r_value, p_value, slope_std_err = stats.linregress(
77
+ x_data, y_data
78
+ )
79
+ r_squared = r_value**2
80
+
81
+ # Calculate slope confidence intervals
82
+ if confidence_method == ConfidenceMethod.T_DIST:
83
+ # t-distribution based CI
84
+ dof = len(x_data) - 2
85
+ t_val = stats.t.ppf((1 + confidence_level) / 2, dof)
86
+ slope_ci = (slope - t_val * slope_std_err, slope + t_val * slope_std_err)
87
+ elif confidence_method == ConfidenceMethod.BOOTSTRAP:
88
+ # Bootstrap confidence intervals for slope
89
+ n_bootstrap = 1000
90
+ bootstrap_slopes = []
91
+ for _ in range(n_bootstrap):
92
+ indices = np.random.randint(0, len(x_data), size=len(x_data))
93
+ x_boot = x_data[indices]
94
+ y_boot = y_data[indices]
95
+ boot_slope = np.polyfit(x_boot, y_boot, 1)[0]
96
+ bootstrap_slopes.append(boot_slope)
97
+ slope_ci = np.percentile(
98
+ bootstrap_slopes,
99
+ [(1 - confidence_level) * 100 / 2, (1 + confidence_level) * 100 / 2],
100
+ )
101
+ else:
102
+ # Simple min/max based on data uncertainty
103
+ lower_slope = np.polyfit(x_data, lower_bounds[mask][valid_mask], 1)[0]
104
+ upper_slope = np.polyfit(x_data, upper_bounds[mask][valid_mask], 1)[0]
105
+ slope_ci = (lower_slope, upper_slope)
106
+
107
+ # Calculate zero crossing
108
+ target = 0 if selected_stat == 'Sigma (diff)' else 1
109
+ zero_crossing = (target - intercept) / slope if abs(slope) > 1e-10 else None
110
+
111
+ # Calculate zero crossing confidence intervals
112
+ if zero_crossing is not None:
113
+ if confidence_method == ConfidenceMethod.T_DIST:
114
+ # Use error propagation for zero crossing uncertainty
115
+ intercept_std_err = stats.linregress(x_data, y_data).intercept_stderr
116
+ zero_crossing_err = np.sqrt(
117
+ (intercept_std_err / slope) ** 2
118
+ + ((target - intercept) * slope_std_err / slope**2) ** 2
119
+ )
120
+ t_val = stats.t.ppf((1 + confidence_level) / 2, len(x_data) - 2)
121
+ zero_crossing_ci = (
122
+ zero_crossing - t_val * zero_crossing_err,
123
+ zero_crossing + t_val * zero_crossing_err,
124
+ )
125
+ elif confidence_method == ConfidenceMethod.BOOTSTRAP:
126
+ # Bootstrap zero crossings
127
+ bootstrap_crossings = []
128
+ for _ in range(n_bootstrap):
129
+ indices = np.random.randint(0, len(x_data), size=len(x_data))
130
+ x_boot = x_data[indices]
131
+ y_boot = y_data[indices]
132
+ boot_slope, boot_intercept = np.polyfit(x_boot, y_boot, 1)
133
+ if abs(boot_slope) > 1e-10:
134
+ boot_crossing = (target - boot_intercept) / boot_slope
135
+ bootstrap_crossings.append(boot_crossing)
136
+ if bootstrap_crossings:
137
+ zero_crossing_ci = np.percentile(
138
+ bootstrap_crossings,
139
+ [
140
+ (1 - confidence_level) * 100 / 2,
141
+ (1 + confidence_level) * 100 / 2,
142
+ ],
143
+ )
144
+ else:
145
+ zero_crossing_ci = None
146
+ else:
147
+ # Simple interval based on slope confidence intervals
148
+ crossings = [
149
+ (target - intercept) / s for s in slope_ci if abs(s) > 1e-10
150
+ ]
151
+ zero_crossing_ci = (
152
+ (min(crossings), max(crossings)) if crossings else None
153
+ )
154
+ else:
155
+ zero_crossing_ci = None
156
+
157
+ return SlopeResult(
158
+ slope=slope,
159
+ intercept=intercept,
160
+ r_squared=r_squared,
161
+ p_value=p_value,
162
+ slope_ci=slope_ci,
163
+ zero_crossing=zero_crossing,
164
+ zero_crossing_ci=zero_crossing_ci,
165
+ data = {
166
+ 'z': x_data.tolist(),
167
+ 'ratio': y_data.tolist(),
168
+ 'lower_bounds': lower_bounds[mask][valid_mask].tolist(),
169
+ 'upper_bounds': upper_bounds[mask][valid_mask].tolist()
170
+ }
171
+ )
@@ -0,0 +1,147 @@
1
+ import numpy as np
2
+ from scipy.optimize import fmin
3
+
4
+
5
+ def robust_mean(data: np.ndarray, dim: int=None, k: float=3, fit: bool=False):
6
+ '''
7
+ Calculate robust mean, standard deviation, and indices of inliers and outliers.
8
+
9
+ Parameters
10
+ ----------
11
+ data : numpy.ndarray
12
+ Input data.
13
+ dim : int, optional
14
+ Dimension along which the mean is taken. Default is None.
15
+ k : float, optional
16
+ Number of sigmas at which to place the cut-off. Default is 3.
17
+ fit : bool, optional
18
+ Whether or not to use fitting to robustly estimate the mean.
19
+ Default is False.
20
+ If True, mean is approximated by minimizing the median deviation.
21
+ If False, mean is approximated by the median.
22
+
23
+ Returns
24
+ -------
25
+ final_mean : float
26
+ Robust mean.
27
+ std_sample : float
28
+ Standard deviation of the data (divide by sqrt(n) to get std of the mean).
29
+ inlier_idx : numpy.ndarray
30
+ Index into data with the inliers.
31
+ outlier_idx : numpy.ndarray
32
+ Index into data with the outliers.
33
+
34
+ Warning
35
+ -------
36
+ NaN or Inf will be counted as neither in- nor outlier.
37
+ The code is based on (linear) Least Median Squares and
38
+ can be changed to include weights.
39
+
40
+ References
41
+ ----------
42
+ J. Ries https://github.com/jries/SMAP
43
+
44
+ Example
45
+ -------
46
+ >>> import numpy as np
47
+ >>> from scipy.stats import norm
48
+ >>> data = np.concatenate([norm.rvs(loc=0, scale=1, size=100),
49
+ ... norm.rvs(loc=10, scale=1, size=5)])
50
+ >>> final_mean, std_sample, inlier_idx, outlier_idx = robust_mean(data)
51
+ '''
52
+ if data.size == 0:
53
+ raise ValueError('Please supply non-empty data to robust_mean')
54
+
55
+ if dim is None:
56
+ # make sure that the dimension is correct if there's a vector
57
+ if np.any(np.array(data.shape) == 1) and len(data.shape) == 2:
58
+ dim = np.where(np.array(data.shape) > 1)[0][0]
59
+ else:
60
+ dim = 0
61
+
62
+ if k is None:
63
+ k=3
64
+
65
+ if fit and len(data.shape) > 1:
66
+ raise ValueError(f'Fitting {len(data.shape)}-D data is not supported!')
67
+
68
+ if np.sum(np.isfinite(data)) < 4:
69
+ print('Warning: Less than 4 data points!')
70
+ finite_data = data[np.isfinite(data)]
71
+ final_mean = np.nanmean(finite_data, axis=dim)
72
+ std_sample = np.nanstd(finite_data, axis=dim)
73
+ inlier_idx = np.where(np.isfinite(data))
74
+ outlier_idx = np.array([], dtype=int)
75
+ return final_mean, std_sample, inlier_idx, outlier_idx
76
+
77
+ # LEAST MEDIAN SQUARES
78
+ # define magic numbers:
79
+ magic_number2 = 1.4826**2 # see Danuser, 1992 or Rousseeuw & Leroy, 1987
80
+
81
+ # remember data size and reduced dataSize
82
+ data_size = np.array(data.shape)
83
+ reduced_data_size = data_size.copy()
84
+ reduced_data_size[dim] = 1
85
+
86
+ # need this for later repmats
87
+ blow_up_data_size = data_size // reduced_data_size
88
+
89
+ # count how many relevant dimensions we have besides dim
90
+ real_dimensions = np.sum(data_size > 1)
91
+
92
+ # calc median - reduce dimension dim to length 1
93
+ if fit:
94
+ # minimize the median deviation from the mean
95
+ median_data = fmin(lambda x: np.median(np.abs(data - x)), np.median(data))
96
+ else:
97
+ median_data = np.nanmedian(data, axis=dim)
98
+
99
+ # calculate statistics
100
+ res2 = (data - np.tile(median_data, blow_up_data_size))**2
101
+ med_res2 = np.maximum(np.nanmedian(res2, axis=dim), np.finfo(float).eps)
102
+
103
+ # test value to calculate weights
104
+ test_value = res2 / np.tile(magic_number2 * med_res2, blow_up_data_size)
105
+
106
+ if real_dimensions == 1:
107
+ # goodRows: weight 1, badRows: weight 0
108
+ inlier_idx = np.where(test_value <= k**2)
109
+ outlier_idx = np.where(test_value > k**2)
110
+
111
+ # calculate std of the sample;
112
+ if len(inlier_idx[0]) > 4:
113
+ std_sample = np.sqrt(np.sum(res2[inlier_idx]) / (len(inlier_idx[0]) - 4))
114
+ else:
115
+ std_sample = np.nan
116
+
117
+ # MEAN
118
+ final_mean = np.mean(data[inlier_idx])
119
+
120
+ else:
121
+ # goodRows: weight 1, badRows: weight 0
122
+ inlier_idx = np.where(test_value <= k**2)
123
+ outlier_idx = np.where(test_value > k**2)
124
+
125
+ # mask outliers
126
+ res2[outlier_idx] = np.nan
127
+
128
+ # count inliers
129
+ n_inliers = np.sum(~np.isnan(res2), axis=dim)
130
+
131
+ # calculate std of the sample;
132
+ if np.any(n_inliers > 4):
133
+ # put NaN wherever there are not enough data points to calculate a
134
+ # standard deviation
135
+ good_idx = np.sum(np.isfinite(res2), axis=dim) > 4
136
+ std_sample = np.full_like(good_idx, np.nan, dtype=float)
137
+ std_sample[good_idx] = np.sqrt(
138
+ np.nansum(res2[good_idx], axis=dim) / (n_inliers[good_idx] - 4)
139
+ )
140
+ else:
141
+ std_sample = np.full_like(n_inliers, np.nan, dtype=float)
142
+
143
+ # MEAN
144
+ data[outlier_idx] = np.nan
145
+ final_mean = np.nanmean(data, axis=dim)
146
+
147
+ return final_mean, std_sample, inlier_idx, outlier_idx
@@ -0,0 +1,47 @@
1
+ import numpy as np
2
+
3
+
4
+ # Placeholder functions, replace with actual implementations
5
+ def images2beads_globalfit(p):
6
+ # Replace with actual implementation
7
+ pass
8
+
9
+ def mleFit_LM(stack, num_params, max_iterations, tol, display, verbose):
10
+ # Replace with actual implementation
11
+ pass
12
+
13
+ def stackas2z_so(PSFxpix, PSFypix, frames, phot, p):
14
+ # Replace with actual implementation
15
+ pass
16
+
17
+ def getspline_so(beadsh, p):
18
+ # Replace with actual implementation
19
+ pass
20
+
21
+ def getstackcal_g(beadsh, p):
22
+ # Replace with actual implementation
23
+ pass
24
+
25
+ def getgausscal_so(ch, p):
26
+ # Replace with actual implementation
27
+ pass
28
+
29
+ def testfit_spline(testallrois, coeffZ, i, p, cellarray, axzernikef):
30
+ # Replace with actual implementation
31
+ pass
32
+
33
+ def plotCRLBcsplinePSF(cspline, axcrlb):
34
+ # Replace with actual implementation
35
+ pass
36
+
37
+ def vectorPSF2cspline(num, zernikefit, p):
38
+ # Replace with actual implementation
39
+ pass
40
+
41
+ def zernikefitBeadstack(stack, zernikefit_params, axzernike, axPupil, axMode):
42
+ # Replace with actual implementation
43
+ pass
44
+
45
+ def Spline3D_interp(PSFZernike):
46
+ # Replace with actual implementation
47
+ pass