Example #1
0
    def _setup_camera(self):
        """
        Setup camera readout and triggering for OPM

        :return None:
        """

        with RemoteMMCore() as mmc_camera_setup:

            # give camera time to change modes if necessary
            mmc_camera_setup.setConfig('Camera-Setup','ScanMode3')
            mmc_camera_setup.waitForConfig('Camera-Setup','ScanMode3')

            # set camera to internal trigger
            mmc_camera_setup.setConfig('Camera-TriggerType','NORMAL')
            mmc_camera_setup.waitForConfig('Camera-TriggerType','NORMAL')
            trigger_value = mmc_camera_setup.getProperty(self.camera_name,'Trigger')
            while not(trigger_value == 'NORMAL'):
                mmc_camera_setup.setConfig('Camera-TriggerType','NORMAL')
                mmc_camera_setup.waitForConfig('Camera-TriggerType','NORMAL')
                time.sleep(2)
                trigger_value = mmc_camera_setup.getProperty(self.camera_name,'Trigger')
                
            # give camera time to change modes if necessary
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER KIND[0]','EXPOSURE')
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER KIND[1]','EXPOSURE')
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER KIND[2]','EXPOSURE')
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER POLARITY[0]','POSITIVE')
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER POLARITY[1]','POSITIVE')
            mmc_camera_setup.setProperty(self.camera_name,r'OUTPUT TRIGGER POLARITY[2]','POSITIVE')
Example #2
0
    def _lasers_to_hardware(self):
        """
        Change lasers to hardware control

        :return None:
        """

        with RemoteMMCore() as mmc_lasers_hardware:
            # turn all lasers off
            mmc_lasers_hardware.setConfig('Laser','Off')
            mmc_lasers_hardware.waitForConfig('Laser','Off')

            # set all laser to external triggering
            mmc_lasers_hardware.setConfig('Modulation-405','External-Digital')
            mmc_lasers_hardware.waitForConfig('Modulation-405','External-Digital')
            mmc_lasers_hardware.setConfig('Modulation-488','External-Digital')
            mmc_lasers_hardware.waitForConfig('Modulation-488','External-Digital')
            mmc_lasers_hardware.setConfig('Modulation-561','External-Digital')
            mmc_lasers_hardware.waitForConfig('Modulation-561','External-Digital')
            mmc_lasers_hardware.setConfig('Modulation-637','External-Digital')
            mmc_lasers_hardware.waitForConfig('Modulation-637','External-Digital')
            mmc_lasers_hardware.setConfig('Modulation-730','External-Digital')
            mmc_lasers_hardware.waitForConfig('Modulation-730','External-Digital')

            # turn all lasers on
            mmc_lasers_hardware.setConfig('Laser','AllOn')
            mmc_lasers_hardware.waitForConfig('Laser','AllOn')
Example #3
0
def main(path_to_mm_config=Path(
    'C:/Program Files/Micro-Manager-2.0gamma/temp_HamDCAM.cfg')):

    # launch pymmcore server
    with RemoteMMCore() as mmc:
        mmc.loadSystemConfiguration(str(path_to_mm_config))

        # create Napari viewer
        viewer = napari.Viewer(
            title='ASU OPM control -- iterative multiplexing')

        # setup OPM widgets
        iterative_control_widget = OPMIterative()
        stage_display_widget = OPMStageMonitor()
        instrument_control_widget = OPMStageScan(iterative_control_widget)
        instrument_control_widget._set_viewer(viewer)

        # startup instrument
        instrument_control_widget._startup()

        # create thread workers
        # 2D
        worker_2d = instrument_control_widget._acquire_2d_data()
        worker_2d.yielded.connect(instrument_control_widget._update_layers)
        instrument_control_widget._set_worker_2d(worker_2d)

        # 3D
        worker_3d = instrument_control_widget._acquire_3d_data()
        worker_3d.yielded.connect(instrument_control_widget._update_layers)
        instrument_control_widget._set_worker_3d(worker_3d)

        # iterative 3D
        instrument_control_widget._create_worker_iterative()

        # instrument setup
        worker_iterative_setup = iterative_control_widget._return_experiment_setup(
        )
        worker_iterative_setup.returned.connect(
            instrument_control_widget._set_iterative_configuration)
        iterative_control_widget._set_worker_iterative_setup(
            worker_iterative_setup)

        # add widgets to Napari viewer
        viewer.window.add_dock_widget(iterative_control_widget,
                                      area='bottom',
                                      name='Iterative setup')
        viewer.window.add_dock_widget(stage_display_widget,
                                      area='bottom',
                                      name='Stage monitor')
        viewer.window.add_dock_widget(instrument_control_widget,
                                      area='right',
                                      name='Instrument setup')

        # start Napari
        napari.run(max_loop_level=2)

        worker_2d.quit()
        worker_3d.quit()

        instrument_control_widget._shutdown()
Example #4
0
    def get_stage_pos(self, get_pos_xyz):

        with RemoteMMCore() as mmc_stage_monitor:
            x = mmc_stage_monitor.getXPosition()
            y = mmc_stage_monitor.getYPosition()
            z = mmc_stage_monitor.getZPosition()

            self.x_stage_pos.value = (f"{x:.1f}")
            self.y_stage_pos.value = (f"{y:.1f}")
            self.z_stage_pos.value = (f"{z:.1f}")
Example #5
0
    def _set_mmc_laser_power(self):
        """
        Change laser power

        :return None:
        """
        with RemoteMMCore() as mmc_laser_power:
            mmc_laser_power.setProperty(r'Coherent-Scientific Remote',r'Laser 405-100C - PowerSetpoint (%)',float(self.channel_powers[0]))
            mmc_laser_power.setProperty(r'Coherent-Scientific Remote',r'Laser 488-150C - PowerSetpoint (%)',float(self.channel_powers[1]))
            mmc_laser_power.setProperty(r'Coherent-Scientific Remote',r'Laser OBIS LS 561-150 - PowerSetpoint (%)',float(self.channel_powers[2]))
            mmc_laser_power.setProperty(r'Coherent-Scientific Remote',r'Laser 637-140C - PowerSetpoint (%)',float(self.channel_powers[3]))
            mmc_laser_power.setProperty(r'Coherent-Scientific Remote',r'Laser 730-30C - PowerSetpoint (%)',float(self.channel_powers[4]))
Example #6
0
    def _acquire_2d_data(self):
        while True:

            # parse which channels are active
            active_channel_indices = [ind for ind, st in zip(self.do_ind, self.channel_states) if st]
            n_active_channels = len(active_channel_indices)
            if n_active_channels == 0:
                yield None
                
            if self.debug:
                print("%d active channels: " % n_active_channels, end="")
                for ind in active_channel_indices:
                    print("%s " % self.channel_labels[ind], end="")
                print("")

            if self.powers_changed:
                self._set_mmc_laser_power()
                self.powers_changed = False

            if self.channels_changed:
                if self.DAQ_running:
                    self.opmdaq.stop_waveform_playback()
                    self.DAQ_running = False
                    self.opmdaq.reset_scan_mirror()
                self.opmdaq.set_scan_type('stage')
                self.opmdaq.set_channels_to_use(self.channel_states)
                self.opmdaq.set_interleave_mode(True)
                self.opmdaq.generate_waveforms()
                self.opmdaq.start_waveform_playback()
                self.DAQ_running=True
                self.channels_changed = False
            
            if not(self.DAQ_running):
                self.opmdaq.start_waveform_playback()
                self.DAQ_running=True

            with RemoteMMCore() as mmc_2d:

                if self.ROI_changed:

                    self._crop_camera()
                    self.ROI_changed = False

                # set exposure time
                if self.exposure_changed:
                    mmc_2d.setExposure(self.exposure_ms)
                    self.exposure_changed = False

                for c in active_channel_indices:
                    mmc_2d.snapImage()
                    raw_image_2d = mmc_2d.getImage()
                    time.sleep(.05)
                    yield c, raw_image_2d
Example #7
0
    def _crop_camera(self):
        """
        Crop camera to GUI values

        :return None:
        """

        with RemoteMMCore() as mmc_crop_camera:
            current_ROI = mmc_crop_camera.getROI()
            if not(current_ROI[2]==2304) or not(current_ROI[3]==2304):
                mmc_crop_camera.clearROI()
                mmc_crop_camera.waitForDevice(self.camera_name)
            mmc_crop_camera.setROI(int(self.ROI_uleft_corner_x),int(self.ROI_uleft_corner_y),int(self.ROI_width_x),int(self.ROI_width_y))
            mmc_crop_camera.waitForDevice(self.camera_name)
Example #8
0
def get_core_singleton(remote=False) -> CMMCorePlus:
    """Retrieve the MMCore singleton for this session.

    The first call to this function determines whether we're running remote or not.
    perhaps a temporary function for now...
    """
    global _SESSION_CORE
    if _SESSION_CORE is None:
        if remote:
            from pymmcore_plus import RemoteMMCore

            _SESSION_CORE = RemoteMMCore()  # type: ignore  # it has the same interface.
        else:
            _SESSION_CORE = CMMCorePlus.instance()
    return _SESSION_CORE
Example #9
0
def main(path_to_mm_config=Path(
    'C:/Program Files/Micro-Manager-2.0gamma/temp_HamDCAM.cfg')):

    # launch pymmcore server
    with RemoteMMCore() as mmc:
        mmc.loadSystemConfiguration(
            str(path_to_mm_config))  # setup OPM GUI and Napari viewer

        instrument_control_widget = OPMMirrorScan()
        # these methods have to be private to not show using magic-class. Maybe a better solution is available?
        instrument_control_widget._startup()

        viewer = napari.Viewer(
            title='ASU Snouty-OPM timelapse acquisition control')

        # these methods have to be private to not show using magic-class. Maybe a better solution is available?
        instrument_control_widget._set_viewer(viewer)

        # setup 2D imaging thread worker
        # these methods have to be private to not show using magic-class. Maybe a better solution is available?
        worker_2d = instrument_control_widget._acquire_2d_data()
        worker_2d.yielded.connect(instrument_control_widget._update_layers)
        instrument_control_widget._set_worker_2d(worker_2d)

        # setup 3D imaging thread worker
        # these methods have to be private to not show using magic-class. Maybe a better solution is available?
        worker_3d = instrument_control_widget._acquire_3d_data()
        worker_3d.yielded.connect(instrument_control_widget._update_layers)
        instrument_control_widget._set_worker_3d(worker_3d)

        instrument_control_widget._create_3d_t_worker()

        viewer.window.add_dock_widget(instrument_control_widget,
                                      name='Instrument control')

        # start Napari
        napari.run(max_loop_level=2)

        # shutdown acquistion threads
        worker_2d.quit()
        worker_3d.quit()

        # shutdown instrument
        # these methods have to be private to not show using magic-class. Maybe a better solution is available?
        instrument_control_widget._shutdown()
Example #10
0
    def _enforce_DCAM_internal_trigger(self):
        """
        Enforce camera being in trigger = INTERNAL mode

        :return None:
        """

        with RemoteMMCore() as mmc_camera_trigger:

            # set camera to START mode upon input trigger
            mmc_camera_trigger.setConfig('Camera-TriggerSource','INTERNAL')
            mmc_camera_trigger.waitForConfig('Camera-TriggerSource','INTERNAL')

            # check if camera actually changed
            # we find that camera doesn't always go back to START mode and need to check it
            trigger_value = mmc_camera_trigger.getProperty(self.camera_name,'TRIGGER SOURCE')
            while not(trigger_value == 'INTERNAL'):
                mmc_camera_trigger.setConfig('Camera-TriggerSource','INTERNAL')
                mmc_camera_trigger.waitForConfig('Camera-TriggerSource','INTERNAL')
                trigger_value = mmc_camera_trigger.getProperty(self.camera_name,'TRIGGER SOURCE')
Example #11
0
    def _lasers_to_software(self):
        """
        Change lasers to software control

        :return None:
        """

        with RemoteMMCore() as mmc_lasers_software:
            # turn all lasers off
            mmc_lasers_software.setConfig('Laser','Off')
            mmc_lasers_software.waitForConfig('Laser','Off')

            # set all lasers back to software control
            mmc_lasers_software.setConfig('Modulation-405','CW (constant power)')
            mmc_lasers_software.waitForConfig('Modulation-405','CW (constant power)')
            mmc_lasers_software.setConfig('Modulation-488','CW (constant power)')
            mmc_lasers_software.waitForConfig('Modulation-488','CW (constant power)')
            mmc_lasers_software.setConfig('Modulation-561','CW (constant power)')
            mmc_lasers_software.waitForConfig('Modulation-561','CW (constant power)')
            mmc_lasers_software.setConfig('Modulation-637','CW (constant power)')
            mmc_lasers_software.waitForConfig('Modulation-637','CW (constant power)')
            mmc_lasers_software.setConfig('Modulation-730','CW (constant power)')
            mmc_lasers_software.waitForConfig('Modulation-730','CW (constant power)')
Example #12
0
from pymmcore_plus import RemoteMMCore

with RemoteMMCore(verbose=True) as mmcore:
    # 'demo' is a special option for the included CMMCorePlus
    # that loads the micro-manager demo config
    mmcore.loadSystemConfiguration("demo")
    print("loaded:", mmcore.getLoadedDevices())
Example #13
0
    def _acquire_3d_t_data(self):

        with RemoteMMCore() as mmc_3d_time:

            #------------------------------------------------------------------------------------------------------------------------------------
            #----------------------------------------------Begin setup of scan parameters--------------------------------------------------------
            #------------------------------------------------------------------------------------------------------------------------------------
            # parse which channels are active
            active_channel_indices = [ind for ind, st in zip(self.do_ind, self.channel_states) if st]
            self.n_active_channels = len(active_channel_indices)
                
            if self.debug:
                print("%d active channels: " % self.n_active_channels, end="")
                for ind in active_channel_indices:
                    print("%s " % self.channel_labels[ind], end="")
                print("")

            if self.ROI_changed:
                self._crop_camera()
                self.ROI_changed = False

            # set exposure time
            if self.exposure_changed:
                mmc_3d_time.setExposure(self.exposure_ms)
                self.exposure_changed = False

            if self.powers_changed:
                self._set_mmc_laser_power()
                self.powers_changed = False
            
            if self.channels_changed or self.footprint_changed or not(self.DAQ_running):
                if self.DAQ_running:
                    self.opmdaq.stop_waveform_playback()
                    self.DAQ_running = False
                self.opmdaq.set_scan_type('mirror')
                self.opmdaq.set_channels_to_use(self.channel_states)
                self.opmdaq.set_interleave_mode(True)
                self.scan_steps = self.opmdaq.set_scan_mirror_range(self.scan_axis_step_um,self.scan_mirror_footprint_um)
                self.opmdaq.generate_waveforms()
                self.channels_changed = False
                self.footprint_changed = False

            # create directory for timelapse
            time_string = datetime.now().strftime("%Y_%m_%d-%I_%M_%S")
            self.output_dir_path = self.save_path / Path('timelapse_'+time_string)
            self.output_dir_path.mkdir(parents=True, exist_ok=True)


            # create name for zarr directory
            zarr_output_path = self.output_dir_path / Path('OPM_data.zarr')

            # create and open zarr file
            opm_data = zarr.open(str(zarr_output_path), mode="w", shape=(self.n_timepoints, self.n_active_channels, self.scan_steps, self.ROI_width_y, self.ROI_width_x), chunks=(1, 1, 1, self.ROI_width_y, self.ROI_width_x),compressor=None, dtype=np.uint16)

            #------------------------------------------------------------------------------------------------------------------------------------
            #----------------------------------------------End setup of scan parameters----------------------------------------------------------
            #------------------------------------------------------------------------------------------------------------------------------------


            #------------------------------------------------------------------------------------------------------------------------------------
            #----------------------------------------------------Start acquisition---------------------------------------------------------------
            #------------------------------------------------------------------------------------------------------------------------------------

            # set circular buffer to be large
            mmc_3d_time.clearCircularBuffer()
            circ_buffer_mb = 96000
            mmc_3d_time.setCircularBufferMemoryFootprint(int(circ_buffer_mb))

            # run hardware triggered acquisition
            if self.wait_time == 0:
                self.opmdaq.start_waveform_playback()
                self.DAQ_running = True
                mmc_3d_time.startSequenceAcquisition(int(self.n_timepoints*self.n_active_channels*self.scan_steps),0,True)
                for t in trange(self.n_timepoints,desc="t", position=0):
                    for z in trange(self.scan_steps,desc="z", position=1, leave=False):
                        for c in range(self.n_active_channels):
                            while mmc_3d_time.getRemainingImageCount()==0:
                                pass
                            opm_data[t, c, z, :, :]  = mmc_3d_time.popNextImage()
                mmc_3d_time.stopSequenceAcquisition()
                self.opmdaq.stop_waveform_playback()
                self.DAQ_running = False
            else:
                for t in trange(self.n_timepoints,desc="t", position=0):
                    self.opmdaq.start_waveform_playback()
                    self.DAQ_running = True
                    mmc_3d_time.startSequenceAcquisition(int(self.n_active_channels*self.scan_steps),0,True)
                    for z in trange(self.scan_steps,desc="z", position=1, leave=False):
                        for c in range(self.n_active_channels):
                            while mmc_3d_time.getRemainingImageCount()==0:
                                pass
                            opm_data[t, c, z, :, :]  = mmc_3d_time.popNextImage()
                    mmc_3d_time.stopSequenceAcquisition()
                    self.opmdaq.stop_waveform_playback()
                    self.DAQ_running = False
                    time.sleep(self.wait_time)
                    
            # construct metadata and save
            self._save_metadata()

            #------------------------------------------------------------------------------------------------------------------------------------
            #--------------------------------------------------------End acquisition-------------------------------------------------------------
            #------------------------------------------------------------------------------------------------------------------------------------

            # set circular buffer to be small 
            mmc_3d_time.clearCircularBuffer()
            circ_buffer_mb = 4000
            mmc_3d_time.setCircularBufferMemoryFootprint(int(circ_buffer_mb))
Example #14
0
    def _calculate_scan_volume(self):

        try:
            with RemoteMMCore() as mmc_stage_setup:

                # set experiment exposure
                mmc_stage_setup.setExposure(self.exposure_ms)

                # snap image
                mmc_stage_setup.snapImage()

                # grab exposure
                true_exposure = mmc_stage_setup.getExposure()

                # grab ROI
                current_ROI = mmc_stage_setup.getROI()
                self.x_pixels = current_ROI[2]
                self.y_pixels = current_ROI[3]
                if not ((self.y_pixels == 256) or (self.y_pixels == 512)):
                    raise Exception('Set camera ROI first.')

                # get actual framerate from micromanager properties
                actual_readout_ms = true_exposure + float(
                    mmc_stage_setup.getProperty('OrcaFusionBT',
                                                'ReadoutTime'))  #unit: ms
                if self.debug:
                    print('Full readout time = ' + str(actual_readout_ms))

                # scan axis setup
                scan_axis_step_mm = self.scan_axis_step_um / 1000.  #unit: mm
                self.scan_axis_start_mm = self.scan_axis_start_um / 1000.  #unit: mm
                self.scan_axis_end_mm = self.scan_axis_end_um / 1000.  #unit: mm
                scan_axis_range_um = np.abs(
                    self.scan_axis_end_um -
                    self.scan_axis_start_um)  # unit: um
                self.scan_axis_range_mm = scan_axis_range_um / 1000  #unit: mm
                actual_exposure_s = actual_readout_ms / 1000.  #unit: s
                self.scan_axis_speed_readout = np.round(
                    scan_axis_step_mm / actual_exposure_s /
                    self.n_active_channels_readout, 5)  #unit: mm/s
                self.scan_axis_speed_nuclei = np.round(
                    scan_axis_step_mm / actual_exposure_s /
                    self.n_active_channels_nuclei, 5)  #unit: mm/s
                self.scan_axis_positions = np.rint(
                    self.scan_axis_range_mm / scan_axis_step_mm).astype(
                        int)  #unit: number of positions

                # tile axis setup
                tile_axis_overlap = 0.2  #unit: percentage
                tile_axis_range_um = np.abs(self.tile_axis_end_um -
                                            self.tile_axis_start_um)  #unit: um
                tile_axis_ROI = self.x_pixels * self.pixel_size_um  #unit: um
                self.tile_axis_step_um = np.round(
                    (tile_axis_ROI) * (1 - tile_axis_overlap), 2)  #unit: um
                self.n_xy_tiles = np.rint(
                    tile_axis_range_um / self.tile_axis_step_um).astype(
                        int) + 1  #unit: number of positions
                # if tile_axis_positions rounded to zero, make sure we acquire at least one position
                if self.n_xy_tiles == 0:
                    self.n_xy_tiles = 1

                # height axis setup
                # check if there are multiple heights
                height_axis_range_um = np.abs(
                    self.height_axis_end_um -
                    self.height_axis_start_um)  #unit: um
                # if multiple heights, check if heights are due to uneven tissue position or for z tiling
                height_axis_overlap = 0.2  #unit: percentage
                height_axis_ROI = self.y_pixels * self.pixel_size_um * np.sin(
                    30. * np.pi / 180.)  #unit: um
                self.height_axis_step_um = np.round(
                    (height_axis_ROI) * (1 - height_axis_overlap),
                    2)  #unit: um
                self.n_z_tiles = np.rint(
                    height_axis_range_um / self.height_axis_step_um).astype(
                        int) + 1  #unit: number of positions
                # if height_axis_positions rounded to zero, make sure we acquire at least one position
                if self.n_z_tiles == 0:
                    self.n_z_tiles = 1

                # create dictionary with scan settings
                self.scan_settings = [{
                    'exposure_ms':
                    float(self.exposure_ms),
                    'scan_axis_start_um':
                    float(self.scan_axis_start_um),
                    'scan_axis_end_um':
                    float(self.scan_axis_end_um),
                    'scan_axis_step_um':
                    float(self.scan_axis_step_um),
                    'tile_axis_start_um':
                    float(self.tile_axis_start_um),
                    'tile_axis_end_um':
                    float(self.tile_axis_end_um),
                    'tile_axis_step_um':
                    float(self.tile_axis_step_um),
                    'height_axis_start_um':
                    float(self.height_axis_start_um),
                    'height_axis_end_um':
                    float(self.height_axis_end_um),
                    'height_axis_step_um':
                    float(self.height_axis_step_um),
                    'n_iterative_rounds':
                    int(self.n_iterative_rounds),
                    'nuclei_round':
                    int(self.codebook['nuclei_round']),
                    'num_xy_tiles':
                    int(self.n_xy_tiles),
                    'num_z_tiles':
                    int(self.n_z_tiles),
                    'n_active_channels_readout':
                    int(self.n_active_channels_readout),
                    'n_active_channels_nuclei':
                    int(self.n_active_channels_nuclei),
                    'scan_axis_positions':
                    int(self.scan_axis_positions),
                    'scan_axis_speed_readout':
                    float(self.scan_axis_speed_readout),
                    'scan_axis_speed_nuclei':
                    float(self.scan_axis_speed_nuclei),
                    'y_pixels':
                    int(self.y_pixels),
                    'x_pixels':
                    int(self.x_pixels),
                    '405_active_readout':
                    bool(self.channel_states_readout[0]),
                    '488_active_readout':
                    bool(self.channel_states_readout[1]),
                    '561_active_readout':
                    bool(self.channel_states_readout[2]),
                    '635_active_readout':
                    bool(self.channel_states_readout[3]),
                    '730_active_readout':
                    bool(self.channel_states_readout[4]),
                    '405_power_readout':
                    float(self.channel_powers_readout[0]),
                    '488_power_readout':
                    float(self.channel_powers_readout[1]),
                    '561_power_readout':
                    float(self.channel_powers_readout[2]),
                    '635_power_readout':
                    float(self.channel_powers_readout[3]),
                    '730_power_readout':
                    float(self.channel_powers_readout[4]),
                    '405_active_nuclei':
                    bool(self.channel_states_nuclei[0]),
                    '488_active_nuclei':
                    bool(self.channel_states_nuclei[1]),
                    '561_active_nuclei':
                    bool(self.channel_states_nuclei[2]),
                    '635_active_nuclei':
                    bool(self.channel_states_nuclei[3]),
                    '730_active_nuclei':
                    bool(self.channel_states_nuclei[4]),
                    '405_power_nuclei':
                    float(self.channel_powers_nuclei[0]),
                    '488_power_nuclei':
                    float(self.channel_powers_nuclei[1]),
                    '561_power_nuclei':
                    float(self.channel_powers_nuclei[2]),
                    '635_power_nuclei':
                    float(self.channel_powers_nuclei[3]),
                    '730_power_nuclei':
                    float(self.channel_powers_nuclei[4])
                }]

                self.stage_volume_set = True
        except:
            raise Exception("Error in stage volume setup.")
    def __init__(self, viewer: napari.viewer.Viewer, remote=True):
        super().__init__()
        self.setup_ui()

        self.viewer = viewer
        self.streaming_timer = None

        # create connection to mmcore server or process-local variant
        self._mmc = RemoteMMCore() if remote else CMMCorePlus()

        # tab widgets
        self.mda = MultiDWidget(self._mmc)
        self.explorer = ExploreSample(self.viewer, self._mmc)
        self.tabWidget.addTab(self.mda, "Multi-D Acquisition")
        self.tabWidget.addTab(self.explorer, "Sample Explorer")

        # connect mmcore signals
        sig = self._mmc.events

        # note: don't use lambdas with closures on `self`, since the connection
        # to core may outlive the lifetime of this particular widget.
        sig.sequenceStarted.connect(self._on_mda_started)
        sig.sequenceFinished.connect(self._on_mda_finished)
        sig.systemConfigurationLoaded.connect(self._refresh_options)
        sig.XYStagePositionChanged.connect(self._on_xy_stage_position_changed)
        sig.stagePositionChanged.connect(self._on_stage_position_changed)
        sig.exposureChanged.connect(self._on_exp_change)
        sig.frameReady.connect(self._on_mda_frame)
        sig.channelGroupChanged.connect(self._refresh_channel_list)
        sig.configSet.connect(self._on_config_set)

        # connect buttons
        self.load_cfg_Button.clicked.connect(self.load_cfg)
        self.browse_cfg_Button.clicked.connect(self.browse_cfg)
        self.left_Button.clicked.connect(self.stage_x_left)
        self.right_Button.clicked.connect(self.stage_x_right)
        self.y_up_Button.clicked.connect(self.stage_y_up)
        self.y_down_Button.clicked.connect(self.stage_y_down)
        self.up_Button.clicked.connect(self.stage_z_up)
        self.down_Button.clicked.connect(self.stage_z_down)

        self.snap_Button.clicked.connect(self.snap)
        self.live_Button.clicked.connect(self.toggle_live)

        self.illumination_Button.clicked.connect(self.illumination)
        self.properties_Button.clicked.connect(self._show_prop_browser)

        # connect comboBox
        self.objective_comboBox.currentIndexChanged.connect(self.change_objective)
        self.bit_comboBox.currentIndexChanged.connect(self.bit_changed)
        self.bin_comboBox.currentIndexChanged.connect(self.bin_changed)
        self.snap_channel_comboBox.currentTextChanged.connect(self._channel_changed)

        # connect spinboxes
        self.exp_spinBox.valueChanged.connect(self._update_exp)
        self.exp_spinBox.setKeyboardTracking(False)

        # refresh options in case a config is already loaded by another remote
        self._refresh_options()

        self.viewer.layers.events.connect(self.update_max_min)
        self.viewer.layers.selection.events.active.connect(self.update_max_min)
        self.viewer.dims.events.current_step.connect(self.update_max_min)
class MainWindow(QtW.QWidget, _MainUI):
    def __init__(self, viewer: napari.viewer.Viewer, remote=True):
        super().__init__()
        self.setup_ui()

        self.viewer = viewer
        self.streaming_timer = None

        # create connection to mmcore server or process-local variant
        self._mmc = RemoteMMCore() if remote else CMMCorePlus()

        # tab widgets
        self.mda = MultiDWidget(self._mmc)
        self.explorer = ExploreSample(self.viewer, self._mmc)
        self.tabWidget.addTab(self.mda, "Multi-D Acquisition")
        self.tabWidget.addTab(self.explorer, "Sample Explorer")

        # connect mmcore signals
        sig = self._mmc.events

        # note: don't use lambdas with closures on `self`, since the connection
        # to core may outlive the lifetime of this particular widget.
        sig.sequenceStarted.connect(self._on_mda_started)
        sig.sequenceFinished.connect(self._on_mda_finished)
        sig.systemConfigurationLoaded.connect(self._refresh_options)
        sig.XYStagePositionChanged.connect(self._on_xy_stage_position_changed)
        sig.stagePositionChanged.connect(self._on_stage_position_changed)
        sig.exposureChanged.connect(self._on_exp_change)
        sig.frameReady.connect(self._on_mda_frame)
        sig.channelGroupChanged.connect(self._refresh_channel_list)
        sig.configSet.connect(self._on_config_set)

        # connect buttons
        self.load_cfg_Button.clicked.connect(self.load_cfg)
        self.browse_cfg_Button.clicked.connect(self.browse_cfg)
        self.left_Button.clicked.connect(self.stage_x_left)
        self.right_Button.clicked.connect(self.stage_x_right)
        self.y_up_Button.clicked.connect(self.stage_y_up)
        self.y_down_Button.clicked.connect(self.stage_y_down)
        self.up_Button.clicked.connect(self.stage_z_up)
        self.down_Button.clicked.connect(self.stage_z_down)

        self.snap_Button.clicked.connect(self.snap)
        self.live_Button.clicked.connect(self.toggle_live)

        self.illumination_Button.clicked.connect(self.illumination)
        self.properties_Button.clicked.connect(self._show_prop_browser)

        # connect comboBox
        self.objective_comboBox.currentIndexChanged.connect(self.change_objective)
        self.bit_comboBox.currentIndexChanged.connect(self.bit_changed)
        self.bin_comboBox.currentIndexChanged.connect(self.bin_changed)
        self.snap_channel_comboBox.currentTextChanged.connect(self._channel_changed)

        # connect spinboxes
        self.exp_spinBox.valueChanged.connect(self._update_exp)
        self.exp_spinBox.setKeyboardTracking(False)

        # refresh options in case a config is already loaded by another remote
        self._refresh_options()

        self.viewer.layers.events.connect(self.update_max_min)
        self.viewer.layers.selection.events.active.connect(self.update_max_min)
        self.viewer.dims.events.current_step.connect(self.update_max_min)

    def illumination(self):
        if not hasattr(self, "_illumination"):
            self._illumination = IlluminationDialog(self._mmc, self)
        self._illumination.show()

    def _show_prop_browser(self):
        pb = PropBrowser(self._mmc, self)
        pb.exec()

    def _on_config_set(self, groupName: str, configName: str):
        if groupName == self._mmc.getOrGuessChannelGroup():
            with blockSignals(self.snap_channel_comboBox):
                self.snap_channel_comboBox.setCurrentText(configName)

    def _set_enabled(self, enabled):
        self.objective_groupBox.setEnabled(enabled)
        self.camera_groupBox.setEnabled(enabled)
        self.XY_groupBox.setEnabled(enabled)
        self.Z_groupBox.setEnabled(enabled)
        self.snap_live_tab.setEnabled(enabled)
        self.snap_live_tab.setEnabled(enabled)

    def _update_exp(self, exposure: float):
        self._mmc.setExposure(exposure)
        if self.streaming_timer:
            self.streaming_timer.setInterval(int(exposure))
            self._mmc.stopSequenceAcquisition()
            self._mmc.startContinuousSequenceAcquisition(exposure)

    def _on_exp_change(self, camera: str, exposure: float):
        with blockSignals(self.exp_spinBox):
            self.exp_spinBox.setValue(exposure)
        if self.streaming_timer:
            self.streaming_timer.setInterval(int(exposure))

    def _on_mda_started(self, sequence: useq.MDASequence):
        """ "create temp folder and block gui when mda starts."""
        self._set_enabled(False)

    def _on_mda_frame(self, image: np.ndarray, event: useq.MDAEvent):
        meta = self.mda.SEQUENCE_META.get(event.sequence) or SequenceMeta()

        if meta.mode != "mda":
            return

        # pick layer name
        file_name = meta.file_name if meta.should_save else "Exp"
        channelstr = (
            f"[{event.channel.config}_idx{event.index['c']}]_"
            if meta.split_channels
            else ""
        )
        layer_name = f"{file_name}_{channelstr}{event.sequence.uid}"

        try:  # see if we already have a layer with this sequence
            layer = self.viewer.layers[layer_name]

            # get indices of new image
            im_idx = tuple(
                event.index[k]
                for k in event_indices(event)
                if not (meta.split_channels and k == "c")
            )

            # make sure array shape contains im_idx, or pad with zeros
            new_array = extend_array_for_index(layer.data, im_idx)
            # add the incoming index at the appropriate index
            new_array[im_idx] = image
            # set layer data
            layer.data = new_array
            for a, v in enumerate(im_idx):
                self.viewer.dims.set_point(a, v)

        except KeyError:  # add the new layer to the viewer
            seq = event.sequence
            _image = image[(np.newaxis,) * len(seq.shape)]
            layer = self.viewer.add_image(_image, name=layer_name, blending="additive")

            # dimensions labels
            labels = [i for i in seq.axis_order if i in event.index] + ["y", "x"]
            self.viewer.dims.axis_labels = labels

            # add metadata to layer
            layer.metadata["useq_sequence"] = seq
            layer.metadata["uid"] = seq.uid
            # storing event.index in addition to channel.config because it's
            # possible to have two of the same channel in one sequence.
            layer.metadata["ch_id"] = f'{event.channel.config}_idx{event.index["c"]}'

    def _on_mda_finished(self, sequence: useq.MDASequence):
        """Save layer and add increment to save name."""
        meta = self.mda.SEQUENCE_META.pop(sequence, SequenceMeta())
        save_sequence(sequence, self.viewer.layers, meta)
        # reactivate gui when mda finishes.
        self._set_enabled(True)

    def browse_cfg(self):
        self._mmc.unloadAllDevices()  # unload all devicies
        print(f"Loaded Devices: {self._mmc.getLoadedDevices()}")

        # clear spinbox/combobox without accidently setting properties
        boxes = [
            self.objective_comboBox,
            self.bin_comboBox,
            self.bit_comboBox,
            self.snap_channel_comboBox,
        ]
        with blockSignals(boxes):
            for box in boxes:
                box.clear()

        file_dir = QtW.QFileDialog.getOpenFileName(self, "", "⁩", "cfg(*.cfg)")
        self.cfg_LineEdit.setText(str(file_dir[0]))
        self.max_min_val_label.setText("None")
        self.load_cfg_Button.setEnabled(True)

    def load_cfg(self):
        self.load_cfg_Button.setEnabled(False)
        print("loading", self.cfg_LineEdit.text())
        self._mmc.loadSystemConfiguration(self.cfg_LineEdit.text())

    def _refresh_camera_options(self):
        cam_device = self._mmc.getCameraDevice()
        if not cam_device:
            return
        cam_props = self._mmc.getDevicePropertyNames(cam_device)
        if "Binning" in cam_props:
            bin_opts = self._mmc.getAllowedPropertyValues(cam_device, "Binning")
            with blockSignals(self.bin_comboBox):
                self.bin_comboBox.clear()
                self.bin_comboBox.addItems(bin_opts)
                self.bin_comboBox.setCurrentText(
                    self._mmc.getProperty(cam_device, "Binning")
                )

        if "PixelType" in cam_props:
            px_t = self._mmc.getAllowedPropertyValues(cam_device, "PixelType")
            with blockSignals(self.bit_comboBox):
                self.bit_comboBox.clear()
                self.bit_comboBox.addItems(px_t)
                self.bit_comboBox.setCurrentText(
                    self._mmc.getProperty(cam_device, "PixelType")
                )

    def _refresh_objective_options(self):
        if "Objective" in self._mmc.getLoadedDevices():
            with blockSignals(self.objective_comboBox):
                self.objective_comboBox.clear()
                self.objective_comboBox.addItems(self._mmc.getStateLabels("Objective"))
                self.objective_comboBox.setCurrentText(
                    self._mmc.getStateLabel("Objective")
                )

    def _refresh_channel_list(self, channel_group: str = None):
        if channel_group is None:
            channel_group = self._mmc.getOrGuessChannelGroup()
        if channel_group:
            channel_list = list(self._mmc.getAvailableConfigs(channel_group))
            with blockSignals(self.snap_channel_comboBox):
                self.snap_channel_comboBox.clear()
                self.snap_channel_comboBox.addItems(channel_list)
                self.snap_channel_comboBox.setCurrentText(
                    self._mmc.getCurrentConfig(channel_group)
                )

    def _refresh_positions(self):
        if self._mmc.getXYStageDevice():
            x, y = self._mmc.getXPosition(), self._mmc.getYPosition()
            self._on_xy_stage_position_changed(self._mmc.getXYStageDevice(), x, y)
        if self._mmc.getFocusDevice():
            self.z_lineEdit.setText(f"{self._mmc.getZPosition():.1f}")

    def _refresh_options(self):
        self._refresh_camera_options()
        self._refresh_objective_options()
        self._refresh_channel_list()
        self._refresh_positions()

    def bit_changed(self):
        if self.bit_comboBox.count() > 0:
            bits = self.bit_comboBox.currentText()
            self._mmc.setProperty(self._mmc.getCameraDevice(), "PixelType", bits)

    def bin_changed(self):
        if self.bin_comboBox.count() > 0:
            bins = self.bin_comboBox.currentText()
            cd = self._mmc.getCameraDevice()
            self._mmc.setProperty(cd, "Binning", bins)

    def _channel_changed(self, newChannel: str):
        channel_group = self._mmc.getOrGuessChannelGroup()
        if channel_group:
            self._mmc.setConfig(channel_group, newChannel)

    def _on_xy_stage_position_changed(self, name, x, y):
        self.x_lineEdit.setText(f"{x:.1f}")
        self.y_lineEdit.setText(f"{y:.1f}")

    def _on_stage_position_changed(self, name, value):
        if "z" in name.lower():  # hack
            self.z_lineEdit.setText(f"{value:.1f}")

    def stage_x_left(self):
        self._mmc.setRelativeXYPosition(-float(self.xy_step_size_SpinBox.value()), 0.0)
        if self.snap_on_click_xy_checkBox.isChecked():
            self.snap()

    def stage_x_right(self):
        self._mmc.setRelativeXYPosition(float(self.xy_step_size_SpinBox.value()), 0.0)
        if self.snap_on_click_xy_checkBox.isChecked():
            self.snap()

    def stage_y_up(self):
        self._mmc.setRelativeXYPosition(
            0.0,
            float(self.xy_step_size_SpinBox.value()),
        )
        if self.snap_on_click_xy_checkBox.isChecked():
            self.snap()

    def stage_y_down(self):
        self._mmc.setRelativeXYPosition(
            0.0,
            -float(self.xy_step_size_SpinBox.value()),
        )
        if self.snap_on_click_xy_checkBox.isChecked():
            self.snap()

    def stage_z_up(self):
        self._mmc.setRelativeXYZPosition(
            0.0, 0.0, float(self.z_step_size_doubleSpinBox.value())
        )
        if self.snap_on_click_z_checkBox.isChecked():
            self.snap()

    def stage_z_down(self):
        self._mmc.setRelativeXYZPosition(
            0.0, 0.0, -float(self.z_step_size_doubleSpinBox.value())
        )
        if self.snap_on_click_z_checkBox.isChecked():
            self.snap()

    def change_objective(self):
        if self.objective_comboBox.count() <= 0:
            return

        zdev = self._mmc.getFocusDevice()

        currentZ = self._mmc.getZPosition()
        self._mmc.setPosition(zdev, 0)
        self._mmc.waitForDevice(zdev)
        self._mmc.setProperty(
            "Objective", "Label", self.objective_comboBox.currentText()
        )
        self._mmc.waitForDevice("Objective")
        self._mmc.setPosition(zdev, currentZ)
        self._mmc.waitForDevice(zdev)

        # define and set pixel size Config
        self._mmc.deletePixelSizeConfig(self._mmc.getCurrentPixelSizeConfig())
        curr_obj_name = self._mmc.getProperty("Objective", "Label")
        self._mmc.definePixelSizeConfig(curr_obj_name)
        self._mmc.setPixelSizeConfig(curr_obj_name)

        # get magnification info from the objective name
        # and set image pixel sixe (x,y) for the current pixel size Config
        match = re.search(r"(\d{1,3})[xX]", curr_obj_name)
        if match:
            mag = int(match.groups()[0])
            self.image_pixel_size = self.px_size_doubleSpinBox.value() / mag
            self._mmc.setPixelSizeUm(
                self._mmc.getCurrentPixelSizeConfig(), self.image_pixel_size
            )

    def update_viewer(self, data=None):
        # TODO: - fix the fact that when you change the objective
        #         the image translation is wrong
        #       - are max and min_val_lineEdit updating in live mode?
        if data is None:
            try:
                data = self._mmc.getLastImage()
            except (RuntimeError, IndexError):
                # circular buffer empty
                return
        try:
            preview_layer = self.viewer.layers["preview"]
            preview_layer.data = data
        except KeyError:
            preview_layer = self.viewer.add_image(data, name="preview")

        self.update_max_min()

        if self.streaming_timer is None:
            self.viewer.reset_view()

    def update_max_min(self, event=None):

        if self.tabWidget.currentIndex() != 0:
            return

        min_max_txt = ""

        for layer in self.viewer.layers.selection:

            if isinstance(layer, napari.layers.Image) and layer.visible:

                col = layer.colormap.name

                if col not in QColor.colorNames():
                    col = "gray"

                # min and max of current slice
                min_max_show = tuple(layer._calc_data_range(mode="slice"))
                min_max_txt += f'<font color="{col}">{min_max_show}</font>'

        self.max_min_val_label.setText(min_max_txt)

    def snap(self):
        self.stop_live()
        self._mmc.snapImage()
        self.update_viewer(self._mmc.getImage())

    def start_live(self):
        self._mmc.startContinuousSequenceAcquisition(self.exp_spinBox.value())
        self.streaming_timer = QTimer()
        self.streaming_timer.timeout.connect(self.update_viewer)
        self.streaming_timer.start(int(self.exp_spinBox.value()))
        self.live_Button.setText("Stop")

    def stop_live(self):
        self._mmc.stopSequenceAcquisition()
        if self.streaming_timer is not None:
            self.streaming_timer.stop()
            self.streaming_timer = None
        self.live_Button.setText("Live")
        self.live_Button.setIcon(CAM_ICON)

    def toggle_live(self, event=None):
        if self.streaming_timer is None:

            ch_group = self._mmc.getOrGuessChannelGroup()
            self._mmc.setConfig(ch_group, self.snap_channel_comboBox.currentText())

            self.start_live()
            self.live_Button.setIcon(CAM_STOP_ICON)
        else:
            self.stop_live()
            self.live_Button.setIcon(CAM_ICON)
Example #17
0
    def _acquire_3d_data(self):

        while True:
            with RemoteMMCore() as mmc_3d:

                #------------------------------------------------------------------------------------------------------------------------------------
                #----------------------------------------------Begin setup of scan parameters--------------------------------------------------------
                #------------------------------------------------------------------------------------------------------------------------------------
                # parse which channels are active
                active_channel_indices = [ind for ind, st in zip(self.do_ind, self.channel_states) if st]
                n_active_channels = len(active_channel_indices)
                    
                if self.debug:
                    print("%d active channels: " % n_active_channels, end="")
                    for ind in active_channel_indices:
                        print("%s " % self.channel_labels[ind], end="")
                    print("")

                n_timepoints = 1

                if self.ROI_changed:
                    self._crop_camera()
                    self.ROI_changed = False

                # set exposure time
                if self.exposure_changed:
                    mmc_3d.setExposure(self.exposure_ms)
                    self.exposure_changed = False

                if self.powers_changed:
                    self._set_mmc_laser_power()
                    self.powers_changed = False
                
                if self.channels_changed or self.footprint_changed or not(self.DAQ_running):
                    if self.DAQ_running:
                        self.opmdaq.stop_waveform_playback()
                        self.DAQ_running = False
                    self.opmdaq.set_scan_type('mirror')
                    self.opmdaq.set_channels_to_use(self.channel_states)
                    self.opmdaq.set_interleave_mode(True)
                    scan_steps = self.opmdaq.set_scan_mirror_range(self.scan_axis_step_um,self.scan_mirror_footprint_um)
                    self.opmdaq.generate_waveforms()
                    self.channels_changed = False
                    self.footprint_changed = False

                raw_image_stack = np.zeros([self.do_ind[-1],scan_steps,self.ROI_width_y,self.ROI_width_x]).astype(np.uint16)
                
                if self.debug:
                    # output experiment info
                    print("Scan axis range: %.1f um, Scan axis step: %.1f nm, Number of galvo positions: %d" % 
                        (self.scan_mirror_footprint_um,  self.scan_axis_step_um * 1000, scan_steps))
                    print('Time points:  ' + str(n_timepoints))

                #------------------------------------------------------------------------------------------------------------------------------------
                #----------------------------------------------End setup of scan parameters----------------------------------------------------------
                #------------------------------------------------------------------------------------------------------------------------------------


                #------------------------------------------------------------------------------------------------------------------------------------
                #----------------------------------------------Start acquisition and deskew----------------------------------------------------------
                #------------------------------------------------------------------------------------------------------------------------------------

                self.opmdaq.start_waveform_playback()
                self.DAQ_running = True
                # run hardware triggered acquisition
                mmc_3d.startSequenceAcquisition(int(n_active_channels*scan_steps),0,True)
                for z in range(scan_steps):
                    for c in active_channel_indices:
                        while mmc_3d.getRemainingImageCount()==0:
                            pass
                        raw_image_stack[c,z,:] = mmc_3d.popNextImage()
                mmc_3d.stopSequenceAcquisition()
                self.opmdaq.stop_waveform_playback()
                self.DAQ_running = False

                # deskew parameters
                deskew_parameters = np.empty([3])
                deskew_parameters[0] = self.opm_tilt                 # (degrees)
                deskew_parameters[1] = self.scan_axis_step_um*100    # (nm)
                deskew_parameters[2] = self.camera_pixel_size_um*100 # (nm)

                for c in active_channel_indices:
                    deskewed_image = deskew(np.flipud(raw_image_stack[c,:]),*deskew_parameters).astype(np.uint16)  
                    yield c, deskewed_image

                del raw_image_stack