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()
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()
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()