Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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()
Esempio n. 4
0
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)
Esempio n. 5
0
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
Esempio n. 6
0
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