forked from pgriffin17/cameraControl
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcamera.py
More file actions
226 lines (173 loc) · 7.23 KB
/
camera.py
File metadata and controls
226 lines (173 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore
import zwoasi as asi
import numpy as np
from roi_manager import ROI_Manager
from displayimageitem import Display_Imi
from bg_roi import Background_ROI
from camera_settings_widget import Camera_Settings_Widget
from spot_tracker import Spot_Tracker
class CameraController(QtCore.QObject):
start_req = QtCore.Signal() # Use to start camera
stop_req = QtCore.Signal() # Sent at close to end camera with no error
def __init__(self, parent = None, roi_manager: ROI_Manager = None):
super().__init__(parent)
self.is_active: bool = False
print("camera time")
# Initializations
asi.init(r"C:\Program Files\ASIStudio\ASICamera2.dll")
try:
print(F"Cameras attached: {asi.list_cameras()}")
self.camera = asi.Camera(asi.list_cameras()[0])
except IndexError as ie:
print("No camera is connected. Camera controller will not function.", ie)
return
if roi_manager:
self.bg_roi = roi_manager.bg_roi
self.bg_roi.sigRegionChangeFinished.connect(self.bg_array_moved)
self.ee_roi = roi_manager.ee_roi
self.settings = Camera_Settings(self.camera)
self.imi = Display_Imi()
# Set Up Worker
self.worker = Camera_Worker(self.camera, self, self.settings)
self.start_req.connect(self.worker.start_live)
self.stop_req.connect(self.worker.end_live)
self.worker_thread = QtCore.QThread()
self.worker.stopped.connect(self.clear_worker_thread)
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
self.worker.first_frame.connect(self.bg_array_moved)
print("Camera Initialized")
# Set up display
self.worker.frame_ready.connect(self.imi.setImage)
#Set up for quit:
app = QtCore.QCoreApplication.instance()
app.aboutToQuit.connect(self.about_to_quit)
def start_live_view(self):
self.set_active()
self.start_req.emit()
def set_active(self, enabled: bool = True):
'''
Sets the camera to an active or inactive state.
'''
self.is_active = enabled
def bg_array_moved(self):
'''
Tells the processor that the slice needs to be updated when bg roi is moved
'''
if self.is_active:
self.bg_roi_slice = self.bg_roi.getArraySlice(self.imi.image, self.imi)[0]
def about_to_quit(self):
'''
Runs at application exit, closes thread so we don't get errors.
'''
print("about to quit")
self.set_active(False)
self.stop_req.emit()
def clear_worker_thread(self):
print("clearing worker thread")
self.worker_thread.quit()
self.worker_thread.wait()
class Camera_Settings(QtCore.QObject):
'''
Settings class that every camera object will have.
'''
bins: int = 4
image_type = asi.ASI_IMG_RAW16
exposure = 32
gain = 1
def __init__(self, camera: asi.Camera, parent = None):
super().__init__(parent)
self.camera = camera
self.camera.set_roi(bins=self.bins)
self.camera.set_image_type(self.image_type)
self.camera.set_control_value(asi.ASI_EXPOSURE, self.exposure)
self.camera.set_control_value(asi.ASI_GAIN, self.gain)
self.timeout = self.get_timeout()
self.widget = Camera_Settings_Widget()
self.set_up_widget()
def set_up_widget(self):
self.widget.exposure_widget.slider.valueChanged.connect(self.set_exposure)
self.widget.gain_widget.slider.valueChanged.connect(self.set_gain)
def get_timeout(self):
'''
Returns the timeout setting of the camera.
'''
self.timeout = (self.camera.get_control_value(asi.ASI_EXPOSURE)[0] * 2000) + 500 # Recommended in docs.
return (self.timeout)
def set_exposure(self, exposure: int):
"""
Sets camera exposure (microseconds)
"""
self.exposure = exposure
self.camera.set_control_value(asi.ASI_EXPOSURE, self.exposure)
print(f"Exposure set to {self.exposure}")
def set_gain(self, gain: int):
self.gain = gain
self.camera.set_control_value(asi.ASI_GAIN, self.gain)
print(f"Gain set to {self.gain}")
class Camera_Worker(QtCore.QObject):
frame_ready = QtCore.Signal(np.ndarray) # Emitted when a new frame has been processed, contains frame.
com_ready = QtCore.Signal(tuple) # Emitted when a new frame has been processed. Contains center of mass of frame.
first_frame = QtCore.Signal() # Emitted with the first frame processed to help GUI set up for live view.
frame_raw_stats = QtCore.Signal(dict)
raw_frame_ready =QtCore.Signal(np.ndarray)
stopped = QtCore.Signal()
def __init__(self, camera: asi.Camera, camera_controller : CameraController, settings: Camera_Settings = None, parent = None):
super().__init__(parent)
self.settings = settings
self.camera = camera
self.controller = camera_controller
self.spot_tracker = Spot_Tracker(self.controller.imi, self.controller.ee_roi)
def start_live(self):
print("Starting live view")
self.camera.start_video_capture()
self.capture_and_process_frame(bg_sub=False)
self.first_frame.emit()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.run_live)
self.timer.start(75)
def run_live(self):
self.capture_and_process_frame()
def capture_and_process_frame(self, bg_sub: bool = True) -> tuple[tuple, np.ndarray, dict]:
frame = self.camera.capture_video_frame(timeout=self.settings.get_timeout())
self.raw_frame_ready.emit(frame)
raw_frame_stats = {"Min": np.min(frame), "Max": np.max(frame)}
self.frame_raw_stats.emit(raw_frame_stats)
if bg_sub:
background_counts = np.average(frame[self.controller.bg_roi_slice])
# Cast to float32 for safe subtraction
temp = frame.astype(np.float32)
bg_subbed_frame = temp - np.float32(background_counts)
# Clamp negative values to 0
bg_subbed_frame[bg_subbed_frame < 0] = 0
# Convert back to uint16
bg_subbed_frame = bg_subbed_frame.astype(np.uint16)
self.frame_ready.emit(bg_subbed_frame)
self.spot_tracker.get_com(bg_subbed_frame)
else:
self.frame_ready.emit(frame)
self.spot_tracker.get_com(frame)
def end_live(self):
print("Ending live view")
self.timer.stop()
print("Timer Stopped")
self.timer.deleteLater()
print("Timer.deleted")
try:
self.camera.stop_video_capture()
print("stopped camera")
except Exception as e:
print(f"Failed to stop camera: {e}")
QtCore.QMetaObject.invokeMethod(
QtCore.QThread.currentThread(),
"quit",
QtCore.Qt.QueuedConnection
)
self.stopped.emit()
print("Stopped emitted")
if __name__ == "__main__":
app = pg.mkQApp()
object = CameraController()
object.start_live_view()
app.exec()