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,338 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
import cv2
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy import signal
|
|
7
|
+
from scipy.fftpack import fft2, fftshift, ifft2, ifftshift
|
|
8
|
+
|
|
9
|
+
from microEye.analysis.filters.base import AbstractFilter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PassFilter(AbstractFilter):
|
|
13
|
+
def run(self, image: np.ndarray) -> np.ndarray:
|
|
14
|
+
return image
|
|
15
|
+
|
|
16
|
+
def get_metadata(self):
|
|
17
|
+
'''Return metadata about the Pass filter.'''
|
|
18
|
+
return {
|
|
19
|
+
'name': 'Pass Filter'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class DoG_Filter(AbstractFilter):
|
|
23
|
+
def __init__(self, sigma: float = 1, factor: float = 2.5) -> None:
|
|
24
|
+
'''Difference of Gauss init
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
sigma : float, optional
|
|
29
|
+
sigma_min, by default 1
|
|
30
|
+
factor : float, optional
|
|
31
|
+
factor = sigma_max/sigma_min, by default 2.5
|
|
32
|
+
'''
|
|
33
|
+
self.set_params(sigma, factor)
|
|
34
|
+
|
|
35
|
+
def set_params(self, sigma: float, factor: float):
|
|
36
|
+
'''Set filter parameters.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
sigma : float
|
|
41
|
+
sigma_min
|
|
42
|
+
factor : float
|
|
43
|
+
factor = sigma_max/sigma_min
|
|
44
|
+
'''
|
|
45
|
+
self._show_filter = False
|
|
46
|
+
self.sigma = sigma
|
|
47
|
+
self.factor = factor
|
|
48
|
+
self.rsize = max(math.ceil(6 * self.sigma + 1), 3)
|
|
49
|
+
self.dog = DoG_Filter.gaussian_kernel(
|
|
50
|
+
self.rsize, self.sigma
|
|
51
|
+
) - DoG_Filter.gaussian_kernel(self.rsize, max(1, self.factor * self.sigma))
|
|
52
|
+
|
|
53
|
+
def gaussian_kernel(dim, sigma):
|
|
54
|
+
x = cv2.getGaussianKernel(dim, sigma)
|
|
55
|
+
kernel = x.dot(x.T)
|
|
56
|
+
return kernel
|
|
57
|
+
|
|
58
|
+
def run(self, image: np.ndarray) -> np.ndarray:
|
|
59
|
+
rows, cols = image.shape
|
|
60
|
+
nrows = cv2.getOptimalDFTSize(rows)
|
|
61
|
+
ncols = cv2.getOptimalDFTSize(cols)
|
|
62
|
+
pad_rows = nrows - rows
|
|
63
|
+
pad_cols = ncols - cols
|
|
64
|
+
|
|
65
|
+
# Ensure that pad_cols[1] and pad_rows[1] are at least 1
|
|
66
|
+
pad_rows = (pad_rows // 2, max(1, pad_rows - pad_rows // 2))
|
|
67
|
+
pad_cols = (pad_cols // 2, max(1, pad_cols - pad_cols // 2))
|
|
68
|
+
|
|
69
|
+
nimg = np.pad(image, (pad_rows, pad_cols), mode='reflect')
|
|
70
|
+
|
|
71
|
+
res = cv2.normalize(
|
|
72
|
+
signal.convolve2d(nimg, np.rot90(self.dog), mode='same')[
|
|
73
|
+
pad_rows[0] : -pad_rows[1], pad_cols[0] : -pad_cols[1]
|
|
74
|
+
],
|
|
75
|
+
None,
|
|
76
|
+
0,
|
|
77
|
+
255,
|
|
78
|
+
cv2.NORM_MINMAX,
|
|
79
|
+
cv2.CV_8U,
|
|
80
|
+
)
|
|
81
|
+
return res
|
|
82
|
+
|
|
83
|
+
def get_metadata(self):
|
|
84
|
+
'''Return metadata about the Difference of Gaussians filter.'''
|
|
85
|
+
return {
|
|
86
|
+
'name': 'Difference of Gaussains Filter',
|
|
87
|
+
'sigma': self.sigma,
|
|
88
|
+
'factor': self.factor,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class GaussFilter(AbstractFilter):
|
|
93
|
+
def __init__(self, sigma=1) -> None:
|
|
94
|
+
'''Gaussian filter initialization.'''
|
|
95
|
+
self.set_sigma(sigma)
|
|
96
|
+
|
|
97
|
+
def set_sigma(self, sigma):
|
|
98
|
+
'''Set the sigma parameter for the Gaussian filter.'''
|
|
99
|
+
self.sigma = 1
|
|
100
|
+
self.gauss = GaussFilter.gaussian_kernel(
|
|
101
|
+
max(3, math.ceil(3 * sigma + 1)), sigma
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def gaussian_kernel(dim, sigma):
|
|
105
|
+
x = cv2.getGaussianKernel(dim, sigma)
|
|
106
|
+
kernel = x.dot(x.T)
|
|
107
|
+
return kernel
|
|
108
|
+
|
|
109
|
+
def run(self, image: np.ndarray) -> np.ndarray:
|
|
110
|
+
return signal.convolve2d(image, np.rot90(self.gauss), mode='same')
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BANDPASS_TYPES(Enum):
|
|
114
|
+
Gaussian = 1
|
|
115
|
+
Butterworth = 2
|
|
116
|
+
Ideal = 3
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_string(cls, s: str):
|
|
120
|
+
for column in cls:
|
|
121
|
+
if column.name.lower() == s.lower() or s.lower() in column.name.lower():
|
|
122
|
+
return column
|
|
123
|
+
raise ValueError(f'{cls.__name__} has no value matching "{s}"')
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def values(cls):
|
|
127
|
+
return [column.name for column in cls]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class BandpassFilter(AbstractFilter):
|
|
131
|
+
def __init__(
|
|
132
|
+
self, center=40.0, width=90.0, filter_type=BANDPASS_TYPES.Gaussian, show=False
|
|
133
|
+
) -> None:
|
|
134
|
+
'''Bandpass filter initialization.'''
|
|
135
|
+
self._radial_coordinates = None
|
|
136
|
+
self._filter = None
|
|
137
|
+
self._center = center
|
|
138
|
+
self._width = width
|
|
139
|
+
self._type = filter_type
|
|
140
|
+
self._show_filter = show
|
|
141
|
+
self._refresh = True
|
|
142
|
+
|
|
143
|
+
def radial_coordinates(self, shape):
|
|
144
|
+
'''Generates a 2D array with radial cordinates
|
|
145
|
+
with according to the first two axis of the
|
|
146
|
+
supplied shape tuple
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
R, Rsq
|
|
151
|
+
Radius 2d matrix (R) and radius squared matrix (Rsq)
|
|
152
|
+
'''
|
|
153
|
+
y_len = np.arange(-shape[0] // 2, shape[0] // 2)
|
|
154
|
+
x_len = np.arange(-shape[1] // 2, shape[1] // 2)
|
|
155
|
+
|
|
156
|
+
X, Y = np.meshgrid(x_len, y_len)
|
|
157
|
+
|
|
158
|
+
Rsq = X**2 + Y**2
|
|
159
|
+
|
|
160
|
+
self._radial_coordinates = (np.sqrt(Rsq), Rsq)
|
|
161
|
+
|
|
162
|
+
return self._radial_coordinates
|
|
163
|
+
|
|
164
|
+
def ideal_bandpass_filter(self, shape, cutoff, cuton):
|
|
165
|
+
'''Generates an ideal bandpass filter of shape
|
|
166
|
+
|
|
167
|
+
Params
|
|
168
|
+
-------
|
|
169
|
+
shape
|
|
170
|
+
the shape of filter matrix
|
|
171
|
+
cutoff
|
|
172
|
+
the cutoff frequency (low)
|
|
173
|
+
cuton
|
|
174
|
+
the cuton frequency (high)
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
filter (np.ndarray)
|
|
179
|
+
the filter in fourier space
|
|
180
|
+
'''
|
|
181
|
+
|
|
182
|
+
cir_filter = np.zeros((shape[0], shape[1]), dtype=np.float32)
|
|
183
|
+
cir_center = (shape[1] // 2, shape[0] // 2)
|
|
184
|
+
cir_filter = cv2.circle(cir_filter, cir_center, cuton, 1, -1)
|
|
185
|
+
cir_filter = cv2.circle(cir_filter, cir_center, cutoff, 0, -1)
|
|
186
|
+
return cir_filter
|
|
187
|
+
|
|
188
|
+
def gauss_bandpass_filter(self, shape, center, width):
|
|
189
|
+
'''Generates a Gaussian bandpass filter of shape
|
|
190
|
+
|
|
191
|
+
Params
|
|
192
|
+
-------
|
|
193
|
+
shape
|
|
194
|
+
the shape of filter matrix
|
|
195
|
+
center
|
|
196
|
+
the center frequency
|
|
197
|
+
width
|
|
198
|
+
the filter bandwidth
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
filter (np.ndarray)
|
|
203
|
+
the filter in fourier space
|
|
204
|
+
'''
|
|
205
|
+
if self._radial_coordinates is None:
|
|
206
|
+
R, Rsq = self.radial_coordinates(shape)
|
|
207
|
+
elif self._radial_coordinates[0].shape != shape[:2]:
|
|
208
|
+
R, Rsq = self.radial_coordinates(shape)
|
|
209
|
+
else:
|
|
210
|
+
R, Rsq = self._radial_coordinates
|
|
211
|
+
|
|
212
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
213
|
+
filter = np.exp(-(((Rsq - center**2) / (R * width)) ** 2))
|
|
214
|
+
filter[filter == np.inf] = 0
|
|
215
|
+
|
|
216
|
+
a, b = np.unravel_index(R.argmin(), R.shape)
|
|
217
|
+
|
|
218
|
+
filter[a, b] = 1
|
|
219
|
+
|
|
220
|
+
return filter
|
|
221
|
+
|
|
222
|
+
def butterworth_bandpass_filter(self, shape, center, width):
|
|
223
|
+
'''Generates a Gaussian bandpass filter of shape
|
|
224
|
+
|
|
225
|
+
Params
|
|
226
|
+
-------
|
|
227
|
+
shape
|
|
228
|
+
the shape of filter matrix
|
|
229
|
+
center
|
|
230
|
+
the center frequency
|
|
231
|
+
width
|
|
232
|
+
the filter bandwidth
|
|
233
|
+
|
|
234
|
+
Returns
|
|
235
|
+
-------
|
|
236
|
+
filter (np.ndarray)
|
|
237
|
+
the filter in fourier space
|
|
238
|
+
'''
|
|
239
|
+
if self._radial_coordinates is None:
|
|
240
|
+
R, Rsq = self.radial_coordinates(shape)
|
|
241
|
+
elif self._radial_coordinates[0].shape != shape[:2]:
|
|
242
|
+
R, Rsq = self.radial_coordinates(shape)
|
|
243
|
+
else:
|
|
244
|
+
R, Rsq = self._radial_coordinates
|
|
245
|
+
|
|
246
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
247
|
+
filter = 1 - (1 / (1 + ((R * width) / (Rsq - center**2)) ** 10))
|
|
248
|
+
filter[filter == np.inf] = 0
|
|
249
|
+
|
|
250
|
+
a, b = np.unravel_index(R.argmin(), R.shape)
|
|
251
|
+
|
|
252
|
+
filter[a, b] = 1
|
|
253
|
+
|
|
254
|
+
return filter
|
|
255
|
+
|
|
256
|
+
def run(self, image: np.ndarray):
|
|
257
|
+
'''Applies an FFT bandpass filter to the 2D image.
|
|
258
|
+
|
|
259
|
+
Params
|
|
260
|
+
-------
|
|
261
|
+
image (np.ndarray)
|
|
262
|
+
the image to be filtered.
|
|
263
|
+
'''
|
|
264
|
+
|
|
265
|
+
# time = QDateTime.currentDateTime()
|
|
266
|
+
|
|
267
|
+
rows, cols = image.shape
|
|
268
|
+
nrows = cv2.getOptimalDFTSize(rows)
|
|
269
|
+
ncols = cv2.getOptimalDFTSize(cols)
|
|
270
|
+
pad_rows = nrows - rows
|
|
271
|
+
pad_cols = ncols - cols
|
|
272
|
+
|
|
273
|
+
# Ensure that pad_cols[1] and pad_rows[1] are at least 1
|
|
274
|
+
pad_rows = (pad_rows // 2, max(1, pad_rows - pad_rows // 2))
|
|
275
|
+
pad_cols = (pad_cols // 2, max(1, pad_cols - pad_cols // 2))
|
|
276
|
+
|
|
277
|
+
nimg = np.pad(image, (pad_rows, pad_cols), mode='reflect')
|
|
278
|
+
# nimg = np.zeros((nrows, ncols))
|
|
279
|
+
# nimg[:rows, :cols] = image
|
|
280
|
+
|
|
281
|
+
ft = fftshift(cv2.dft(np.float64(nimg), flags=cv2.DFT_COMPLEX_OUTPUT))
|
|
282
|
+
|
|
283
|
+
filter = np.ones(ft.shape[:2])
|
|
284
|
+
|
|
285
|
+
refresh = self._refresh
|
|
286
|
+
|
|
287
|
+
if self._filter is None:
|
|
288
|
+
refresh = True
|
|
289
|
+
elif self._filter.shape != ft.shape[:2]:
|
|
290
|
+
refresh = True
|
|
291
|
+
|
|
292
|
+
if refresh:
|
|
293
|
+
if self._type == BANDPASS_TYPES.Gaussian.name:
|
|
294
|
+
filter = self.gauss_bandpass_filter(ft.shape, self._center, self._width)
|
|
295
|
+
elif self._type == BANDPASS_TYPES.Butterworth.name:
|
|
296
|
+
filter = self.butterworth_bandpass_filter(
|
|
297
|
+
ft.shape, self._center, self._width
|
|
298
|
+
)
|
|
299
|
+
else:
|
|
300
|
+
cutoff = int(max(0, self._center - self._width // 2))
|
|
301
|
+
cuton = int(self._center + self._width // 2)
|
|
302
|
+
filter = self.ideal_bandpass_filter(ft.shape, cutoff, cuton)
|
|
303
|
+
|
|
304
|
+
self._filter = filter
|
|
305
|
+
else:
|
|
306
|
+
filter = self._filter
|
|
307
|
+
|
|
308
|
+
if self._show_filter:
|
|
309
|
+
cv2.namedWindow('BandpassFilter', cv2.WINDOW_NORMAL)
|
|
310
|
+
cv2.imshow('BandpassFilter', (filter * 255).astype(np.uint8))
|
|
311
|
+
|
|
312
|
+
img = np.zeros(nimg.shape, dtype=np.uint8)
|
|
313
|
+
ft[:, :, 0] *= filter
|
|
314
|
+
ft[:, :, 1] *= filter
|
|
315
|
+
idft = cv2.idft(ifftshift(ft))
|
|
316
|
+
idft = cv2.magnitude(idft[:, :, 0], idft[:, :, 1])
|
|
317
|
+
cv2.normalize(idft, img, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
|
|
318
|
+
|
|
319
|
+
# exex = time.msecsTo(QDateTime.currentDateTime())
|
|
320
|
+
return img[pad_rows[0] : -pad_rows[1], pad_cols[0] : -pad_cols[1]]
|
|
321
|
+
|
|
322
|
+
def set_params(
|
|
323
|
+
self, center: float, width: float, filter_type: BANDPASS_TYPES, show: bool
|
|
324
|
+
):
|
|
325
|
+
self._center = center
|
|
326
|
+
self._width = width
|
|
327
|
+
self._type = filter_type
|
|
328
|
+
self._show_filter = show
|
|
329
|
+
|
|
330
|
+
def get_metadata(self):
|
|
331
|
+
'''Return metadata about the Bandpass Fourier Filter.'''
|
|
332
|
+
return {
|
|
333
|
+
'name': 'Fourier Bandpass Filter',
|
|
334
|
+
'type': self._type,
|
|
335
|
+
'band center': self._center,
|
|
336
|
+
'band width': self._width,
|
|
337
|
+
'show filter': self._show_filter,
|
|
338
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import dask.array
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from microEye.analysis.filters.base import AbstractFilter
|
|
5
|
+
from microEye.utils.uImage import ZarrImageSequence
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TemporalMedianFilter(AbstractFilter):
|
|
9
|
+
|
|
10
|
+
def __init__(self, window=3) -> None:
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self._temporal_window = window
|
|
14
|
+
self._frames = None
|
|
15
|
+
self._median = None
|
|
16
|
+
|
|
17
|
+
def getFrames(
|
|
18
|
+
self, index,
|
|
19
|
+
dataHandler: ZarrImageSequence, c_slice=0, z_slice=0):
|
|
20
|
+
if self._temporal_window < 2:
|
|
21
|
+
self._frames = None
|
|
22
|
+
self._median = None
|
|
23
|
+
self._start = None
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
maximum = dataHandler.shapeTCZYX()[0]
|
|
27
|
+
step = 0
|
|
28
|
+
if self._temporal_window % 2:
|
|
29
|
+
step = self._temporal_window // 2
|
|
30
|
+
else:
|
|
31
|
+
step = (self._temporal_window // 2) - 1
|
|
32
|
+
|
|
33
|
+
self._start = max(
|
|
34
|
+
0,
|
|
35
|
+
min(
|
|
36
|
+
index - step,
|
|
37
|
+
maximum - self._temporal_window))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
data_slice = slice(
|
|
41
|
+
max(self._start, 0),
|
|
42
|
+
min(self._start + self._temporal_window, maximum),
|
|
43
|
+
1
|
|
44
|
+
)
|
|
45
|
+
return dask.array.from_array(
|
|
46
|
+
dataHandler.getSlice(data_slice, c_slice, z_slice))
|
|
47
|
+
|
|
48
|
+
def run(self, image: np.ndarray, frames: np.ndarray, roiInfo=None):
|
|
49
|
+
if frames is not None:
|
|
50
|
+
if roiInfo is None:
|
|
51
|
+
median = np.array(
|
|
52
|
+
dask.array.median(frames, axis=0))
|
|
53
|
+
|
|
54
|
+
img = image - median
|
|
55
|
+
else:
|
|
56
|
+
origin = roiInfo[0] # ROI (x,y)
|
|
57
|
+
dim = roiInfo[1] # ROI (w,h)
|
|
58
|
+
median = np.array(
|
|
59
|
+
dask.array.median(frames[
|
|
60
|
+
:,
|
|
61
|
+
int(origin[1]):int(origin[1] + dim[1]),
|
|
62
|
+
int(origin[0]):int(origin[0] + dim[0])
|
|
63
|
+
], axis=0))
|
|
64
|
+
|
|
65
|
+
img = image.astype(np.float64)
|
|
66
|
+
img[
|
|
67
|
+
int(origin[1]):int(origin[1] + dim[1]),
|
|
68
|
+
int(origin[0]):int(origin[0] + dim[0])] -= median
|
|
69
|
+
|
|
70
|
+
img[img < 0] = 0
|
|
71
|
+
# max_val = np.iinfo(image.dtype).max
|
|
72
|
+
# img *= 10 # 0.9 * max_val / img.max()
|
|
73
|
+
return img.astype(image.dtype)
|
|
74
|
+
else:
|
|
75
|
+
return image
|
|
76
|
+
|
|
File without changes
|