magscope 0.1.0__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.
- magscope/__init__.py +18 -0
- magscope/beadlock.py +231 -0
- magscope/camera.py +682 -0
- magscope/datatypes.py +572 -0
- magscope/hardware.py +45 -0
- magscope/processes.py +199 -0
- magscope/scope.py +326 -0
- magscope/scripting.py +305 -0
- magscope/utils.py +118 -0
- magscope/videoprocessing.py +435 -0
- magscope-0.1.0.dist-info/METADATA +484 -0
- magscope-0.1.0.dist-info/RECORD +15 -0
- magscope-0.1.0.dist-info/WHEEL +5 -0
- magscope-0.1.0.dist-info/licenses/license +674 -0
- magscope-0.1.0.dist-info/top_level.txt +1 -0
magscope/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from magscope.utils import (
|
|
2
|
+
AcquisitionMode,
|
|
3
|
+
crop_stack_to_rois,
|
|
4
|
+
date_timestamp_str,
|
|
5
|
+
Message,
|
|
6
|
+
numpy_type_to_qt_image_type,
|
|
7
|
+
PoolVideoFlag,
|
|
8
|
+
registerwithscript,
|
|
9
|
+
Units,
|
|
10
|
+
)
|
|
11
|
+
import magscope.gui
|
|
12
|
+
from magscope.gui import ControlPanelBase, WindowManager, TimeSeriesPlotBase
|
|
13
|
+
from magscope.processes import ManagerProcessBase
|
|
14
|
+
from magscope.hardware import HardwareManagerBase
|
|
15
|
+
from magscope.camera import CameraBase, CameraManager
|
|
16
|
+
from magscope.datatypes import MatrixBuffer
|
|
17
|
+
from magscope.scope import MagScope
|
|
18
|
+
from magscope.scripting import Script
|
magscope/beadlock.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from math import copysign, isnan
|
|
2
|
+
import numpy as np
|
|
3
|
+
from time import time
|
|
4
|
+
|
|
5
|
+
from magscope.gui import WindowManager
|
|
6
|
+
from magscope.processes import ManagerProcessBase
|
|
7
|
+
from magscope.utils import Message, registerwithscript
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BeadLockManager(ManagerProcessBase):
|
|
11
|
+
def __init__(self):
|
|
12
|
+
super().__init__()
|
|
13
|
+
|
|
14
|
+
# XY-Lock Properties
|
|
15
|
+
self.xy_lock_on: bool = False
|
|
16
|
+
self.xy_lock_interval: float
|
|
17
|
+
self.xy_lock_max: float
|
|
18
|
+
self._xy_lock_last_time: float = 0.
|
|
19
|
+
self._xy_lock_pending_moves: list[int] = []
|
|
20
|
+
|
|
21
|
+
# Z-Lock Properties
|
|
22
|
+
self.z_lock_on: bool = False
|
|
23
|
+
self.z_lock_bead: int = 0
|
|
24
|
+
self.z_lock_target: float | None = None
|
|
25
|
+
self.z_lock_interval: float
|
|
26
|
+
self.z_lock_max: float
|
|
27
|
+
self._z_lock_last_time: float = 0.
|
|
28
|
+
|
|
29
|
+
def setup(self):
|
|
30
|
+
self.xy_lock_interval = self.settings['xy-lock default interval']
|
|
31
|
+
self.xy_lock_max = self.settings['xy-lock default max']
|
|
32
|
+
self.z_lock_interval = self.settings['z-lock default interval']
|
|
33
|
+
self.z_lock_max = self.settings['z-lock default max']
|
|
34
|
+
|
|
35
|
+
def do_main_loop(self):
|
|
36
|
+
# XY-Lock Enabled
|
|
37
|
+
if self.xy_lock_on:
|
|
38
|
+
# Timer
|
|
39
|
+
if (now:=time()) - self._xy_lock_last_time > self.xy_lock_interval:
|
|
40
|
+
self.do_xy_lock(now=now)
|
|
41
|
+
|
|
42
|
+
# Z-Lock Enabled
|
|
43
|
+
if self.z_lock_on:
|
|
44
|
+
# Timer
|
|
45
|
+
if (now:=time()) - self._z_lock_last_time > self.z_lock_interval:
|
|
46
|
+
self.do_z_lock(now=now)
|
|
47
|
+
|
|
48
|
+
@registerwithscript('do_xy_lock')
|
|
49
|
+
def do_xy_lock(self, now=None):
|
|
50
|
+
""" Centers the bead-rois based on their tracked position """
|
|
51
|
+
|
|
52
|
+
# Gather information
|
|
53
|
+
width = self.settings['bead roi width']
|
|
54
|
+
half_width = width // 2
|
|
55
|
+
tracks = self.tracks_buffer.peak_unsorted().copy()
|
|
56
|
+
if now is None: now = time()
|
|
57
|
+
self._xy_lock_last_time = now
|
|
58
|
+
|
|
59
|
+
# For each bead calculate if/how much to move
|
|
60
|
+
for id, roi in self.bead_rois.items():
|
|
61
|
+
|
|
62
|
+
# Get the track for this bead
|
|
63
|
+
track = tracks[tracks[:, 4] == id, :]
|
|
64
|
+
|
|
65
|
+
# Check there is track data
|
|
66
|
+
if track.shape[0] == 0:
|
|
67
|
+
continue
|
|
68
|
+
|
|
69
|
+
# Get the latest position
|
|
70
|
+
idx = np.argmax(track[:, 0])
|
|
71
|
+
t, x, y, roi_x, roi_y = track[idx, [0, 1, 2, 5, 6]].tolist()
|
|
72
|
+
|
|
73
|
+
# Check the position is valid
|
|
74
|
+
if isnan(t) or isnan(x) or isnan(y):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# Check the position was recent
|
|
78
|
+
if now - t > 3*self.xy_lock_interval:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
# Check the bead-roi is current
|
|
82
|
+
if roi[0] != int(roi_x) or roi[2] != int(roi_y):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# Check the bead started the last move
|
|
86
|
+
if id in self._xy_lock_pending_moves:
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
# Calculate the move
|
|
90
|
+
nm_per_px = self.camera_type.nm_per_px / self.settings['magnification']
|
|
91
|
+
dx = (x / nm_per_px) - half_width - roi_x
|
|
92
|
+
dy = (y / nm_per_px) - half_width - roi_y
|
|
93
|
+
if abs(dx) <= 1:
|
|
94
|
+
dx = 0.
|
|
95
|
+
if abs(dy) <= 1:
|
|
96
|
+
dy = 0.
|
|
97
|
+
dx = round(dx)
|
|
98
|
+
dy = round(dy)
|
|
99
|
+
|
|
100
|
+
# Limit movement to the maximum threshold
|
|
101
|
+
dx = copysign(min(abs(dx), self.xy_lock_max), dx)
|
|
102
|
+
dy = copysign(min(abs(dy), self.xy_lock_max), dy)
|
|
103
|
+
|
|
104
|
+
# Move the bead as needed
|
|
105
|
+
if abs(dx) > 0. or abs(dy) > 0.:
|
|
106
|
+
self._xy_lock_pending_moves.append(id)
|
|
107
|
+
message = Message(
|
|
108
|
+
to=WindowManager,
|
|
109
|
+
meth=WindowManager.move_bead,
|
|
110
|
+
args=(id, dx, dy)
|
|
111
|
+
)
|
|
112
|
+
self.send_ipc(message)
|
|
113
|
+
|
|
114
|
+
@registerwithscript('do_z_lock')
|
|
115
|
+
def do_z_lock(self, now=None):
|
|
116
|
+
# Gather information
|
|
117
|
+
if now is None: now = time()
|
|
118
|
+
self._z_lock_last_time = now
|
|
119
|
+
|
|
120
|
+
raise NotImplementedError
|
|
121
|
+
|
|
122
|
+
def set_bead_rois(self, value: dict[int, tuple[int, int, int, int]]):
|
|
123
|
+
super().set_bead_rois(value)
|
|
124
|
+
|
|
125
|
+
# Check if any of the beads have been deleted
|
|
126
|
+
keys = list(self._xy_lock_pending_moves) # copy
|
|
127
|
+
for id in keys:
|
|
128
|
+
if id not in self.bead_rois:
|
|
129
|
+
self._xy_lock_pending_moves.pop(id)
|
|
130
|
+
|
|
131
|
+
def remove_bead_from_xy_lock_pending_moves(self, id: int):
|
|
132
|
+
if id in self._xy_lock_pending_moves:
|
|
133
|
+
self._xy_lock_pending_moves.remove(id)
|
|
134
|
+
|
|
135
|
+
@registerwithscript('set_xy_lock_on')
|
|
136
|
+
def set_xy_lock_on(self, value: bool):
|
|
137
|
+
self.xy_lock_on = value
|
|
138
|
+
|
|
139
|
+
from magscope.gui import WindowManager
|
|
140
|
+
message = Message(
|
|
141
|
+
to=WindowManager,
|
|
142
|
+
meth=WindowManager.update_xy_lock_enabled,
|
|
143
|
+
args=(value,)
|
|
144
|
+
)
|
|
145
|
+
self.send_ipc(message)
|
|
146
|
+
|
|
147
|
+
@registerwithscript('set_xy_lock_interval')
|
|
148
|
+
def set_xy_lock_interval(self, value: float):
|
|
149
|
+
self.xy_lock_interval = value
|
|
150
|
+
|
|
151
|
+
from magscope.gui import WindowManager
|
|
152
|
+
message = Message(
|
|
153
|
+
to=WindowManager,
|
|
154
|
+
meth=WindowManager.update_xy_lock_interval,
|
|
155
|
+
args=(value,)
|
|
156
|
+
)
|
|
157
|
+
self.send_ipc(message)
|
|
158
|
+
|
|
159
|
+
@registerwithscript('set_xy_lock_max')
|
|
160
|
+
def set_xy_lock_max(self, value: float):
|
|
161
|
+
value = max(1, round(value))
|
|
162
|
+
self.xy_lock_max = value
|
|
163
|
+
|
|
164
|
+
from magscope.gui import WindowManager
|
|
165
|
+
message = Message(
|
|
166
|
+
to=WindowManager,
|
|
167
|
+
meth=WindowManager.update_xy_lock_max,
|
|
168
|
+
args=(value,)
|
|
169
|
+
)
|
|
170
|
+
self.send_ipc(message)
|
|
171
|
+
|
|
172
|
+
@registerwithscript('set_z_lock_on')
|
|
173
|
+
def set_z_lock_on(self, value: bool):
|
|
174
|
+
self.z_lock_on = value
|
|
175
|
+
|
|
176
|
+
from magscope.gui import WindowManager
|
|
177
|
+
message = Message(
|
|
178
|
+
to=WindowManager,
|
|
179
|
+
meth=WindowManager.update_z_lock_enabled,
|
|
180
|
+
args=(value,)
|
|
181
|
+
)
|
|
182
|
+
self.send_ipc(message)
|
|
183
|
+
|
|
184
|
+
@registerwithscript('set_z_lock_bead')
|
|
185
|
+
def set_z_lock_bead(self, value: int):
|
|
186
|
+
value = int(value)
|
|
187
|
+
self.z_lock_bead = value
|
|
188
|
+
|
|
189
|
+
from magscope.gui import WindowManager
|
|
190
|
+
message = Message(
|
|
191
|
+
to=WindowManager,
|
|
192
|
+
meth=WindowManager.update_z_lock_bead,
|
|
193
|
+
args=(value,)
|
|
194
|
+
)
|
|
195
|
+
self.send_ipc(message)
|
|
196
|
+
|
|
197
|
+
@registerwithscript('set_z_lock_target')
|
|
198
|
+
def set_z_lock_target(self, value: float):
|
|
199
|
+
self.z_lock_target = value
|
|
200
|
+
|
|
201
|
+
from magscope.gui import WindowManager
|
|
202
|
+
message = Message(
|
|
203
|
+
to=WindowManager,
|
|
204
|
+
meth=WindowManager.update_z_lock_target,
|
|
205
|
+
args=(value,)
|
|
206
|
+
)
|
|
207
|
+
self.send_ipc(message)
|
|
208
|
+
|
|
209
|
+
@registerwithscript('set_z_lock_interval')
|
|
210
|
+
def set_z_lock_interval(self, value: float):
|
|
211
|
+
self.z_lock_interval = value
|
|
212
|
+
|
|
213
|
+
from magscope.gui import WindowManager
|
|
214
|
+
message = Message(
|
|
215
|
+
to=WindowManager,
|
|
216
|
+
meth=WindowManager.update_z_lock_interval,
|
|
217
|
+
args=(value,)
|
|
218
|
+
)
|
|
219
|
+
self.send_ipc(message)
|
|
220
|
+
|
|
221
|
+
@registerwithscript('set_z_lock_max')
|
|
222
|
+
def set_z_lock_max(self, value: float):
|
|
223
|
+
self.z_lock_max = value
|
|
224
|
+
|
|
225
|
+
from magscope.gui import WindowManager
|
|
226
|
+
message = Message(
|
|
227
|
+
to=WindowManager,
|
|
228
|
+
meth=WindowManager.update_z_lock_max,
|
|
229
|
+
args=(value,)
|
|
230
|
+
)
|
|
231
|
+
self.send_ipc(message)
|