def __init__( self, stop_event: LoggedEvent, saving_signal: LoggedEvent, wait_signal: LoggedEvent, noise_subtraction_on: Event, camera_queue: ArrayQueue, saver_queue: ArrayQueue, max_queue_size=1200, ): super().__init__(name="dispatcher") self.stop_event = stop_event self.saving_signal = saving_signal.new_reference(self.logger) self.wait_signal = wait_signal.new_reference(self.logger) self.noise_subtraction_active = noise_subtraction_on.new_reference( self.logger) self.camera_queue = camera_queue self.saver_queue = saver_queue self.n_planes_queue = Queue() self.viewer_queue = ArrayQueue(max_mbytes=max_queue_size) self.calibration_ref_queue = ArrayQueue() self.volume_buffer = None self.calibration_ref = None self.n_planes = 1 self.i_plane = 0 self.first_volume = True
def __init__( self, stop_event: LoggedEvent, is_saving_event: LoggedEvent, duration_queue: Queue, max_queue_size=2000, ): super().__init__(name="saver") self.stop_event = stop_event.new_reference(self.logger) self.save_queue = ArrayQueue(max_mbytes=max_queue_size) self.saving_signal = is_saving_event self.saver_stopped_signal = LoggedEvent(self.logger, SashimiEvents.SAVING_STOPPED) self.saving = False self.saving_parameter_queue = Queue() self.save_parameters: Optional[SavingParameters] = SavingParameters() self.i_in_chunk = 0 self.i_chunk = 0 self.i_plane = 0 self.i_volume = 0 self.n_volumes = 10 self.current_data = None self.saved_status_queue = Queue() self.frame_shape = None self.dtype = np.uint16 self.duration_queue = duration_queue self.notifier = notifiers[conf["notifier"]]
class ImageReconstructor(Process): def __init__(self, data_in_queue, stop_event, max_mbytes_queue=300): super().__init__() self.data_in_queue = data_in_queue self.parameter_queue = Queue() self.stop_event = stop_event self.output_queue = ArrayQueue(max_mbytes_queue) self.scanning_parameters = None self.waveform = None def run(self): while not self.stop_event.is_set(): try: self.scanning_parameters = self.parameter_queue.get( timeout=0.001) self.waveform = compute_waveform(self.scanning_parameters) except Empty: pass try: image = self.data_in_queue.get(timeout=0.001) self.output_queue.put( scanning_patterns.reconstruct_image_pattern( np.roll(image, self.scanning_parameters.mystery_offset), *self.waveform, (self.scanning_parameters.n_x, self.scanning_parameters.n_y), self.scanning_parameters.n_bin, )) except Empty: pass
def __init__(self, data_in_queue, stop_event, max_mbytes_queue=300): super().__init__() self.data_in_queue = data_in_queue self.parameter_queue = Queue() self.stop_event = stop_event self.output_queue = ArrayQueue(max_mbytes_queue) self.scanning_parameters = None self.waveform = None
def __init__(self, experiment_start_event, duration_queue, max_queuesize=200): super().__init__() self.data_queue = ArrayQueue(max_mbytes=max_queuesize) self.parameter_queue = Queue() self.stop_event = Event() self.experiment_start_event = experiment_start_event self.scanning_parameters = ScanningParameters() self.new_parameters = copy(self.scanning_parameters) self.duration_queue = duration_queue self.n_frames_queue = Queue()
def __init__(self, *args, signal_start_rec, output_queue_mb=500, **kwargs): super().__init__(*args, **kwargs) self.save_queue = ArrayQueue(max_mbytes=output_queue_mb) self.framestart_queue = Queue() self.diagnostic_queue = Queue() self.processing_parameters = MovementDetectionParameters( ).get_clean_values() self.signal_start_rec = signal_start_rec self.mem_use = 0 self.diagnostic_params = [ "n_pixels_difference", "recording_state", "n_images_in_buffer", ]
def __init__(self, n_items=100, timestamped=False, indexed=False, n_mbytes=2, wait=0, test_full=False): super().__init__() self.source_array = IndexedArrayQueue(max_mbytes==n_mbytes) if indexed \ else (TimestampedArrayQueue(max_mbytes=n_mbytes) if timestamped \ else ArrayQueue(max_mbytes=n_mbytes)) self.n_items = n_items self.wait = wait self.test_full = test_full
def __init__(self, diagnostics=False): super().__init__() if diagnostics: self.sequence_queue = Queue() self.sequence_diagram = SequenceDiagram(self.sequence_queue, "main") self.experiment_start_event = Event() self.scanning_settings = ScanningSettings() self.experiment_settings = ExperimentSettings() self.pause_after = False self.parameter_tree = ParameterTree() self.parameter_tree.add(self.scanning_settings) self.parameter_tree.add(self.experiment_settings) self.end_event = Event() self.external_sync = ZMQcomm() self.duration_queue = Queue() self.scanner = Scanner(self.experiment_start_event, duration_queue=self.duration_queue) self.scanning_parameters = None self.reconstructor = ImageReconstructor(self.scanner.data_queue, self.scanner.stop_event) self.save_queue = ArrayQueue(max_mbytes=800) self.saver = StackSaver(self.scanner.stop_event, self.save_queue, self.scanner.n_frames_queue) self.save_status: Optional[SavingStatus] = None self.motors = dict() self.motors["x"] = MotorControl("COM6", axes="x") self.motors["y"] = MotorControl("COM6", axes="y") self.motors["z"] = MotorControl("COM6", axes="z") self.power_controller = LaserPowerControl() self.scanning_settings.sig_param_changed.connect(self.send_scan_params) self.scanning_settings.sig_param_changed.connect(self.send_save_params) self.scanner.start() self.reconstructor.start() self.saver.start() self.open_setup() self.paused = False
def __init__( self, stop_event: LoggedEvent, wait_event: LoggedEvent, exp_trigger_event: LoggedEvent, camera_id=0, max_queue_size=1200, n_fps_frames=20, ): super().__init__(name="camera") # Queue to communicate self.triggered_frame_rate_queue = Queue() self.parameter_queue = Queue() self.stop_event = stop_event.new_reference(self.logger) self.wait_event = wait_event.new_reference(self.logger) self.experiment_trigger_event = exp_trigger_event.new_reference( self.logger) self.image_queue = ArrayQueue(max_mbytes=max_queue_size) self.camera_id = camera_id self.camera = None self.parameters = CamParameters() self.framerate_rec = FramerateRecorder(n_fps_frames=n_fps_frames) self.was_waiting = False
def __init__( self, stop_event: LoggedEvent, waiting_event: LoggedEvent, restart_event: LoggedEvent, start_experiment_from_scanner=False, n_samples_waveform=10000, sample_rate=40000, ): """""" super().__init__(name="scanner") self.stop_event = stop_event.new_reference(self.logger) self.restart_event = restart_event.new_reference(self.logger) self.wait_signal = waiting_event.new_reference(self.logger) self.parameter_queue = Queue() self.waveform_queue = ArrayQueue(max_mbytes=100) self.n_samples = n_samples_waveform self.sample_rate = sample_rate self.parameters = ScanParameters() self.start_experiment_from_scanner = start_experiment_from_scanner
class VolumeDispatcher(LoggingProcess): """ Parameters ---------- stop_event saving_signal wait_signal noise_subtraction_on camera_queue saver_queue max_queue_size """ def __init__( self, stop_event: LoggedEvent, saving_signal: LoggedEvent, wait_signal: LoggedEvent, noise_subtraction_on: Event, camera_queue: ArrayQueue, saver_queue: ArrayQueue, max_queue_size=1200, ): super().__init__(name="dispatcher") self.stop_event = stop_event self.saving_signal = saving_signal.new_reference(self.logger) self.wait_signal = wait_signal.new_reference(self.logger) self.noise_subtraction_active = noise_subtraction_on.new_reference( self.logger) self.camera_queue = camera_queue self.saver_queue = saver_queue self.n_planes_queue = Queue() self.viewer_queue = ArrayQueue(max_mbytes=max_queue_size) self.calibration_ref_queue = ArrayQueue() self.volume_buffer = None self.calibration_ref = None self.n_planes = 1 self.i_plane = 0 self.first_volume = True def run(self): self.logger.log_message("started") while not self.stop_event.is_set(): self.receive_options() self.get_frame() self.close_log() def process_frame(self, current_frame): if self.calibration_ref is not None and self.noise_subtraction_active.is_set( ): current_frame = neg_dif(current_frame, self.calibration_ref) if self.first_volume or self.volume_buffer.shape[ 1:3] != current_frame.shape: self.volume_buffer = np.empty( (self.n_planes, *current_frame.shape), dtype=np.uint16) self.first_volume = False self.logger.log_message(f"received plane {self.i_plane}") self.volume_buffer[self.i_plane, :, :] = current_frame self.i_plane += 1 if self.i_plane == self.n_planes: self.fill_queues() self.i_plane = 0 def fill_queues(self): if self.viewer_queue.queue.qsize() < 3: self.viewer_queue.put(self.volume_buffer) else: pass # volume has been dropped from the viewer if self.saving_signal.is_set(): self.saver_queue.put(self.volume_buffer) def get_frame(self): if self.wait_signal.is_set(): self.logger.log_message("wait starting") self.saver_queue.clear() while self.wait_signal.is_set(): try: _ = self.camera_queue.get(timeout=TIMEOUT_S) except Empty: pass self.logger.log_message("wait over") self.i_plane = 0 try: current_frame = self.camera_queue.get(timeout=TIMEOUT_S) self.process_frame(current_frame) except Empty: pass def receive_options(self): # Get number of planes: n_planes = get_last_parameters(self.n_planes_queue, timeout=TIMEOUT_S) if n_planes is not None: self.n_planes = n_planes self.reset() # Get flat noise image to subtract: calibration_ref = get_last_parameters(self.calibration_ref_queue, timeout=TIMEOUT_S) if calibration_ref is not None: self.calibration_ref = calibration_ref def reset(self): self.first_volume = True self.i_plane = 0
class StackSaver(LoggingProcess): def __init__( self, stop_event: LoggedEvent, is_saving_event: LoggedEvent, duration_queue: Queue, max_queue_size=2000, ): super().__init__(name="saver") self.stop_event = stop_event.new_reference(self.logger) self.save_queue = ArrayQueue(max_mbytes=max_queue_size) self.saving_signal = is_saving_event self.saver_stopped_signal = LoggedEvent(self.logger, SashimiEvents.SAVING_STOPPED) self.saving = False self.saving_parameter_queue = Queue() self.save_parameters: Optional[SavingParameters] = SavingParameters() self.i_in_chunk = 0 self.i_chunk = 0 self.i_plane = 0 self.i_volume = 0 self.n_volumes = 10 self.current_data = None self.saved_status_queue = Queue() self.frame_shape = None self.dtype = np.uint16 self.duration_queue = duration_queue self.notifier = notifiers[conf["notifier"]] def run(self): self.logger.log_message("started") while not self.stop_event.is_set(): if self.saving_signal.is_set( ) and self.save_parameters is not None: self.save_loop() else: self.receive_save_parameters() self.close_log() def save_loop(self): notifier = self.notifier("lightsheet", **conf["notifier_options"]) # remove files if some are found at the save location Path(self.save_parameters.output_dir).mkdir(parents=True, exist_ok=True) if (Path(self.save_parameters.output_dir) / "original" / "stack_metadata.json").is_file(): shutil.rmtree(Path(self.save_parameters.output_dir) / "original") (Path(self.save_parameters.output_dir) / "original").mkdir( parents=True, exist_ok=True) self.i_in_chunk = 0 self.i_chunk = 0 self.i_volume = 0 self.current_data = None while (self.i_volume < self.n_volumes and self.saving_signal.is_set() and not self.stop_event.is_set()): self.receive_save_parameters() try: frame = self.save_queue.get(timeout=0.01) self.logger.log_message("received volume") self.fill_dataset(frame) except Empty: pass if self.i_volume > 0: if self.i_in_chunk != 0: self.save_chunk() self.update_saved_status_queue() self.finalize_dataset() self.current_data = None if self.saving_signal.is_set(): notifier.notify() self.saving_signal.clear() self.saver_stopped_signal.set() # Update status queue by resetting it: self.update_saved_status_queue() self.i_in_chunk = 0 self.i_chunk = 0 self.i_volume = 0 self.save_parameters = None def fill_dataset(self, volume): if self.current_data is None: self.calculate_optimal_size(volume) self.current_data = np.empty( (self.save_parameters.chunk_size, *volume.shape), dtype=self.dtype, ) self.current_data[self.i_in_chunk, :, :, :] = volume self.i_volume += 1 self.i_in_chunk += 1 self.update_saved_status_queue() if self.i_in_chunk == self.save_parameters.chunk_size: self.save_chunk() def update_saved_status_queue(self): self.saved_status_queue.put( SavingStatus( target_params=self.save_parameters, i_in_chunk=self.i_in_chunk, i_chunk=self.i_chunk, i_volume=self.i_volume, n_volumes=self.n_volumes, )) def finalize_dataset(self): self.logger.log_message("finished saving") with open( (Path(self.save_parameters.output_dir) / "original" / "stack_metadata.json"), "w", ) as f: json.dump( { "shape_full": ( self.n_volumes, *self.current_data.shape[1:], ), "shape_block": ( self.save_parameters.chunk_size, *self.current_data.shape[1:], ), "crop_start": [0, 0, 0, 0], "crop_end": [0, 0, 0, 0], "padding": [0, 0, 0, 0], "voxel_size": self.save_parameters.voxel_size, }, f, ) def save_chunk(self): self.logger.log_message("saved chunk") fl.save( Path(self.save_parameters.output_dir) / "original/{:04d}.h5".format(self.i_chunk), {"stack_4D": self.current_data[:self.i_in_chunk, :, :, :]}, compression="blosc", ) self.i_in_chunk = 0 self.i_chunk += 1 def calculate_optimal_size(self, volume): if self.dtype == np.uint16: array_megabytes = (2 * volume.shape[0] * volume.shape[1] * volume.shape[2] / 1048576) else: raise TypeError( "Saving data type not supported. Only uint16 is supported") self.save_parameters.chunk_size = int( self.save_parameters.optimal_chunk_MB_RAM / array_megabytes) def receive_save_parameters(self): """Receive parameters on the stack shape from the `State` obj and new duration from either the `EsternalTrigger` or the `State` if triggering is disabled. """ # Get parameters: parameters = get_last_parameters(self.saving_parameter_queue) if parameters is not None: self.save_parameters = parameters # Get duration and update number of volumes: new_duration = get_last_parameters(self.duration_queue) if new_duration is not None: self.n_volumes = int( np.ceil(self.save_parameters.volumerate * new_duration))
class CameraProcess(LoggingProcess): """Process that handles the setting of new parameters to and the acquisition of frames from the camera. Parameters ---------- stop_event wait_event exp_trigger_event camera_id max_queue_size n_fps_frames """ def __init__( self, stop_event: LoggedEvent, wait_event: LoggedEvent, exp_trigger_event: LoggedEvent, camera_id=0, max_queue_size=1200, n_fps_frames=20, ): super().__init__(name="camera") # Queue to communicate self.triggered_frame_rate_queue = Queue() self.parameter_queue = Queue() self.stop_event = stop_event.new_reference(self.logger) self.wait_event = wait_event.new_reference(self.logger) self.experiment_trigger_event = exp_trigger_event.new_reference( self.logger) self.image_queue = ArrayQueue(max_mbytes=max_queue_size) self.camera_id = camera_id self.camera = None self.parameters = CamParameters() self.framerate_rec = FramerateRecorder(n_fps_frames=n_fps_frames) self.was_waiting = False def initialize_camera(self): if conf["scopeless"]: self.camera = camera_class_dict["mock"]() else: self.camera = camera_class_dict[conf["camera"]["name"]]( camera_id=conf["camera"]["id"], max_sensor_resolution=tuple( conf["camera"]["max_sensor_resolution"]), ) def run(self): self.logger.log_message("started") self.initialize_camera() self.run_camera() self.camera.shutdown() self.logger.close() def run_camera(self): """Main run for the camera. Depending on whether the camera mode is PAUSED or not, either the self.pause_loop or self.camera_loop are executed. """ while not self.stop_event.is_set(): # removed if statement based on camera mode: toggeling between modes is done in loops. self.pause_loop() self.camera_loop() def pause_loop(self): """Camera idle loop, just wait until parameters are updated. Check them, and if CameraMode.PAUSED is still set, return here. """ self.logger.log_message("Paused aquisition") self.camera.stop_acquisition() while (not self.stop_event.is_set() and self.parameters.camera_mode == CameraMode.PAUSED): try: new_parameters = self.parameter_queue.get(timeout=0.001) if new_parameters != self.parameters: self.update_parameters(new_parameters, stop_start=False) if self.parameters.camera_mode != CameraMode.PAUSED: break except Empty: pass def camera_loop(self): """Camera running loop, grab frames and set new parameters if available.""" self.logger.log_message("Started acquisition") self.camera.start_acquisition() while (not self.stop_event.is_set() and self.parameters.camera_mode != CameraMode.PAUSED): is_waiting = self.wait_event.is_set() frames = self.camera.get_frames() # if no frames are received (either this loop is in between frames # or we are in the waining period) if frames: for frame in frames: self.logger.log_message("received frame of shape " + str(frame.shape)) # this means this is the first frame received since # the waiting period is over, the signal has to be sent that # saving can start if self.was_waiting and not is_waiting: self.experiment_trigger_event.set() # TODO do not crash here if queue is full self.image_queue.put(frame) self.was_waiting = is_waiting self.update_framerate() # Empty parameters queue and set new parameters with the most recent value new_parameters = get_last_parameters(self.parameter_queue, timeout=0.001) if new_parameters is not None: if new_parameters.camera_mode == CameraMode.ABORT or ( new_parameters != self.parameters): self.update_parameters(new_parameters) def update_parameters(self, new_parameters, stop_start=True): """ "Set new parameters and stop and start the camera to make sure all changes take place.""" self.parameters = new_parameters if stop_start: self.camera.stop_acquisition() # In general, ROI and binning are a bit funny in their interactions, and need to be handled # carefully in the specific camera interfaces. for attribute in ["binning", "roi", "exposure_time", "trigger_mode"]: setattr(self.camera, attribute, getattr(self.parameters, attribute)) if stop_start: self.camera.start_acquisition() self.framerate_rec.restart() self.logger.log_message("Updated parameters " + str(self.parameters)) def update_framerate(self): self.framerate_rec.update_framerate() if self.framerate_rec.i_fps == 0: self.triggered_frame_rate_queue.put( self.framerate_rec.current_framerate) def close_camera(self): self.camera.shutdown()
class Scanner(Process): def __init__(self, experiment_start_event, duration_queue, max_queuesize=200): super().__init__() self.data_queue = ArrayQueue(max_mbytes=max_queuesize) self.parameter_queue = Queue() self.stop_event = Event() self.experiment_start_event = experiment_start_event self.scanning_parameters = ScanningParameters() self.new_parameters = copy(self.scanning_parameters) self.duration_queue = duration_queue self.n_frames_queue = Queue() def run(self): self.compute_scan_parameters() self.run_scanning() def compute_scan_parameters(self): self.extent_x = ( -self.scanning_parameters.voltage_x, self.scanning_parameters.voltage_x, ) self.extent_y = ( -self.scanning_parameters.voltage_y, self.scanning_parameters.voltage_y, ) self.n_x = self.scanning_parameters.n_x self.n_y = self.scanning_parameters.n_y self.raw_x, self.raw_y = compute_waveform(self.scanning_parameters) self.pos_x = (self.raw_x * ((self.extent_x[1] - self.extent_x[0]) / self.n_x) + self.extent_x[0]) self.pos_y = (self.raw_y * ((self.extent_y[1] - self.extent_y[0]) / self.n_y) + self.extent_y[0]) self.n_bin = self.scanning_parameters.n_bin self.n_samples_out = len(self.raw_x) self.n_samples_in = self.n_samples_out * self.n_bin self.sample_rate_out = self.scanning_parameters.sample_rate_out self.plane_duration = self.n_samples_out / self.sample_rate_out self.sample_rate_in = self.n_bin * self.sample_rate_out self.write_signals = np.stack([self.pos_x, self.pos_y], 0) self.read_buffer = np.zeros((2, self.n_samples_in)) self.mystery_offset = self.scanning_parameters.mystery_offset def setup_tasks(self, read_task, write_task, shutter_task): # Configure the channels read_task.ai_channels.add_ai_voltage_chan( "Dev1/ai0:1", min_val=-1, max_val=1 ) # channels are 0: green PMT, 1 x galvo pos 2 y galvo pos write_task.ao_channels.add_ao_voltage_chan("Dev1/ao0:1", min_val=-10, max_val=10) shutter_task.do_channels.add_do_chan( "Dev1/port0/line1", line_grouping=LineGrouping.CHAN_PER_LINE) # Set the timing of both to the onboard clock so that they are synchronised read_task.timing.cfg_samp_clk_timing( rate=self.sample_rate_in, source="OnboardClock", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.n_samples_in, ) write_task.timing.cfg_samp_clk_timing( rate=self.sample_rate_out, source="OnboardClock", active_edge=Edge.RISING, sample_mode=AcquisitionType.CONTINUOUS, samps_per_chan=self.n_samples_out, ) # This is necessary to synchronise reading and wrting read_task.triggers.start_trigger.cfg_dig_edge_start_trig( "/Dev1/ao/StartTrigger", Edge.RISING) def check_start_plane(self): if self.scanning_parameters.scanning_state == ScanningState.EXPERIMENT_RUNNING: while not self.experiment_start_event.is_set(): sleep(0.00001) def calculate_duration(self): try: duration = self.duration_queue.get(timeout=0.0001) self.scanning_parameters.n_frames = (int( ceil(duration / frame_duration(self.scanning_parameters))) + 1) self.n_frames_queue.put(self.scanning_parameters.n_frames) except Empty: pass def scan_loop(self, read_task, write_task): writer = AnalogMultiChannelWriter(write_task.out_stream) reader = AnalogMultiChannelReader(read_task.in_stream) first_write = True i_acquired = 0 while not self.stop_event.is_set() and ( not self.scanning_parameters.scanning_state == ScanningState.EXPERIMENT_RUNNING or i_acquired < self.scanning_parameters.n_frames): # The first write has to be defined before the task starts try: writer.write_many_sample(self.write_signals) if i_acquired == 0: self.check_start_plane() if first_write: read_task.start() write_task.start() first_write = False reader.read_many_sample( self.read_buffer, number_of_samples_per_channel=self.n_samples_in, timeout=1, ) i_acquired += 1 except nidaqmx.DaqError as e: print(e) break self.data_queue.put(self.read_buffer[0, :]) # if new parameters have been received and changed, update # them, breaking out of the loop if the experiment is not running try: self.new_parameters = self.parameter_queue.get(timeout=0.0001) if self.new_parameters != self.scanning_parameters and ( self.scanning_parameters.scanning_state != ScanningState.EXPERIMENT_RUNNING or self.new_parameters.scanning_state == ScanningState.PREVIEW): break except Empty: pass # calculate duration self.calculate_duration() def pause_loop(self): while not self.stop_event.is_set(): try: self.new_parameters = self.parameter_queue.get(timeout=0.001) if self.new_parameters != self.scanning_parameters and ( self.scanning_parameters.scanning_state != ScanningState.EXPERIMENT_RUNNING or self.new_parameters.scanning_state == ScanningState.PREVIEW): break except Empty: pass def toggle_shutter(self, shutter_task): shutter_task.write(False, auto_start=True) shutter_task.write(True, auto_start=True) shutter_task.write(False, auto_start=True) sleep(0.05) def run_scanning(self): while not self.stop_event.is_set(): toggle_shutter = False if (self.new_parameters.scanning_state == ScanningState.PAUSED and self.scanning_parameters.scanning_state != ScanningState.PAUSED ) or ( self.new_parameters.scanning_state != ScanningState.PAUSED and self.scanning_parameters.scanning_state == ScanningState.PAUSED): toggle_shutter = True self.scanning_parameters = self.new_parameters self.compute_scan_parameters() with Task() as write_task, Task() as read_task, Task( ) as shutter_task: self.setup_tasks(read_task, write_task, shutter_task) if self.scanning_parameters.reset_shutter or toggle_shutter: self.toggle_shutter(shutter_task) if self.scanning_parameters.scanning_state == ScanningState.PAUSED: self.pause_loop() else: self.scan_loop(read_task, write_task)
class ExperimentState(QObject): sig_scanning_changed = pyqtSignal() def __init__(self, diagnostics=False): super().__init__() if diagnostics: self.sequence_queue = Queue() self.sequence_diagram = SequenceDiagram(self.sequence_queue, "main") self.experiment_start_event = Event() self.scanning_settings = ScanningSettings() self.experiment_settings = ExperimentSettings() self.pause_after = False self.parameter_tree = ParameterTree() self.parameter_tree.add(self.scanning_settings) self.parameter_tree.add(self.experiment_settings) self.end_event = Event() self.external_sync = ZMQcomm() self.duration_queue = Queue() self.scanner = Scanner(self.experiment_start_event, duration_queue=self.duration_queue) self.scanning_parameters = None self.reconstructor = ImageReconstructor(self.scanner.data_queue, self.scanner.stop_event) self.save_queue = ArrayQueue(max_mbytes=800) self.saver = StackSaver(self.scanner.stop_event, self.save_queue, self.scanner.n_frames_queue) self.save_status: Optional[SavingStatus] = None self.motors = dict() self.motors["x"] = MotorControl("COM6", axes="x") self.motors["y"] = MotorControl("COM6", axes="y") self.motors["z"] = MotorControl("COM6", axes="z") self.power_controller = LaserPowerControl() self.scanning_settings.sig_param_changed.connect(self.send_scan_params) self.scanning_settings.sig_param_changed.connect(self.send_save_params) self.scanner.start() self.reconstructor.start() self.saver.start() self.open_setup() self.paused = False @property def saving(self): return self.saver.saving_signal.is_set() def open_setup(self): self.send_scan_params() def start_experiment(self, first_plane=True): duration = self.external_sync.send(self.parameter_tree.serialize()) if duration is None: self.restart_scanning() return False self.duration_queue.put(duration) params_to_send = convert_params(self.scanning_settings) params_to_send.scanning_state = ScanningState.EXPERIMENT_RUNNING self.scanner.parameter_queue.put(params_to_send) if first_plane: self.send_save_params() self.saver.saving_signal.set() self.experiment_start_event.set() return True def end_experiment(self, force=False): self.experiment_start_event.clear() if not force and self.save_status.i_z + 1 < self.save_status.target_params.n_z: self.advance_plane() else: self.saver.saving_signal.clear() if self.pause_after: self.pause_scanning() else: self.restart_scanning() def restart_scanning(self): params_to_send = convert_params(self.scanning_settings) params_to_send.scanning_state = ScanningState.PREVIEW self.scanner.parameter_queue.put(params_to_send) self.paused = False def pause_scanning(self): params_to_send = convert_params(self.scanning_settings) params_to_send.scanning_state = ScanningState.PAUSED self.scanner.parameter_queue.put(params_to_send) self.paused = True def advance_plane(self): self.motors["z"].move_rel(self.experiment_settings.dz / 1000) sleep(0.2) self.start_experiment(first_plane=False) def close_setup(self): """ Cleanup on programe close: end all parallel processes, close all communication channels """ for motor in self.motors.values(): motor.end_session() self.power_controller.terminate_connection() self.scanner.stop_event.set() self.end_event.set() self.scanner.join() self.reconstructor.join() def get_image(self): try: image = -self.reconstructor.output_queue.get(timeout=0.001) if self.saver.saving_signal.is_set(): if (self.save_status is not None and self.save_status.i_t + 1 == self.save_status.target_params.n_t): self.end_experiment() self.save_queue.put(image) return image except Empty: return None def send_scan_params(self): self.scanning_parameters = convert_params(self.scanning_settings) self.power_controller.move_abs(self.scanning_settings.laser_power) self.scanner.parameter_queue.put(self.scanning_parameters) self.reconstructor.parameter_queue.put(self.scanning_parameters) self.sig_scanning_changed.emit() def send_save_params(self): self.saver.saving_parameter_queue.put( SavingParameters( output_dir=Path(self.experiment_settings.save_dir), plane_size=(self.scanning_parameters.n_x, self.scanning_parameters.n_y), n_z=self.experiment_settings.n_planes, notification_email=self.experiment_settings.notification_email, notification_frequency=self.experiment_settings. notify_every_n_planes)) def get_save_status(self) -> Optional[SavingStatus]: try: self.save_status = self.saver.saved_status_queue.get(timeout=0.001) return self.save_status except Empty: pass return None
class MovingFrameDispatcher(FrameDispatcher): """ """ def __init__(self, *args, signal_start_rec, output_queue_mb=500, **kwargs): super().__init__(*args, **kwargs) self.save_queue = ArrayQueue(max_mbytes=output_queue_mb) self.framestart_queue = Queue() self.diagnostic_queue = Queue() self.processing_parameters = MovementDetectionParameters( ).get_clean_values() self.signal_start_rec = signal_start_rec self.mem_use = 0 self.diagnostic_params = [ "n_pixels_difference", "recording_state", "n_images_in_buffer", ] def run(self): """ """ t, frame_0 = self.frame_queue.get(timeout=10) n_previous_compare = 3 image_crop = slice( self.processing_parameters["frame_margin"], -self.processing_parameters["frame_margin"], ) previous_ims = np.zeros( (n_previous_compare, ) + frame_0[image_crop].shape, dtype=np.uint8) image_buffer = deque() record_counter = 0 i_frame = 0 recording_state = False i_recorded = 0 while not self.finished_signal.is_set(): # Gets the processing parameters from their queue if self.processing_parameter_queue is not None: try: self.processing_parameters = self.processing_parameter_queue.get( timeout=0.00001) except Empty: pass try: current_time, current_frame = self.frame_queue.get( timeout=0.001) # process frames as they come, threshold them to roughly # find the fish (e.g. eyes) _, current_frame_thresh = cv2.threshold( cv2.boxFilter(current_frame[image_crop], -1, (3, 3)), self.processing_parameters["fish_threshold"], 255, cv2.THRESH_BINARY, ) # compare the thresholded frame to the previous ones, # if there are enough differences # because the fish moves, start recording to file if i_frame >= n_previous_compare: difsum = _compare_to_previous(current_frame_thresh, previous_ims) # put the difference in the diagnostic queue so that # the threshold can be set correctly if np.all(difsum > self. processing_parameters["motion_threshold_n_pix"]): record_counter = self.processing_parameters[ "n_next_save"] if record_counter > 0: if self.signal_start_rec.is_set( ) and self.mem_use < 0.9: if not recording_state: while image_buffer: time, im = image_buffer.popleft() self.framestart_queue.put(time) self.save_queue.put(im) i_recorded += 1 self.save_queue.put(current_frame) self.framestart_queue.put(current_time) i_recorded += 1 recording_state = True record_counter -= 1 else: recording_state = False image_buffer.append((current_time, current_frame)) if (len(image_buffer) > self.processing_parameters["n_previous_save"]): image_buffer.popleft() self.diagnostic_queue.put(( current_time, ( difsum[i_frame % n_previous_compare], recording_state, len(image_buffer), ), )) i_frame += 1 previous_ims[i_frame % n_previous_compare, :, :] = current_frame_thresh # calculate the framerate and send frame to gui self.update_framerate() if self.processing_parameters["show_thresholded"]: self.send_to_gui(current_frame_thresh) else: self.send_to_gui(current_frame) except Empty: pass