Example #1
0
def main():

    #------------------------------------------------------------------------------------------------------------------------------------
    #----------------------------------------------Begin setup of scan parameters--------------------------------------------------------
    #------------------------------------------------------------------------------------------------------------------------------------

    # set up lasers
    channel_labels = ["405", "488", "561", "635", "730"]
    channel_states = [False, False, True, False,
                      False]  # true -> active, false -> inactive
    channel_powers = [50, 10, 90, 100, 95]  # (0 -> 100%)
    do_ind = [0, 1, 2, 3,
              4]  # digital output line corresponding to each channel

    # parse which channels are active
    active_channel_indices = [
        ind for ind, st in zip(do_ind, channel_states) if st
    ]
    n_active_channels = len(active_channel_indices)

    print("%d active channels: " % n_active_channels, end="")
    for ind in active_channel_indices:
        print("%s " % channel_labels[ind], end="")
    print("")

    # exposure time
    exposure_ms = 50.0

    # excess scan positions
    excess_scan_positions = 10

    # galvo voltage at neutral
    galvo_neutral_volt = -0.15  # unit: volts

    # scan axis limits. Use stage positions reported by MM
    scan_axis_start_um = 8680.  #unit: um
    scan_axis_end_um = 8800.  #unit: um

    # tile axis limits. Use stage positions reported by MM
    tile_axis_start_um = -3841.28  #unit: um
    tile_axis_end_um = -3841.28  #unit: um

    # height axis limits. Use stage positions reported by MM
    height_axis_start_um = 13128.63  #unit: um
    height_axis_end_um = 13128.63  #unit:  um

    # number of timepoints to execute
    # TO DO: add in control for rate of experiment
    timepoints = 1

    # FOV parameters
    # ONLY MODIFY IF NECESSARY
    # ROI = [0, 1152, 2304, 512] #unit: pixels

    # setup file name
    save_directory = Path('D:/20210831')
    save_name = 'stage_scan'

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

    # connect to Micromanager instance
    bridge = Bridge()
    core = bridge.get_core()

    # turn off lasers
    core.set_config('Laser', 'Off')
    core.wait_for_config('Laser', 'Off')

    # set camera to fast readout mode
    core.set_config('Camera-Setup', 'ScanMode3')
    core.wait_for_config('Camera-Setup', 'ScanMode3')

    # set camera to START mode upon input trigger
    core.set_config('Camera-TriggerType', 'START')
    core.wait_for_config('Camera-TriggerType', 'START')

    # set camera to positive input trigger
    core.set_config('Camera-TriggerPolarity', 'POSITIVE')
    core.wait_for_config('Camera-TriggerPolarity', 'POSITIVE')

    # set camera to internal control
    core.set_config('Camera-TriggerSource', 'INTERNAL')
    core.wait_for_config('Camera-TriggerSource', 'INTERNAL')

    # set camera to output positive triggers on all lines for exposure
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER KIND[0]', 'EXPOSURE')
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER KIND[1]', 'EXPOSURE')
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER KIND[2]', 'EXPOSURE')
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER POLARITY[0]', 'POSITIVE')
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER POLARITY[1]', 'POSITIVE')
    core.set_property('OrcaFusionBT', 'OUTPUT TRIGGER POLARITY[2]', 'POSITIVE')

    # change core timeout for long stage moves
    core.set_property('Core', 'TimeoutMs', 100000)
    time.sleep(1)

    # set exposure
    core.set_exposure(exposure_ms)

    # determine image size
    core.snap_image()
    y_pixels = core.get_image_height()
    x_pixels = core.get_image_width()

    # grab exposure
    true_exposure = core.get_exposure()

    # get actual framerate from micromanager properties
    actual_readout_ms = true_exposure + float(
        core.get_property('OrcaFusionBT', 'ReadoutTime'))  #unit: ms

    # camera pixel size
    pixel_size_um = .115  # unit: um

    # scan axis setup
    scan_axis_step_um = 0.4  # unit: um
    scan_axis_step_mm = scan_axis_step_um / 1000.  #unit: mm
    scan_axis_start_mm = scan_axis_start_um / 1000.  #unit: mm
    scan_axis_end_mm = scan_axis_end_um / 1000.  #unit: mm
    scan_axis_range_um = np.abs(scan_axis_end_um -
                                scan_axis_start_um)  # unit: um
    scan_axis_range_mm = scan_axis_range_um / 1000  #unit: mm
    actual_exposure_s = actual_readout_ms / 1000.  #unit: s
    scan_axis_speed = np.round(scan_axis_step_mm / actual_exposure_s,
                               4)  #unit: mm/s
    scan_axis_positions = np.rint(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(tile_axis_end_um -
                                tile_axis_start_um)  #unit: um
    tile_axis_range_mm = tile_axis_range_um / 1000  #unit: mm
    tile_axis_ROI = x_pixels * pixel_size_um  #unit: um
    tile_axis_step_um = np.round((tile_axis_ROI) * (1 - tile_axis_overlap),
                                 2)  #unit: um
    tile_axis_step_mm = tile_axis_step_um / 1000  #unit: mm
    tile_axis_positions = np.rint(
        tile_axis_range_mm /
        tile_axis_step_mm).astype(int) + 1  #unit: number of positions
    # if tile_axis_positions rounded to zero, make sure we acquire at least one position
    if tile_axis_positions == 0:
        tile_axis_positions = 1

    # height axis setup
    height_axis_overlap = 0.2  #unit: percentage
    height_axis_range_um = np.abs(height_axis_end_um -
                                  height_axis_start_um)  #unit: um
    height_axis_range_mm = height_axis_range_um / 1000  #unit: mm
    height_axis_ROI = y_pixels * pixel_size_um * np.sin(
        30. * np.pi / 180.)  #unit: um
    height_axis_step_um = np.round(
        (height_axis_ROI) * (1 - height_axis_overlap), 2)  #unit: um
    height_axis_step_mm = height_axis_step_um / 1000  #unit: mm
    height_axis_positions = np.rint(
        height_axis_range_mm /
        height_axis_step_mm).astype(int) + 1  #unit: number of positions
    # if height_axis_positions rounded to zero, make sure we acquire at least one position
    if height_axis_positions == 0:
        height_axis_positions = 1

    # get handle to xy and z stages
    xy_stage = core.get_xy_stage_device()
    z_stage = core.get_focus_device()

    # galvo voltage at neutral
    galvo_neutral_volt = -.15  # unit: volts

    # set the galvo to the neutral position if it is not already
    try:
        taskAO_first = daq.Task()
        taskAO_first.CreateAOVoltageChan("/Dev1/ao0", "", -4.0, 4.0,
                                         daq.DAQmx_Val_Volts, None)
        taskAO_first.WriteAnalogScalarF64(True, -1, galvo_neutral_volt, None)
        taskAO_first.StopTask()
        taskAO_first.ClearTask()
    except:
        print("DAQmx Error %s" % err)

    # Setup Tiger controller to pass signal when the scan stage cross the start position to the PLC
    plcName = 'PLogic:E:36'
    propPosition = 'PointerPosition'
    propCellConfig = 'EditCellConfig'
    #addrOutputBNC3 = 35 # BNC3 on the PLC front panel
    addrOutputBNC1 = 33  # BNC1 on the PLC front panel
    addrStageSync = 46  # TTL5 on Tiger backplane = stage sync signal

    # connect stage sync signal to BNC output
    core.set_property(plcName, propPosition, addrOutputBNC1)
    core.set_property(plcName, propCellConfig, addrStageSync)

    # turn on 'transmit repeated commands' for Tiger
    core.set_property('TigerCommHub', 'OnlySendSerialCommandOnChange', 'No')

    # set tile axis speed for all moves
    command = 'SPEED Y=.1'
    core.set_property('TigerCommHub', 'SerialCommand', command)

    # check to make sure Tiger is not busy
    ready = 'B'
    while (ready != 'N'):
        command = 'STATUS'
        core.set_property('TigerCommHub', 'SerialCommand', command)
        ready = core.get_property('TigerCommHub', 'SerialResponse')
        time.sleep(.500)

    # set scan axis speed for large move to initial position
    command = 'SPEED X=.1'
    core.set_property('TigerCommHub', 'SerialCommand', command)

    # check to make sure Tiger is not busy
    ready = 'B'
    while (ready != 'N'):
        command = 'STATUS'
        core.set_property('TigerCommHub', 'SerialCommand', command)
        ready = core.get_property('TigerCommHub', 'SerialResponse')
        time.sleep(.500)

    # turn off 'transmit repeated commands' for Tiger
    core.set_property('TigerCommHub', 'OnlySendSerialCommandOnChange', 'Yes')

    # move scan scan stage to initial position
    core.set_xy_position(scan_axis_start_um, tile_axis_start_um)
    core.wait_for_device(xy_stage)
    core.set_position(height_axis_start_um)
    core.wait_for_device(z_stage)

    # turn on 'transmit repeated commands' for Tiger
    core.set_property('TigerCommHub', 'OnlySendSerialCommandOnChange', 'No')

    # set scan axis speed to correct speed for continuous stage scan
    # expects mm/s
    command = 'SPEED X=' + str(scan_axis_speed)
    core.set_property('TigerCommHub', 'SerialCommand', command)

    # check to make sure Tiger is not busy
    ready = 'B'
    while (ready != 'N'):
        command = 'STATUS'
        core.set_property('TigerCommHub', 'SerialCommand', command)
        ready = core.get_property('TigerCommHub', 'SerialResponse')
        time.sleep(.500)

    # set scan axis to true 1D scan with no backlash
    command = '1SCAN X? Y=0 Z=9 F=0'
    core.set_property('TigerCommHub', 'SerialCommand', command)

    # check to make sure Tiger is not busy
    ready = 'B'
    while (ready != 'N'):
        command = 'STATUS'
        core.set_property('TigerCommHub', 'SerialCommand', command)
        ready = core.get_property('TigerCommHub', 'SerialResponse')
        time.sleep(.500)

    # set range and return speed (5% of max) for scan axis
    # expects mm
    command = '1SCANR X=' + str(scan_axis_start_mm) + ' Y=' + str(
        scan_axis_end_mm) + ' R=10'
    core.set_property('TigerCommHub', 'SerialCommand', command)

    # check to make sure Tiger is not busy
    ready = 'B'
    while (ready != 'N'):
        command = 'STATUS'
        core.set_property('TigerCommHub', 'SerialCommand', command)
        ready = core.get_property('TigerCommHub', 'SerialResponse')
        time.sleep(.500)

    # turn off 'transmit repeated commands' for Tiger
    core.set_property('TigerCommHub', 'OnlySendSerialCommandOnChange', 'Yes')

    # set all laser to external triggering
    core.set_config('Modulation-405', 'External-Digital')
    core.wait_for_config('Modulation-405', 'External-Digital')
    core.set_config('Modulation-488', 'External-Digital')
    core.wait_for_config('Modulation-488', 'External-Digital')
    core.set_config('Modulation-561', 'External-Digital')
    core.wait_for_config('Modulation-561', 'External-Digital')
    core.set_config('Modulation-637', 'External-Digital')
    core.wait_for_config('Modulation-637', 'External-Digital')
    core.set_config('Modulation-730', 'External-Digital')
    core.wait_for_config('Modulation-730', 'External-Digital')

    # turn all lasers on
    core.set_config('Laser', 'AllOn')
    core.wait_for_config('Laser', 'AllOn')

    # set lasers to user defined power
    core.set_property('Coherent-Scientific Remote',
                      'Laser 405-100C - PowerSetpoint (%)', channel_powers[0])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 488-150C - PowerSetpoint (%)', channel_powers[1])
    core.set_property('Coherent-Scientific Remote',
                      'Laser OBIS LS 561-150 - PowerSetpoint (%)',
                      channel_powers[2])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 637-140C - PowerSetpoint (%)', channel_powers[3])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 730-30C - PowerSetpoint (%)', channel_powers[4])

    # setup DAQ
    samples_per_ch = 2
    DAQ_sample_rate_Hz = 10000
    num_DI_channels = 8

    # set the galvo to neutral
    taskAO_last = daq.Task()
    taskAO_last.CreateAOVoltageChan("/Dev1/ao0", "", -4.0, 4.0,
                                    daq.DAQmx_Val_Volts, None)
    taskAO_last.WriteAnalogScalarF64(True, -1, galvo_neutral_volt, None)
    taskAO_last.StopTask()
    taskAO_last.ClearTask()

    # output experiment info
    print('Number of X positions: ' + str(scan_axis_positions))
    print('Number of Y tiles: ' + str(tile_axis_positions))
    print('Number of Z slabs: ' + str(height_axis_positions))
    print('Number of channels: ' + str(n_active_channels))

    # flags for metadata and processing
    setup_processing = True
    setup_metadata = True

    # create events to execute scan
    events = []
    for x in range(scan_axis_positions + excess_scan_positions):
        evt = {'axes': {'z': x}}
        events.append(evt)

    for t_idx in range(timepoints):
        for y_idx in range(tile_axis_positions):
            # calculate tile axis position
            tile_position_um = tile_axis_start_um + (tile_axis_step_um * y_idx)

            # move XY stage to new tile axis position
            core.set_xy_position(scan_axis_start_um, tile_position_um)
            core.wait_for_device(xy_stage)

            for z_idx in range(height_axis_positions):
                # calculate height axis position
                height_position_um = height_axis_start_um + (
                    height_axis_step_um * z_idx)

                # move Z stage to new height axis position
                core.set_position(height_position_um)
                core.wait_for_device(z_stage)

                for ch_idx in active_channel_indices:

                    # create DAQ pattern for laser strobing controlled via rolling shutter
                    dataDO = np.zeros((samples_per_ch, num_DI_channels),
                                      dtype=np.uint8)
                    dataDO[0, ch_idx] = 1
                    dataDO[1, ch_idx] = 0
                    #print(dataDO)

                    # update save_name with current tile information
                    save_name_tyzc = save_name + '_t' + str(t_idx).zfill(
                        4) + '_y' + str(y_idx).zfill(4) + '_z' + str(
                            z_idx).zfill(4) + '_ch' + str(ch_idx).zfill(4)

                    # turn on 'transmit repeated commands' for Tiger
                    core.set_property('TigerCommHub',
                                      'OnlySendSerialCommandOnChange', 'No')

                    # check to make sure Tiger is not busy
                    ready = 'B'
                    while (ready != 'N'):
                        command = 'STATUS'
                        core.set_property('TigerCommHub', 'SerialCommand',
                                          command)
                        ready = core.get_property('TigerCommHub',
                                                  'SerialResponse')
                        time.sleep(.500)

                    # turn off 'transmit repeated commands' for Tiger
                    core.set_property('TigerCommHub',
                                      'OnlySendSerialCommandOnChange', 'Yes')

                    # save actual stage positions
                    xy_pos = core.get_xy_stage_position()
                    stage_x = xy_pos.x
                    stage_y = xy_pos.y
                    stage_z = core.get_position()
                    current_stage_data = [{
                        'stage_x': stage_x,
                        'stage_y': stage_y,
                        'stage_z': stage_z
                    }]
                    df_current_stage = pd.DataFrame(current_stage_data)

                    # setup DAQ for laser strobing
                    try:
                        # ----- DIGITAL input -------
                        taskDI = daq.Task()
                        taskDI.CreateDIChan("/Dev1/PFI0", "",
                                            daq.DAQmx_Val_ChanForAllLines)

                        ## Configure change detection timing (from wave generator)
                        taskDI.CfgInputBuffer(
                            0
                        )  # must be enforced for change-detection timing, i.e no buffer
                        taskDI.CfgChangeDetectionTiming(
                            "/Dev1/PFI0", "/Dev1/PFI0",
                            daq.DAQmx_Val_ContSamps, 0)

                        ## Set where the starting trigger
                        taskDI.CfgDigEdgeStartTrig("/Dev1/PFI0",
                                                   daq.DAQmx_Val_Rising)

                        ## Export DI signal to unused PFI pins, for clock and start
                        taskDI.ExportSignal(daq.DAQmx_Val_ChangeDetectionEvent,
                                            "/Dev1/PFI2")
                        taskDI.ExportSignal(daq.DAQmx_Val_StartTrigger,
                                            "/Dev1/PFI1")

                        # ----- DIGITAL output ------
                        taskDO = daq.Task()
                        taskDO.CreateDOChan("/Dev1/port0/line0:7", "",
                                            daq.DAQmx_Val_ChanForAllLines)

                        ## Configure timing (from DI task)
                        taskDO.CfgSampClkTiming("/Dev1/PFI2",
                                                DAQ_sample_rate_Hz,
                                                daq.DAQmx_Val_Rising,
                                                daq.DAQmx_Val_ContSamps,
                                                samples_per_ch)

                        ## Write the output waveform
                        samples_per_ch_ct_digital = ct.c_int32()
                        taskDO.WriteDigitalLines(
                            samples_per_ch, False, 10.0,
                            daq.DAQmx_Val_GroupByChannel, dataDO,
                            ct.byref(samples_per_ch_ct_digital), None)

                        ## ------ Start digital input and output tasks ----------
                        taskDO.StartTask()
                        taskDI.StartTask()

                    except daq.DAQError as err:
                        print("DAQmx Error %s" % err)

                    # set camera to external control
                    # DCAM sets the camera back to INTERNAL mode after each acquisition
                    core.set_config('Camera-TriggerSource', 'EXTERNAL')
                    core.wait_for_config('Camera-TriggerSource', 'EXTERNAL')

                    # verify that camera actually switched back to external trigger mode
                    trigger_state = core.get_property('OrcaFusionBT',
                                                      'TRIGGER SOURCE')

                    # if not in external control, keep trying until camera changes settings
                    while not (trigger_state == 'EXTERNAL'):
                        time.sleep(2.0)
                        core.set_config('Camera-TriggerSource', 'EXTERNAL')
                        core.wait_for_config('Camera-TriggerSource',
                                             'EXTERNAL')
                        trigger_state = core.get_property(
                            'OrcaFusionBT', 'TRIGGER SOURCE')

                    print('T: ' + str(t_idx) + ' Y: ' + str(y_idx) + ' Z: ' +
                          str(z_idx) + ' C: ' + str(ch_idx))
                    # run acquisition for this tyzc combination
                    with Acquisition(directory=save_directory,
                                     name=save_name_tyzc,
                                     post_camera_hook_fn=camera_hook_fn,
                                     show_display=False,
                                     max_multi_res_index=0,
                                     saving_queue_size=5000) as acq:

                        acq.acquire(events)

                    # clean up acquisition so that AcqEngJ releases directory.
                    # NOTE: This currently does not work.
                    acq = None

                    acq_deleted = False
                    while not (acq_deleted):
                        try:
                            del acq
                        except:
                            time.sleep(0.1)
                            acq_deleted = False
                        else:
                            gc.collect()
                            acq_deleted = True

                    # stop DAQ and make sure it is at zero
                    try:
                        ## Stop and clear both tasks
                        taskDI.StopTask()
                        taskDO.StopTask()
                        taskDI.ClearTask()
                        taskDO.ClearTask()
                    except daq.DAQError as err:
                        print("DAQmx Error %s" % err)

                    # save experimental info after first tile.
                    # we do it this way so that Pycromanager can manage the directories.
                    if (setup_metadata):
                        # save stage scan parameters
                        scan_param_data = [{
                            'root_name':
                            str(save_name),
                            'scan_type':
                            str('stage'),
                            'theta':
                            float(30.0),
                            'scan_step':
                            float(scan_axis_step_um * 1000.),
                            'pixel_size':
                            float(pixel_size_um * 1000.),
                            'num_t':
                            int(timepoints),
                            'num_y':
                            int(tile_axis_positions),
                            'num_z':
                            int(height_axis_positions),
                            'num_ch':
                            int(n_active_channels),
                            'scan_axis_positions':
                            int(scan_axis_positions),
                            'excess_scan_positions':
                            int(excess_scan_positions),
                            'y_pixels':
                            int(y_pixels),
                            'x_pixels':
                            int(x_pixels),
                            '405_active':
                            bool(channel_states[0]),
                            '488_active':
                            bool(channel_states[1]),
                            '561_active':
                            bool(channel_states[2]),
                            '635_active':
                            bool(channel_states[3]),
                            '730_active':
                            bool(channel_states[4])
                        }]

                        # df_stage_scan_params = pd.DataFrame(scan_param_data)
                        # save_name_stage_params = save_directory / 'scan_metadata.csv'
                        # df_stage_scan_params.to_csv(save_name_stage_params)
                        data_io.write_metadata(
                            scan_param_data[0],
                            save_directory / Path('scan_metadata.csv'))

                        setup_metadata = False

                    # save stage scan positions after each tile
                    save_name_stage_positions = Path('t' +
                                                     str(t_idx).zfill(4) +
                                                     '_y' +
                                                     str(y_idx).zfill(4) +
                                                     '_z' +
                                                     str(z_idx).zfill(4) +
                                                     '_ch' +
                                                     str(ch_idx).zfill(4) +
                                                     '_stage_positions.csv')
                    save_name_stage_positions = save_directory / save_name_stage_positions
                    # todo: use data_io instead
                    df_current_stage.to_csv(save_name_stage_positions)

                    # turn on 'transmit repeated commands' for Tiger
                    core.set_property('TigerCommHub',
                                      'OnlySendSerialCommandOnChange', 'No')

                    # check to make sure Tiger is not busy
                    ready = 'B'
                    while (ready != 'N'):
                        command = 'STATUS'
                        core.set_property('TigerCommHub', 'SerialCommand',
                                          command)
                        ready = core.get_property('TigerCommHub',
                                                  'SerialResponse')
                        time.sleep(.500)

                    # turn off 'transmit repeated commands' for Tiger
                    core.set_property('TigerCommHub',
                                      'OnlySendSerialCommandOnChange', 'Yes')
                    '''
                    # if first tile, make parent directory on NAS and start reconstruction script on the server
                    if setup_processing:
                        # make home directory on NAS
                        save_directory_path = Path(save_directory)
                        remote_directory = Path('y:/') / Path(save_directory_path.parts[1])
                        cmd='mkdir ' + str(remote_directory)
                        status_mkdir = subprocess.run(cmd, shell=True)

                        # copy full experiment metadata to NAS
                        src= Path(save_directory) / Path('scan_metadata.csv') 
                        dst= Path(remote_directory) / Path('scan_metadata.csv') 
                        Thread(target=shutil.copy, args=[str(src), str(dst)]).start()

                        setup_processing=False
                    
                    # copy current tyzc metadata to NAS
                    save_directory_path = Path(save_directory)
                    remote_directory = Path('y:/') / Path(save_directory_path.parts[1])
                    src= Path(save_directory) / Path(save_name_stage_positions.parts[2])
                    dst= Path(remote_directory) / Path(save_name_stage_positions.parts[2])
                    Thread(target=shutil.copy, args=[str(src), str(dst)]).start()

                    # copy current tyzc data to NAS
                    save_directory_path = Path(save_directory)
                    remote_directory = Path('y:/') / Path(save_directory_path.parts[1])
                    src= Path(save_directory) / Path(save_name_tyzc+ '_1') 
                    dst= Path(remote_directory) / Path(save_name_tyzc+ '_1') 
                    Thread(target=shutil.copytree, args=[str(src), str(dst)]).start()
                    '''

    # set lasers to zero power
    channel_powers = [0., 0., 0., 0., 0.]
    core.set_property('Coherent-Scientific Remote',
                      'Laser 405-100C - PowerSetpoint (%)', channel_powers[0])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 488-150C - PowerSetpoint (%)', channel_powers[1])
    core.set_property('Coherent-Scientific Remote',
                      'Laser OBIS LS 561-150 - PowerSetpoint (%)',
                      channel_powers[2])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 637-140C - PowerSetpoint (%)', channel_powers[3])
    core.set_property('Coherent-Scientific Remote',
                      'Laser 730-30C - PowerSetpoint (%)', channel_powers[4])

    # turn all lasers off
    core.set_config('Laser', 'Off')
    core.wait_for_config('Laser', 'Off')

    # set all lasers back to software control
    core.set_config('Modulation-405', 'CW (constant power)')
    core.wait_for_config('Modulation-405', 'CW (constant power)')
    core.set_config('Modulation-488', 'CW (constant power)')
    core.wait_for_config('Modulation-488', 'CW (constant power)')
    core.set_config('Modulation-561', 'CW (constant power)')
    core.wait_for_config('Modulation-561', 'CW (constant power)')
    core.set_config('Modulation-637', 'CW (constant power)')
    core.wait_for_config('Modulation-637', 'CW (constant power)')
    core.set_config('Modulation-730', 'CW (constant power)')
    core.wait_for_config('Modulation-730', 'CW (constant power)')

    # set camera to internal control
    core.set_config('Camera-TriggerSource', 'INTERNAL')
    core.wait_for_config('Camera-TriggerSource', 'INTERNAL')

    bridge.close()
Example #2
0
        viewer = napari.Viewer(ndisplay=2)

        # initialize napari viewer with stack view and random data, reslice view
        scale = [(z_range[1] - z_range[0]) / z_range[2] * z_scale, size_um[1],
                 size_um[0]]
        layers = [
            viewer.add_image(data,
                             name=layer_names[c],
                             colormap=cmap[c],
                             interpolation='nearest',
                             blending='additive',
                             rendering='attenuated_mip',
                             scale=scale,
                             contrast_limits=clim) for c in range(channels[0])
        ]
        viewer.dims._roll()

        # set sliders to the middle of the stack for all three dimensions.
        # doesn't work anymore after fixing scaling
        # would have to be done for both layers
        #for dd, dim in enumerate(layers[0].data.shape):
        #    viewer.dims.set_point(dd, dim*scale[2-dd]//2)

        # define start stop buttons and add to napari gui
        # these will break when upgrading to magicgui v0.2.0
        gui_start = start_acq.Gui()
        gui_stop = stop_acq.Gui()
        viewer.window.add_dock_widget(gui_start)
        viewer.window.add_dock_widget(gui_stop)
        bridge.close()
Example #3
0
class MainWindow(NewWindow):
    def __init__(self,
                 app,
                 args=None,
                 parent=None,
                 spatial_subsampling=4,
                 time_subsampling=1):
        """
        Intrinsic Imaging GUI
        """
        self.app = app

        super(MainWindow, self).__init__(i=1, title='intrinsic imaging')

        # some initialisation
        self.running, self.stim, self.STIM = False, None, None
        self.datafolder, self.img, self.vasculature_img = '', None, None

        self.t0, self.period = 0, 1

        ### trying the camera
        try:
            # we initialize the camera
            self.bridge = Bridge()
            self.core = self.bridge.get_core()
            self.exposure = self.core.get_exposure()
            self.demo = False
            auto_shutter = self.core.get_property('Core', 'AutoShutter')
            self.core.set_property('Core', 'AutoShutter', 0)
        except BaseException as be:
            print(be)
            print('')
            print(' /!\ Problem with the Camera /!\ ')
            print('        --> no camera found ')
            print('')
            self.exposure = -1  # flag for no camera
            self.demo = True

        ########################
        ##### building GUI #####
        ########################

        self.minView = False
        self.showwindow()

        # layout (from NewWindow class)
        self.init_basic_widget_grid(wdgt_length=3, Ncol_wdgt=20, Nrow_wdgt=20)

        # -- A plot area (ViewBox + axes) for displaying the image ---
        self.view = self.graphics_layout.addViewBox(lockAspect=True,
                                                    invertY=True)
        self.view.setMenuEnabled(False)
        self.view.setAspectLocked()
        self.pimg = pg.ImageItem()

        # ---  setting subject information ---
        self.add_widget(QtWidgets.QLabel('subjects file:'))
        self.subjectFileBox = QtWidgets.QComboBox(self)
        self.subjectFileBox.addItems([
            f for f in os.listdir(subjects_path)[::-1] if f.endswith('.json')
        ])
        self.subjectFileBox.activated.connect(self.get_subject_list)
        self.add_widget(self.subjectFileBox)

        self.add_widget(QtWidgets.QLabel('subject:'))
        self.subjectBox = QtWidgets.QComboBox(self)
        self.get_subject_list()
        self.add_widget(self.subjectBox)

        self.add_widget(QtWidgets.QLabel(20 * ' - '))
        self.vascButton = QtWidgets.QPushButton(
            " - = save Vasculature Picture = - ", self)
        self.vascButton.clicked.connect(self.take_vasculature_picture)
        self.add_widget(self.vascButton)

        self.add_widget(QtWidgets.QLabel(20 * ' - '))

        # ---  data acquisition properties ---
        self.add_widget(QtWidgets.QLabel('data folder:'), spec='small-left')
        self.folderB = QtWidgets.QComboBox(self)
        self.folderB.addItems(FOLDERS.keys())
        self.add_widget(self.folderB, spec='large-right')

        self.add_widget(QtWidgets.QLabel('  - protocol:'), spec='small-left')
        self.protocolBox = QtWidgets.QComboBox(self)
        self.protocolBox.addItems(['ALL', 'up', 'down', 'left', 'right'])
        self.add_widget(self.protocolBox, spec='large-right')
        self.add_widget(
            QtWidgets.QLabel('  - exposure: %.0f ms (from Micro-Manager)' %
                             self.exposure))

        self.add_widget(QtWidgets.QLabel('  - Nrepeat :'), spec='large-left')
        self.repeatBox = QtWidgets.QLineEdit()
        self.repeatBox.setText('10')
        self.add_widget(self.repeatBox, spec='small-right')

        self.add_widget(QtWidgets.QLabel('  - stim. period (s):'),
                        spec='large-left')
        self.periodBox = QtWidgets.QLineEdit()
        self.periodBox.setText('10')
        self.add_widget(self.periodBox, spec='small-right')

        self.add_widget(QtWidgets.QLabel('  - bar size (degree):'),
                        spec='large-left')
        self.barBox = QtWidgets.QLineEdit()
        self.barBox.setText('6')
        self.add_widget(self.barBox, spec='small-right')

        self.add_widget(QtWidgets.QLabel('  - spatial sub-sampling (px):'),
                        spec='large-left')
        self.spatialBox = QtWidgets.QLineEdit()
        self.spatialBox.setText(str(spatial_subsampling))
        self.add_widget(self.spatialBox, spec='small-right')

        self.add_widget(QtWidgets.QLabel('  - acq. freq. (Hz):'),
                        spec='large-left')
        self.freqBox = QtWidgets.QLineEdit()
        self.freqBox.setText('10')
        self.add_widget(self.freqBox, spec='small-right')

        # self.add_widget(QtWidgets.QLabel('  - flick. freq. (Hz) /!\ > acq:'),
        #                 spec='large-left')
        # self.flickBox = QtWidgets.QLineEdit()
        # self.flickBox.setText('10')
        # self.add_widget(self.flickBox, spec='small-right')

        self.demoBox = QtWidgets.QCheckBox("demo mode")
        self.demoBox.setStyleSheet("color: gray;")
        self.add_widget(self.demoBox, spec='large-left')
        self.demoBox.setChecked(self.demo)

        self.camBox = QtWidgets.QCheckBox("cam.")
        self.camBox.setStyleSheet("color: gray;")
        self.add_widget(self.camBox, spec='small-right')
        self.camBox.setChecked(True)

        # ---  launching acquisition ---
        self.liveButton = QtWidgets.QPushButton("--   live view    -- ", self)
        self.liveButton.clicked.connect(self.live_view)
        self.add_widget(self.liveButton)

        # ---  launching acquisition ---
        self.acqButton = QtWidgets.QPushButton("-- RUN PROTOCOL -- ", self)
        self.acqButton.clicked.connect(self.launch_protocol)
        self.add_widget(self.acqButton, spec='large-left')
        self.stopButton = QtWidgets.QPushButton(" STOP ", self)
        self.stopButton.clicked.connect(self.stop_protocol)
        self.add_widget(self.stopButton, spec='small-right')

        # ---  launching analysis ---
        self.add_widget(QtWidgets.QLabel(20 * ' - '))
        self.analysisButton = QtWidgets.QPushButton(" - = Analysis GUI = - ",
                                                    self)
        self.analysisButton.clicked.connect(self.open_analysis)
        self.add_widget(self.analysisButton, spec='large-left')

        self.pimg.setImage(0 * self.get_frame())
        self.view.addItem(self.pimg)
        self.view.autoRange(padding=0.001)
        self.analysisWindow = None

    def take_vasculature_picture(self):

        filename = generate_filename_path(FOLDERS[self.folderB.currentText()],
                                          filename='vasculature-%s' %
                                          self.subjectBox.currentText(),
                                          extension='.tif')

        # save HQ image as tiff
        img = self.get_frame(force_HQ=True)
        np.save(filename.replace('.tif', '.npy'), img)
        img = np.array(255 * (img - img.min()) / (img.max() - img.min()),
                       dtype=np.uint8)
        im = PIL.Image.fromarray(img)
        im.save(filename)
        print('vasculature image, saved as:')
        print(filename)

        # then keep a version to store with imaging:
        self.vasculature_img = self.get_frame()
        self.pimg.setImage(img)  # show on displayn

    def open_analysis(self):

        self.analysisWindow = runAnalysis(self.app, parent=self)

    def get_subject_list(self):
        with open(
                os.path.join(subjects_path,
                             self.subjectFileBox.currentText())) as f:
            self.subjects = json.load(f)
        self.subjectBox.clear()
        self.subjectBox.addItems(self.subjects.keys())

    def init_visual_stim(self, demo=True):

        with open(
                os.path.join(
                    pathlib.Path(__file__).resolve().parents[1], 'intrinsic',
                    'vis_stim', 'up.json'), 'r') as fp:
            protocol = json.load(fp)

        if self.demoBox.isChecked():
            protocol['demo'] = True

        self.stim = visual_stim.build_stim(protocol)
        self.parent = dummy_parent()

    def get_patterns(self, protocol, angle, size, Npatch=30):

        patterns = []

        if protocol in ['left', 'right']:
            z = np.linspace(-self.stim.screen['resolution'][1],
                            self.stim.screen['resolution'][1], Npatch)
            for i in np.arange(len(z) - 1)[(1 if self.flip else 0)::2]:
                patterns.append(
                    visual.Rect(win=self.stim.win,
                                size=(self.stim.angle_to_pix(size),
                                      z[1] - z[0]),
                                pos=(self.stim.angle_to_pix(angle), z[i]),
                                units='pix',
                                fillColor=1))
        if protocol in ['up', 'down']:
            x = np.linspace(-self.stim.screen['resolution'][0],
                            self.stim.screen['resolution'][0], Npatch)
            for i in np.arange(len(x) - 1)[(1 if self.flip else 0)::2]:
                patterns.append(
                    visual.Rect(win=self.stim.win,
                                size=(x[1] - x[0],
                                      self.stim.angle_to_pix(size)),
                                pos=(x[i], self.stim.angle_to_pix(angle)),
                                units='pix',
                                fillColor=1))

        return patterns

    def run(self):

        self.flip = False

        self.stim = visual_stim(
            {
                "Screen": "Dell-2020",
                "presentation-prestim-screen": -1,
                "presentation-poststim-screen": -1
            },
            demo=self.demoBox.isChecked())

        self.Nrepeat = int(self.repeatBox.text())  #
        self.period = float(self.periodBox.text())  # degree / second
        self.bar_size = float(self.barBox.text())  # degree / second
        # self.dt_save, self.dt = 1./float(self.freqBox.text()), 1./float(self.flickBox.text())
        self.dt_save, self.dt = 1. / float(self.freqBox.text()), 1. / float(
            self.freqBox.text())

        xmin, xmax = 1.15 * np.min(self.stim.x), 1.15 * np.max(self.stim.x)
        zmin, zmax = 1.3 * np.min(self.stim.z), 1.3 * np.max(self.stim.z)

        self.angle_start, self.angle_max, self.protocol, self.label = 0, 0, '', ''
        self.Npoints = int(self.period / self.dt_save)

        if self.protocolBox.currentText() == 'ALL':
            self.STIM = {
                'angle_start': [zmin, xmax, zmax, xmin],
                'angle_stop': [zmax, xmin, zmin, xmax],
                'label': ['up', 'left', 'down', 'right'],
                'xmin': xmin,
                'xmax': xmax,
                'zmin': zmin,
                'zmax': zmax
            }
            self.label = 'up'  # starting point
        else:
            self.STIM = {
                'label': [self.protocolBox.currentText()],
                'xmin': xmin,
                'xmax': xmax,
                'zmin': zmin,
                'zmax': zmax
            }
            if self.protocolBox.currentText() == 'up':
                self.STIM['angle_start'] = [zmin]
                self.STIM['angle_stop'] = [zmax]
            if self.protocolBox.currentText() == 'down':
                self.STIM['angle_start'] = [zmax]
                self.STIM['angle_stop'] = [zmin]
            if self.protocolBox.currentText() == 'left':
                self.STIM['angle_start'] = [xmax]
                self.STIM['angle_stop'] = [xmin]
            if self.protocolBox.currentText() == 'right':
                self.STIM['angle_start'] = [xmin]
                self.STIM['angle_stop'] = [xmax]
            self.label = self.protocolBox.currentText()

        for il, label in enumerate(self.STIM['label']):
            self.STIM[label + '-times'] = np.arange(
                self.Npoints * self.Nrepeat) * self.dt_save
            self.STIM[label + '-angle'] = np.concatenate([
                np.linspace(self.STIM['angle_start'][il],
                            self.STIM['angle_stop'][il], self.Npoints)
                for n in range(self.Nrepeat)
            ])

        # initialize one episode:
        self.iEp, self.iTime, self.t0_episode = 0, 0, time.time()

        self.img, self.nSave = self.new_img(), 0

        self.save_metadata()

        print('acquisition running [...]')

        self.update_dt()  # while loop

    def new_img(self):
        return np.zeros(self.imgsize, dtype=np.float64)

    def save_img(self):

        if self.nSave > 0:
            self.img /= self.nSave

        if True:  # live display
            self.pimg.setImage(self.img)

        # NEED TO STORE DATA HERE
        self.FRAMES.append(self.img)

        # re-init time step of acquisition
        self.img, self.nSave = self.new_img(), 0

    def update_dt(self):

        self.tSave = time.time()

        while (time.time() - self.tSave) <= self.dt_save:

            self.t = time.time()
            # show image
            patterns = self.get_patterns(
                self.STIM['label'][self.iEp % len(self.STIM['label'])],
                self.STIM[self.STIM['label'][self.iEp %
                                             len(self.STIM['label'])] +
                          '-angle'][self.iTime], self.bar_size)
            for pattern in patterns:
                pattern.draw()
            try:
                self.stim.win.flip()
            except BaseException:
                pass

            if self.camBox.isChecked():
                # # fetch image
                self.img += self.get_frame()
                self.nSave += 1.0

            # time.sleep(max([self.dt-(time.time()-self.t), 0]))
            # self.flip = (False if self.flip else True) # flip the flag

        self.flip = (False if self.flip else True)  # flip the flag

        if self.camBox.isChecked():
            self.save_img()  # re-init image here

        self.iTime += 1

        # checking if not episode over
        if not (self.iTime < len(self.STIM[self.STIM['label'][
                self.iEp % len(self.STIM['label'])] + '-angle'])):
            if self.camBox.isChecked():
                self.write_data()  # writing data when over
            self.t0_episode, self.img, self.nSave = time.time(), self.new_img(
            ), 0
            self.FRAMES = []  # re init data
            self.iTime = 0
            self.iEp += 1

        # continuing ?
        if self.running:
            QtCore.QTimer.singleShot(1, self.update_dt)

    def write_data(self):

        filename = '%s-%i.nwb' % (self.STIM['label'][self.iEp % len(
            self.STIM['label'])], int(self.iEp / len(self.STIM['label'])) + 1)

        nwbfile = pynwb.NWBFile(
            'Intrinsic Imaging data following bar stimulation',
            'intrinsic',
            datetime.datetime.utcnow(),
            file_create_date=datetime.datetime.utcnow())

        # Create our time series
        angles = pynwb.TimeSeries(
            name='angle_timeseries',
            data=self.STIM[self.STIM['label'][self.iEp %
                                              len(self.STIM['label'])] +
                           '-angle'],
            unit='Rd',
            timestamps=self.STIM[self.STIM['label'][self.iEp %
                                                    len(self.STIM['label'])] +
                                 '-times'])
        nwbfile.add_acquisition(angles)

        images = pynwb.image.ImageSeries(
            name='image_timeseries',
            data=np.array(self.FRAMES, dtype=np.float64),
            unit='a.u.',
            timestamps=self.STIM[self.STIM['label'][self.iEp %
                                                    len(self.STIM['label'])] +
                                 '-times'])

        nwbfile.add_acquisition(images)

        # Write the data to file
        io = pynwb.NWBHDF5IO(os.path.join(self.datafolder, filename), 'w')
        print('writing:', filename)
        io.write(nwbfile)
        io.close()
        print(filename, ' saved !')

    def save_metadata(self):

        filename = generate_filename_path(FOLDERS[self.folderB.currentText()],
                                          filename='metadata',
                                          extension='.npy')
        metadata = {
            'subject': str(self.subjectBox.currentText()),
            'exposure': self.exposure,
            'bar-size': float(self.barBox.text()),
            'acq-freq': float(self.freqBox.text()),
            'period': float(self.periodBox.text()),
            'Nrepeat': int(self.repeatBox.text()),
            'imgsize': self.imgsize,
            'STIM': self.STIM
        }

        np.save(filename, metadata)
        if self.vasculature_img is not None:
            np.save(filename.replace('metadata', 'vasculature'),
                    self.vasculature_img)

        self.datafolder = os.path.dirname(filename)

    def launch_protocol(self):

        if not self.running:
            self.running = True

            # initialization of data
            self.FRAMES = []
            self.img = self.get_frame()
            self.imgsize = self.img.shape
            self.pimg.setImage(self.img)
            self.view.autoRange(padding=0.001)

            self.run()

        else:
            print(
                ' /!\  --> pb in launching acquisition (either already running or missing camera)'
            )

    def live_view(self):
        self.running, self.t0 = True, time.time()
        self.update_Image()

    def stop_protocol(self):
        if self.running:
            self.running = False
            if self.stim is not None:
                self.stim.close()
        else:
            print('acquisition not launched')

    def get_frame(self, force_HQ=False):

        if self.exposure > 0:
            self.core.snap_image()
            tagged_image = self.core.get_tagged_image()
            #pixels by default come out as a 1D array. We can reshape them into an image
            img = np.reshape(tagged_image.pix,
                             newshape=[
                                 tagged_image.tags['Height'],
                                 tagged_image.tags['Width']
                             ])
        elif (self.stim is not None) and (self.STIM is not None):
            it = int((time.time() - self.t0_episode) / self.dt_save) % int(
                self.period / self.dt_save)
            protocol = self.STIM['label'][self.iEp % len(self.STIM['label'])]
            if protocol == 'left':
                img = np.random.randn(*self.stim.x.shape)+\
                    np.exp(-(self.stim.x-(40*it/self.Npoints-20))**2/2./10**2)*\
                    np.exp(-self.stim.z**2/2./15**2)
            elif protocol == 'right':
                img = np.random.randn(*self.stim.x.shape)+\
                    np.exp(-(self.stim.x+(40*it/self.Npoints-20))**2/2./10**2)*\
                    np.exp(-self.stim.z**2/2./15**2)
            elif protocol == 'up':
                img = np.random.randn(*self.stim.x.shape)+\
                    np.exp(-(self.stim.z-(40*it/self.Npoints-20))**2/2./10**2)*\
                    np.exp(-self.stim.x**2/2./15**2)
            else:  # down
                img = np.random.randn(*self.stim.x.shape)+\
                    np.exp(-(self.stim.z+(40*it/self.Npoints-20))**2/2./10**2)*\
                    np.exp(-self.stim.x**2/2./15**2)

        else:
            img = np.random.randn(720, 1280)

        if (int(self.spatialBox.text()) > 1) and not force_HQ:
            return 1.0 * analysis.resample_img(img, int(
                self.spatialBox.text()))
        else:
            return 1.0 * img

    def update_Image(self):
        # plot it
        self.pimg.setImage(self.get_frame())
        new_t0 = time.time()
        print('dt=%.1f ms' % (1e3 * (new_t0 - self.t0)))
        self.t0 = new_t0
        if self.running:
            QtCore.QTimer.singleShot(1, self.update_Image)

    def hitting_space(self):
        if not self.running:
            self.launch_protocol()
        else:
            self.stop_protocol()

    def process(self):
        self.launch_analysis()

    def quit(self):
        if self.exposure > 0:
            self.bridge.close()
        sys.exit()