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,375 @@
1
+ import numba as nb
2
+ import numpy as np
3
+ import pyqtgraph as pg
4
+ from scipy.optimize import minimize
5
+ from scipy.stats import norm
6
+
7
+ from microEye.qt import QDateTime, Qt, QtWidgets
8
+
9
+
10
+ @nb.njit(cache=True)
11
+ def Rayleigh_dist(x: np.ndarray, x0: float, sig: float):
12
+ '''Rayleigh distribution PDF
13
+
14
+ X = (x - x0) / sig
15
+
16
+ X[X < 0] = 0
17
+
18
+ PDF = 0.5 * X / sig * np.exp(-0.25 * X**2)
19
+
20
+ Parameters
21
+ ----------
22
+ x : np.ndarray
23
+ x values array.
24
+ x0 : float
25
+ loc parameter.
26
+ sig : float
27
+ scale parameter.
28
+
29
+ Returns
30
+ -------
31
+ np.ndarray
32
+ the Rayleigh distribution PDF
33
+ '''
34
+ X = (x - x0) / sig
35
+ X[X < 0] = 0
36
+ return 0.5 * X / sig * np.exp(-0.25 * X**2)
37
+
38
+
39
+ @nb.njit(cache=True)
40
+ def Gaussian_dist(x: np.ndarray, mu: np.ndarray, sigma: np.ndarray):
41
+ '''Gaussian distribution PDF
42
+
43
+ 1 / (sqrt(pi / 2) * sigma) * exp(-2 * (x - mu)**2 / sigma**2)
44
+
45
+ Parameters
46
+ ----------
47
+ x : np.ndarray
48
+ x values array.
49
+ mu : np.ndarray
50
+ mu parameter.
51
+ sigma : np.ndarray
52
+ sigma parameter.
53
+
54
+ Returns
55
+ -------
56
+ np.ndarray
57
+ the Gaussian distribution PDF
58
+ '''
59
+ return 1 / (np.sqrt(np.pi / 2) * sigma) * \
60
+ np.exp(-2 * (x - mu)**2 / sigma**2)
61
+
62
+
63
+ @nb.njit(cache=True)
64
+ def NeNA_model(x: np.ndarray, params):
65
+ '''The NeNA model
66
+
67
+ NeNA(x) = a0 * Rayleigh_dist(x, x0=0, sig0) +
68
+ a1 * Gaussian_dist(x, x1, sig1) + a2 * x
69
+
70
+ Parameters
71
+ ----------
72
+ x : np.ndarray
73
+ x values array.
74
+ params : list[float]
75
+ list or array of parameters [a0, a1, a2, x1, sig0, sig1]
76
+
77
+ Returns
78
+ -------
79
+ np.ndarray
80
+ the NeNA model
81
+ '''
82
+ a0, a1, a2 = params[:3]
83
+ x0 = 0
84
+ x1 = params[3]
85
+ sig0, sig1 = params[4:6]
86
+
87
+ return (
88
+ a0 * Rayleigh_dist(x, x0, sig0) +
89
+ a1 * Gaussian_dist(x, x1, sig1) +
90
+ a2 * x)
91
+
92
+
93
+ def NeNA_log_like(params, args):
94
+ '''The NeNA model fitting loglikihood function
95
+
96
+ Parameters
97
+ ----------
98
+ params : list[float]
99
+ list or array of parameters [a0, a1, a2, x1, sig0, sig1, sd]
100
+ args : tuple[np.ndarray, np.ndarray]
101
+ tuple of (x, y) data used for fitting
102
+
103
+ Returns
104
+ -------
105
+ float
106
+ loglikelihood value
107
+ '''
108
+ sd = params[6]
109
+
110
+ x, data = args[0], args[1]
111
+
112
+ # only positive coeffs
113
+ for param in params[:3]:
114
+ if not 0 <= param < 100:
115
+ return np.inf
116
+
117
+ # only positive sigma
118
+ for param in params[4:6]:
119
+ if param < 0:
120
+ return np.inf
121
+
122
+ dataPred = NeNA_model(x, params[:7])
123
+
124
+ # Calculate negative log likelihood
125
+ LL = -np.sum(norm.logpdf(data, loc=dataPred, scale=sd))
126
+
127
+ return (LL)
128
+
129
+
130
+ def NeNA_fit(x, y, params):
131
+ '''The NeNA model fitting MLE minimization function
132
+
133
+ Parameters
134
+ ----------
135
+ x : np.ndarray
136
+ values of x used for fitting.
137
+ y : np.ndarray
138
+ values of y used for fitting.
139
+ params : list[float]
140
+ list or array of parameters [a0, a1, a2, x1, sig0, sig1, sd]
141
+
142
+ Returns
143
+ -------
144
+ OptimizeResult
145
+ The optimization result represented as an OptimizeResult object.
146
+ '''
147
+ return minimize(
148
+ NeNA_log_like, params, args=[x, y], method='Nelder-Mead', tol=1e-8)
149
+
150
+
151
+ def get_bincenters(edges: np.ndarray):
152
+ '''Bin centers of histogram bin edges.
153
+
154
+ Parameters
155
+ ----------
156
+ edges : np.ndarray
157
+ array of histogram bin edges.
158
+
159
+ Returns
160
+ -------
161
+ np.ndarray
162
+ array of bin centers of histogram bin edges.
163
+ '''
164
+ return np.array([(edges[i]+edges[i+1])/2. for i in range(len(edges)-1)])
165
+
166
+
167
+ def NeNA_resolution_estimate(
168
+ distances: np.ndarray, trackIDs: np.ndarray,
169
+ minDist=0.5, range=None, bins=500,
170
+ a_ray=0.5, a_gauss=0.25, a_lin=1, xc_gauss=15,
171
+ sig_ray=None, sig_gauss=30, sd_fit=1e-4):
172
+ if range is None:
173
+ range = [0, 200]
174
+ dist = distances[np.logical_and(trackIDs > 0, distances > minDist)]
175
+
176
+ n, bin_edges = np.histogram(dist, density=True, range=range, bins=bins)
177
+
178
+ if sig_ray is None:
179
+ sig_ray = np.sqrt(np.sum(np.square(dist)) / (2 * dist.shape[0]))
180
+
181
+ res = NeNA_fit(
182
+ get_bincenters(bin_edges), n,
183
+ [a_ray, a_gauss, a_lin, xc_gauss, sig_ray, sig_gauss, sd_fit])
184
+
185
+ print('NeNA resolution estimate')
186
+ print(f'A0 {res.x[0]:.5f} Loc0 {0:.5f} Sig0 {res.x[4]:.5f}')
187
+ print(f'A1 {res.x[1]:.5f} Loc1 {res.x[3]:.5f} Sig1 {res.x[5]:.5f}')
188
+ print(f'A2 {res.x[2]:e} SD {res.x[6]:e}')
189
+
190
+ return res, (bin_edges, n, get_bincenters(bin_edges))
191
+
192
+
193
+ class NeNA_Widget(QtWidgets.QDialog):
194
+
195
+ def __init__(
196
+ self,
197
+ neighbourDists: np.ndarray,
198
+ trackIDs: np.ndarray,
199
+ parent=None,
200
+ ):
201
+ super().__init__(parent)
202
+
203
+ self.setWindowTitle('NeNA localization precision estimate')
204
+ self.nDists = neighbourDists
205
+ self.trackIDs = trackIDs
206
+ self.maxDist = 200
207
+ self.res = None
208
+
209
+ self.flayout = QtWidgets.QHBoxLayout()
210
+ self.setLayout(self.flayout)
211
+
212
+ self.histogram = pg.PlotWidget()
213
+ self.flayout.addWidget(self.histogram)
214
+
215
+ self.fitgroup = QtWidgets.QGroupBox('Fitting parameters')
216
+ self.flayout.addWidget(self.fitgroup)
217
+
218
+ self.fitlay = QtWidgets.QFormLayout()
219
+ self.fitgroup.setLayout(self.fitlay)
220
+
221
+ self.bins = QtWidgets.QSpinBox()
222
+ self.bins.setMinimum(5)
223
+ self.bins.setMaximum(10000)
224
+ self.bins.setValue(200)
225
+
226
+ self.fitlay.addRow(
227
+ QtWidgets.QLabel('N of bins:'), self.bins)
228
+
229
+ self.A0 = QtWidgets.QDoubleSpinBox()
230
+ self.A0.setMinimum(0)
231
+ self.A0.setMaximum(10)
232
+ self.A0.setDecimals(4)
233
+ self.A0.setSingleStep(0.01)
234
+ self.A0.setValue(0.75)
235
+
236
+ self.sig0 = QtWidgets.QDoubleSpinBox()
237
+ self.sig0.setMinimum(0)
238
+ self.sig0.setMaximum(self.maxDist)
239
+ self.sig0.setDecimals(4)
240
+ self.sig0.setSingleStep(0.01)
241
+ self.sig0.setValue(
242
+ np.sqrt(
243
+ np.sum(np.square(self.nDists)
244
+ ) / (2 * self.nDists.shape[0])))
245
+
246
+ self.A1 = QtWidgets.QDoubleSpinBox()
247
+ self.A1.setMinimum(0)
248
+ self.A1.setMaximum(10)
249
+ self.A1.setDecimals(4)
250
+ self.A1.setSingleStep(0.01)
251
+ self.A1.setValue(0.25)
252
+
253
+ self.sig1 = QtWidgets.QDoubleSpinBox()
254
+ self.sig1.setMinimum(0)
255
+ self.sig1.setMaximum(self.maxDist)
256
+ self.sig1.setDecimals(4)
257
+ self.sig1.setSingleStep(0.01)
258
+ self.sig1.setValue(100)
259
+
260
+ self.loc1 = QtWidgets.QDoubleSpinBox()
261
+ self.loc1.setMinimum(0)
262
+ self.loc1.setMaximum(self.maxDist)
263
+ self.loc1.setDecimals(4)
264
+ self.loc1.setSingleStep(0.01)
265
+ self.loc1.setValue(
266
+ np.sqrt(
267
+ np.sum(np.square(self.nDists)
268
+ ) / (2 * self.nDists.shape[0])))
269
+
270
+ self.A2 = QtWidgets.QDoubleSpinBox()
271
+ self.A2.setMinimum(0)
272
+ self.A2.setMaximum(10)
273
+ self.A2.setDecimals(4)
274
+ self.A2.setSingleStep(0.01)
275
+ self.A2.setValue(1.0)
276
+
277
+ self.sd = QtWidgets.QDoubleSpinBox()
278
+ self.sd.setMinimum(0)
279
+ self.sd.setMaximum(10)
280
+ self.sd.setDecimals(6)
281
+ self.sd.setSingleStep(0.00001)
282
+ self.sd.setValue(1e-4)
283
+
284
+ self.fitlay.addRow(
285
+ QtWidgets.QLabel('Rayleigh Coeff.:'), self.A0)
286
+ self.fitlay.addRow(
287
+ QtWidgets.QLabel('Rayleigh Sigma:'), self.sig0)
288
+ self.fitlay.addRow(
289
+ QtWidgets.QLabel('Rayleigh Loc:'), QtWidgets.QLabel('0.00'))
290
+ self.fitlay.addRow(
291
+ QtWidgets.QLabel('Gaussian Coeff.:'), self.A1)
292
+ self.fitlay.addRow(
293
+ QtWidgets.QLabel('Gaussian Sigma:'), self.sig1)
294
+ self.fitlay.addRow(
295
+ QtWidgets.QLabel('Gaussian Loc:'), self.loc1)
296
+ self.fitlay.addRow(
297
+ QtWidgets.QLabel('Linear Coeff.:'), self.A2)
298
+ self.fitlay.addRow(
299
+ QtWidgets.QLabel('MLE SD:'), self.sd)
300
+
301
+ self.res_guess = QtWidgets.QCheckBox('Result as initial guess.')
302
+ self.fitlay.addWidget(self.res_guess)
303
+
304
+ self.fit_btn = QtWidgets.QPushButton('Fit', clicked=self.fit_data)
305
+ self.fitlay.addWidget(self.fit_btn)
306
+
307
+ self.log = QtWidgets.QPlainTextEdit()
308
+ self.log.setReadOnly(True)
309
+ self.log.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
310
+ self.log.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
311
+ self.fitlay.addRow(self.log)
312
+
313
+ greenP = pg.mkPen(color='g')
314
+ redP = pg.mkPen(color='r')
315
+ yellowP = pg.mkPen(color='y')
316
+ whiteP = pg.mkPen(color='w')
317
+ label_style = {'color': '#EEE', 'font-size': '10pt'}
318
+
319
+ legend = self.histogram.addLegend()
320
+ legend.anchor(itemPos=(1, 0), parentPos=(1, 0))
321
+ self.histogram.setLabel('bottom', 'Distance [nm]', **label_style)
322
+ self.histogram.setLabel('left', 'PDF', **label_style)
323
+ _bins = np.linspace(0, self.maxDist, self.bins.value())
324
+ self._hist_ref = self.histogram.plot(
325
+ _bins, np.zeros(_bins.shape[0]-1), stepMode='center',
326
+ fillLevel=0, fillOutline=True, brush=(0, 0, 255, 150),
327
+ name='NN Dinstances')
328
+ self._fit_ref = self.histogram.plot(
329
+ _bins, np.ones_like(_bins), pen=greenP, name='NeNA fit')
330
+ self._ray_ref = self.histogram.plot(
331
+ _bins, np.ones_like(_bins), pen=redP, name='Rayleigh')
332
+ self._gauss_ref = self.histogram.plot(
333
+ _bins, np.ones_like(_bins), pen=yellowP, name='Gauss')
334
+ self._line_ref = self.histogram.plot(
335
+ _bins, np.ones_like(_bins), pen=whiteP, name='Linear')
336
+
337
+ def fit_data(self):
338
+ self.log.appendPlainText(
339
+ QDateTime.currentDateTime().toString(
340
+ '>>> yyyy/MM/dd hh:mm:ss') + ' NeNA fit started \n'
341
+ )
342
+
343
+ if self.res is None or not self.res_guess.isChecked():
344
+ res, (edges, n, x) = NeNA_resolution_estimate(
345
+ self.nDists, self.trackIDs, bins=self.bins.value(),
346
+ a_ray=self.A0.value(), sig_ray=self.sig0.value(),
347
+ a_gauss=self.A1.value(), sig_gauss=self.sig1.value(),
348
+ xc_gauss=self.loc1.value(), a_lin=self.A2.value(),
349
+ sd_fit=self.sd.value())
350
+ else:
351
+ res, (edges, n, x) = NeNA_resolution_estimate(
352
+ self.nDists, self.trackIDs, bins=self.bins.value(),
353
+ a_ray=self.res.x[0], sig_ray=self.res.x[4],
354
+ a_gauss=self.res.x[1], sig_gauss=self.res.x[5],
355
+ xc_gauss=self.res.x[3], a_lin=self.res.x[2],
356
+ sd_fit=self.res.x[6])
357
+
358
+ self.res = res
359
+
360
+ self._hist_ref.setData(edges, n)
361
+ self._fit_ref.setData(edges, NeNA_model(edges, res.x[:7]))
362
+ self._ray_ref.setData(
363
+ edges, res.x[0] * Rayleigh_dist(edges, 0, res.x[4]))
364
+ self._gauss_ref.setData(
365
+ edges, res.x[1] * Gaussian_dist(edges, res.x[3], res.x[5]))
366
+ self._line_ref.setData(edges, res.x[2] * edges)
367
+
368
+ self.log.appendPlainText(
369
+ ' NeNA resolution estimate \n')
370
+ self.log.appendPlainText(
371
+ f' A0 {res.x[0]:.5f} Loc0 {0:.5f} Sig0 {res.x[4]:.5f}\n')
372
+ self.log.appendPlainText(
373
+ f' A1 {res.x[1]:.5f} Loc1 {res.x[3]:.5f} Sig1 {res.x[5]:.5f}\n')
374
+ self.log.appendPlainText(
375
+ f' A2 {res.x[2]:e} SD {res.x[6]:e}\n')
@@ -0,0 +1,90 @@
1
+
2
+ import numpy as np
3
+
4
+ from microEye.analysis.utils import radial_coordinate
5
+
6
+
7
+ def phasor_fit(image: np.ndarray, points: np.ndarray,
8
+ intensity=True, roi_size=7):
9
+ '''Sub-pixel Phasor 2D fit
10
+
11
+ More details:
12
+ see doi.org/10.1063/1.5005899 (Martens et al., 2017)
13
+ '''
14
+ if len(points) < 1:
15
+ return None
16
+
17
+ sub_fit = np.zeros((points.shape[0], 5), points.dtype)
18
+
19
+ if intensity:
20
+ bg_mask, sig_mask = roi_mask(roi_size)
21
+
22
+ for r in range(points.shape[0]):
23
+ x, y = points[r, :]
24
+ idx = int(x - roi_size//2)
25
+ idy = int(y - roi_size//2)
26
+ if idx < 0:
27
+ idx = 0
28
+ if idy < 0:
29
+ idy = 0
30
+ if idx + roi_size > image.shape[1]:
31
+ idx = image.shape[1] - roi_size
32
+ if idy + roi_size > image.shape[0]:
33
+ idy = image.shape[0] - roi_size
34
+ roi = image[idy:idy+roi_size, idx:idx+roi_size]
35
+ fft_roi = np.fft.fft2(roi)
36
+ theta_x = np.angle(fft_roi[0, 1])
37
+ theta_y = np.angle(fft_roi[1, 0])
38
+ if theta_x > 0:
39
+ theta_x = theta_x - 2 * np.pi
40
+ if theta_y > 0:
41
+ theta_y = theta_y - 2 * np.pi
42
+ x = idx + np.abs(theta_x) / (2 * np.pi / roi_size)
43
+ y = idy + np.abs(theta_y) / (2 * np.pi / roi_size)
44
+ sub_fit[r, :2] = [x, y]
45
+
46
+ magnitudeX = np.abs(fft_roi[0, 1])
47
+ magnitudeY = np.abs(fft_roi[1, 0])
48
+
49
+ sub_fit[r, 4] = magnitudeX / magnitudeY
50
+
51
+ if intensity:
52
+ idx = int(x - roi_size//2)
53
+ idy = int(y - roi_size//2)
54
+ if idx < 0:
55
+ idx = 0
56
+ if idy < 0:
57
+ idy = 0
58
+ if idx + roi_size > image.shape[1]:
59
+ idx = image.shape[1] - roi_size
60
+ if idy + roi_size > image.shape[0]:
61
+ idy = image.shape[0] - roi_size
62
+ int_roi = image[idy:idy + roi_size, idx:idx + roi_size]
63
+
64
+ sub_fit[r, 2:4] = intensity_estimate(int_roi, bg_mask, sig_mask)
65
+
66
+ return sub_fit
67
+
68
+
69
+ def roi_mask(roi_size=7):
70
+
71
+ roi_shape = [roi_size] * 2
72
+ roi_radius = roi_size / 2
73
+
74
+ radius_map, _ = radial_coordinate(roi_shape)
75
+
76
+ bg_mask = radius_map > (roi_radius - 0.5)
77
+ sig_mask = radius_map <= roi_radius
78
+
79
+ return bg_mask, sig_mask
80
+
81
+
82
+ def intensity_estimate(roi: np.ndarray, bg_mask, sig_mask, percentile=56):
83
+
84
+ background_map = roi[bg_mask]
85
+ background = np.percentile(
86
+ background_map, percentile)
87
+
88
+ intensity = np.sum(roi[sig_mask]) - (np.sum(sig_mask) * background)
89
+
90
+ return max(0, intensity), background