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.
- microEye/__init__.py +47 -0
- microEye/_version.py +2 -0
- microEye/analysis/__init__.py +1 -0
- microEye/analysis/checklist_dialog.py +143 -0
- microEye/analysis/cmosMaps.py +228 -0
- microEye/analysis/filters/__init__.py +9 -0
- microEye/analysis/filters/base.py +21 -0
- microEye/analysis/filters/spatial.py +338 -0
- microEye/analysis/filters/temporal.py +76 -0
- microEye/analysis/fitting/__init__.py +0 -0
- microEye/analysis/fitting/fit.py +680 -0
- microEye/analysis/fitting/nena.py +375 -0
- microEye/analysis/fitting/phasor_fit.py +90 -0
- microEye/analysis/fitting/processing.py +317 -0
- microEye/analysis/fitting/psf/__init__.py +6 -0
- microEye/analysis/fitting/psf/extract.py +1129 -0
- microEye/analysis/fitting/psf/rubost_mean.py +150 -0
- microEye/analysis/fitting/psf/spline.py +167 -0
- microEye/analysis/fitting/psf/stats/__init__.py +12 -0
- microEye/analysis/fitting/psf/stats/core.py +295 -0
- microEye/analysis/fitting/psf/stats/curve_fit.py +708 -0
- microEye/analysis/fitting/psf/stats/io.py +104 -0
- microEye/analysis/fitting/psf/stats/slope_fit.py +171 -0
- microEye/analysis/fitting/psf/temp.py +147 -0
- microEye/analysis/fitting/psf/test.py +47 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUfunctions.py +657 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUmleFit_LM.py +1336 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/CPUsplineLib.py +270 -0
- microEye/analysis/fitting/pyfit3Dcspline/CPU/__init__.py +1 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUfunctions.py +609 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_EMCCD.py +1396 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUmleFit_LM_sCMOS.py +1426 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/GPUsplineLib.py +231 -0
- microEye/analysis/fitting/pyfit3Dcspline/GPU/__init__.py +2 -0
- microEye/analysis/fitting/pyfit3Dcspline/__init__.py +10 -0
- microEye/analysis/fitting/pyfit3Dcspline/constants.py +27 -0
- microEye/analysis/fitting/pyfit3Dcspline/mainfunctions.py +903 -0
- microEye/analysis/fitting/results.py +917 -0
- microEye/analysis/fitting/results_stats.py +251 -0
- microEye/analysis/fitting/tardis.py +209 -0
- microEye/analysis/multi_viewer.py +568 -0
- microEye/analysis/processing/__init__.py +1 -0
- microEye/analysis/processing/frc.py +120 -0
- microEye/analysis/rendering/__init__.py +3 -0
- microEye/analysis/rendering/base.py +452 -0
- microEye/analysis/rendering/cloud.py +215 -0
- microEye/analysis/rendering/core.py +47 -0
- microEye/analysis/rendering/volumetric.py +198 -0
- microEye/analysis/tools/__init__.py +0 -0
- microEye/analysis/tools/kymograms.py +1079 -0
- microEye/analysis/tools/roi_selectors.py +556 -0
- microEye/analysis/utils/__init__.py +3 -0
- microEye/analysis/utils/coordinates.py +18 -0
- microEye/analysis/utils/images.py +68 -0
- microEye/analysis/utils/windows.py +19 -0
- microEye/analysis/viewer/__init__.py +3 -0
- microEye/analysis/viewer/image_options_widget.py +615 -0
- microEye/analysis/viewer/images.py +1225 -0
- microEye/analysis/viewer/layers_widget.py +415 -0
- microEye/analysis/viewer/localizations.py +1304 -0
- microEye/analysis/viewer/psf.py +1003 -0
- microEye/analysis/viewer/volume.py +456 -0
- microEye/hardware/__init__.py +3 -0
- microEye/hardware/cams/__init__.py +29 -0
- microEye/hardware/cams/camera_calibration.py +99 -0
- microEye/hardware/cams/camera_list.py +528 -0
- microEye/hardware/cams/camera_options.py +694 -0
- microEye/hardware/cams/camera_panel.py +941 -0
- microEye/hardware/cams/dummy/__init__.py +1 -0
- microEye/hardware/cams/dummy/dummy_panel.py +759 -0
- microEye/hardware/cams/jobs.py +497 -0
- microEye/hardware/cams/line_profiler.py +99 -0
- microEye/hardware/cams/linescan/IR_Cam.py +474 -0
- microEye/hardware/cams/linescan/__init__.py +1 -0
- microEye/hardware/cams/micam.py +524 -0
- microEye/hardware/cams/pco/__init__.py +58 -0
- microEye/hardware/cams/pco/enums.py +382 -0
- microEye/hardware/cams/pco/pco_cam.py +761 -0
- microEye/hardware/cams/pco/pco_panel.py +477 -0
- microEye/hardware/cams/shortcuts.py +312 -0
- microEye/hardware/cams/thorlabs/__init__.py +1 -0
- microEye/hardware/cams/thorlabs/thorlabs.py +1508 -0
- microEye/hardware/cams/thorlabs/thorlabs_panel.py +850 -0
- microEye/hardware/cams/ueye/__init__.py +1 -0
- microEye/hardware/cams/ueye/ueye_camera.py +1023 -0
- microEye/hardware/cams/ueye/ueye_panel.py +861 -0
- microEye/hardware/cams/vimba/__init__.py +1 -0
- microEye/hardware/cams/vimba/vimba_cam.py +1000 -0
- microEye/hardware/cams/vimba/vimba_panel.py +813 -0
- microEye/hardware/device.py +60 -0
- microEye/hardware/lasers/__init__.py +13 -0
- microEye/hardware/lasers/io_matchbox.py +791 -0
- microEye/hardware/lasers/io_params.py +85 -0
- microEye/hardware/lasers/io_single_laser.py +742 -0
- microEye/hardware/lasers/laser_relay.py +594 -0
- microEye/hardware/mieye/__init__.py +1 -0
- microEye/hardware/mieye/acquisition_manager.py +467 -0
- microEye/hardware/mieye/devices_manager.py +533 -0
- microEye/hardware/mieye/miEye.py +659 -0
- microEye/hardware/misc/__init__.py +0 -0
- microEye/hardware/misc/acquisition_view.py +71 -0
- microEye/hardware/misc/reglo.py +761 -0
- microEye/hardware/misc/temp.py +188 -0
- microEye/hardware/port_config.py +59 -0
- microEye/hardware/protocols/__init__.py +2 -0
- microEye/hardware/protocols/actions.py +402 -0
- microEye/hardware/protocols/actions_items.py +703 -0
- microEye/hardware/protocols/designer.py +244 -0
- microEye/hardware/protocols/scene_manager.py +191 -0
- microEye/hardware/protocols/serialization.py +97 -0
- microEye/hardware/pycromanager/__init__.py +16 -0
- microEye/hardware/pycromanager/core.py +1433 -0
- microEye/hardware/pycromanager/devices.py +461 -0
- microEye/hardware/pycromanager/enums.py +107 -0
- microEye/hardware/pycromanager/headless.py +153 -0
- microEye/hardware/pycromanager/utils.py +34 -0
- microEye/hardware/pycromanager/widgets/__init__.py +5 -0
- microEye/hardware/pycromanager/widgets/bridges.py +407 -0
- microEye/hardware/pycromanager/widgets/headless_manager.py +258 -0
- microEye/hardware/pycromanager/widgets/headless_options.py +224 -0
- microEye/hardware/pycromanager/widgets/pycro_panel.py +455 -0
- microEye/hardware/stages/__init__.py +18 -0
- microEye/hardware/stages/elliptec/__init__.py +5 -0
- microEye/hardware/stages/elliptec/baseDevice.py +314 -0
- microEye/hardware/stages/elliptec/device.py +384 -0
- microEye/hardware/stages/elliptec/deviceID.py +212 -0
- microEye/hardware/stages/elliptec/devicePort.py +379 -0
- microEye/hardware/stages/elliptec/deviceStatus.py +65 -0
- microEye/hardware/stages/elliptec/devicesView.py +706 -0
- microEye/hardware/stages/elliptec/ellDevices.py +134 -0
- microEye/hardware/stages/elliptec/messageUpdater.py +34 -0
- microEye/hardware/stages/elliptec/motorInfo.py +153 -0
- microEye/hardware/stages/elliptec/stage.py +62 -0
- microEye/hardware/stages/elliptec/test.py +139 -0
- microEye/hardware/stages/kinesis/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/__init__.py +1 -0
- microEye/hardware/stages/kinesis/kdc101/enums.py +1002 -0
- microEye/hardware/stages/kinesis/kdc101/factory.py +171 -0
- microEye/hardware/stages/kinesis/kdc101/kdc101.py +718 -0
- microEye/hardware/stages/kinesis/kinesis.py +776 -0
- microEye/hardware/stages/piezo_concept.py +607 -0
- microEye/hardware/stages/stabilizer.py +785 -0
- microEye/hardware/stages/stage.py +89 -0
- microEye/hardware/widgets/__init__.py +10 -0
- microEye/hardware/widgets/controller.py +246 -0
- microEye/hardware/widgets/devices.py +133 -0
- microEye/hardware/widgets/focusWidget.py +264 -0
- microEye/hardware/widgets/qlist_slider.py +113 -0
- microEye/hardware/widgets/scan_acquisition.py +424 -0
- microEye/icons/1024.png +0 -0
- microEye/icons/128.png +0 -0
- microEye/icons/16.png +0 -0
- microEye/icons/24.png +0 -0
- microEye/icons/256.png +0 -0
- microEye/icons/32.png +0 -0
- microEye/icons/48.png +0 -0
- microEye/icons/512.png +0 -0
- microEye/icons/64.png +0 -0
- microEye/icons/__init__.py +0 -0
- microEye/icons/close.svg +88 -0
- microEye/icons/mieye.png +0 -0
- microEye/icons/min.svg +83 -0
- microEye/icons/viewer.png +0 -0
- microEye/launcher.py +42 -0
- microEye/qt.py +181 -0
- microEye/utils/__init__.py +2 -0
- microEye/utils/enum_encoder.py +10 -0
- microEye/utils/expandable_groupbox.py +93 -0
- microEye/utils/gui_helper.py +457 -0
- microEye/utils/hid/__init__.py +8 -0
- microEye/utils/hid/controller.py +153 -0
- microEye/utils/hid/device.py +63 -0
- microEye/utils/hid/enums.py +140 -0
- microEye/utils/hid/utils.py +58 -0
- microEye/utils/labelled_slider.py +134 -0
- microEye/utils/metadata.py +599 -0
- microEye/utils/metadata_tree.py +718 -0
- microEye/utils/micro_launcher.py +245 -0
- microEye/utils/parameter_tree.py +325 -0
- microEye/utils/pyscripting.py +444 -0
- microEye/utils/retry_exec.py +35 -0
- microEye/utils/start_gui.py +112 -0
- microEye/utils/thread_worker.py +115 -0
- microEye/utils/uImage.py +1512 -0
- microeye-2.3.2.dist-info/METADATA +401 -0
- microeye-2.3.2.dist-info/RECORD +190 -0
- microeye-2.3.2.dist-info/WHEEL +5 -0
- microeye-2.3.2.dist-info/entry_points.txt +2 -0
- microeye-2.3.2.dist-info/licenses/LICENSE +674 -0
- 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
|