def pre_run(self): ''' Initialization of the ImageManager class and of figures ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val/self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val/self.camera.binning.val) self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val ) _plots = [None]*len(self.channels) plot0 = pg.PlotItem(title="channel0") self.imv0 = pg.ImageView(view = plot0) self.imv0.ui.histogram.hide() self.imv0.ui.roiBtn.hide() self.imv0.ui.menuBtn.hide() self.imv0.show() plot1 = pg.PlotItem(title="channel1") self.imv1 = pg.ImageView(view = plot1) self.imv1.ui.histogram.hide() self.imv1.ui.roiBtn.hide() self.imv1.ui.menuBtn.hide() self.imv1.show() self.settings['captured_cells'] = 0
def pre_run(self): ''' Initialization of the ImageManager class and of figures and creation of the image panels ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val / self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val / self.camera.binning.val) numChannels = self.settings.channels_num.val self.channels = list(range(numChannels)) # channels = [0,1,2,...] self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val, numChannels) imvs = [] for ch in self.channels: plot = pg.PlotItem(title=f'channel{ch}') imv = pg.ImageView(view=plot) imv.ui.histogram.hide() imv.ui.roiBtn.hide() imv.ui.menuBtn.hide() imv.show() imvs.append(imv) self.imvs = imvs self.settings['captured_cells'] = 0
class PROCHIP_termalshifter_Measurement(Measurement): name = "PROCHIP_termalshifter" def setup(self): "..." self.ui_filename = sibling_path(__file__, "DualColor.ui") self.ui = load_qt_ui_file(self.ui_filename) # settings creation self.settings.New('refresh_period', dtype=float, unit='s', spinbox_decimals = 4, initial=0.08, vmin = 0 ,vmax=10) self.settings.New('auto_range_0', dtype=bool, initial=True) self.settings.New('auto_levels_0', dtype=bool, initial=True) self.settings.New('level_min_0', dtype=int, initial=60) self.settings.New('level_max_0', dtype=int, initial=150) self.settings.New('auto_range_1', dtype=bool, initial=True) self.settings.New('auto_levels_1', dtype=bool, initial=True) self.settings.New('level_min_1', dtype=int, initial=60) self.settings.New('level_max_1', dtype=int, initial=150) self.settings.New('save_h5', dtype=bool, initial=False) self.settings.New('save_roi_h5', dtype=bool, initial=False) self.settings.New('roi_half_side', dtype=int, initial=100) self.settings.New('min_cell_size', dtype=int, initial=1600) self.settings.New('selected_channel', dtype=int, initial=0, vmin = 0, vmax = 1) self.settings.New('captured_cells', dtype=int, initial=0) self.settings.New('acq_freq', dtype=float, unit='Hz', initial=200) self.settings.New('xsampling', dtype=float, unit='um', initial=0.11) self.settings.New('ysampling', dtype=float, unit='um', initial=0.11) self.settings.New('zsampling', dtype=float, unit='um', initial=1.0) self.settings.New('flowrate', dtype=float, unit='nl/s', initial=250.0) self.camera = self.app.hardware['HamamatsuHardware'] self.display_update_period = self.settings.refresh_period.val self.laser_0 = self.app.hardware['Laser_0'] self.laser_1 = self.app.hardware['Laser_1'] self.ni_co_0 = self.app.hardware['Counter_Output_0'] self.ni_co_1 = self.app.hardware['Counter_Output_1'] self.ni_do_0 = self.app.hardware['Digital_Output_0'] self.ni_ao_0 = self.app.hardware['Analog_Output_0'] self.channels = [0,1] def setup_figure(self): """ Runs once during App initialization, after setup() This is the place to make all graphical interface initializations, build plots, etc. """ # connect ui widgets to measurement/hardware settings or functions self.ui.start_pushButton.clicked.connect(self.start) self.ui.interrupt_pushButton.clicked.connect(self.interrupt) self.settings.save_h5.connect_to_widget(self.ui.save_h5_checkBox) self.settings.save_roi_h5.connect_to_widget(self.ui.save_ROI_h5_checkBox) self.settings.auto_levels_0.connect_to_widget(self.ui.autoLevels_checkBox0) self.settings.auto_range_0.connect_to_widget(self.ui.autoRange_checkBox0) self.settings.level_min_0.connect_to_widget(self.ui.min_doubleSpinBox0) self.settings.level_max_0.connect_to_widget(self.ui.max_doubleSpinBox0) self.settings.auto_levels_1.connect_to_widget(self.ui.autoLevels_checkBox1) self.settings.auto_range_1.connect_to_widget(self.ui.autoRange_checkBox1) self.settings.level_min_1.connect_to_widget(self.ui.min_doubleSpinBox1) self.settings.level_max_1.connect_to_widget(self.ui.max_doubleSpinBox1) self.settings.selected_channel.connect_to_widget(self.ui.ch_doubleSpinBox) self.settings.captured_cells.connect_to_widget(self.ui.captured_doubleSpinBox) self.settings.flowrate.connect_to_widget(self.ui.flowrate_doubleSpinBox) def pre_run(self): ''' Initialization of the ImageManager class and of figures ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val/self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val/self.camera.binning.val) self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val ) _plots = [None]*len(self.channels) plot0 = pg.PlotItem(title="channel0") self.imv0 = pg.ImageView(view = plot0) self.imv0.ui.histogram.hide() self.imv0.ui.roiBtn.hide() self.imv0.ui.menuBtn.hide() self.imv0.show() plot1 = pg.PlotItem(title="channel1") self.imv1 = pg.ImageView(view = plot1) self.imv1.ui.histogram.hide() self.imv1.ui.roiBtn.hide() self.imv1.ui.menuBtn.hide() self.imv1.show() self.settings['captured_cells'] = 0 def run(self): eff_subarrayh = self.eff_subarrayh eff_subarrayv = self.eff_subarrayv try: self.camera.read_from_hardware() freq1 = self.settings['acq_freq'] self.start_triggered_Acquisition(freq1) first_cycle = True # it will become False when the roi_h5_file will be created z_index_roi = [0]*len(self.channels) # list of z indexes in which each element is the z index corresponding to a different channel roi_index = 0 # absolute index of rois num_rois = 0 # number of rois detected in the current image num_active_rois = 0 # number of rois detected in the previous image self.roi_h5 = [] # content to be saved in the roi_h5_file self.image_h5 = [None]*len(self.channels) # prepare list to contain the image data to be saved in the h5file active_rois =[] active_cx = [] active_cy = [] while not self.interrupt_measurement_called: # "run till abort" mode [frames, _dims] = self.camera.hamamatsu.getFrames() # processing of each acquired image for frame_index, frame in enumerate(frames): # define the correct channel in use for cells detection channel_index = (self.camera.hamamatsu.buffer_index - self.camera.hamamatsu.backlog + frame_index + 1) % 2 self.np_data = frame.getData() self.im.image[channel_index] = np.reshape(self.np_data, (eff_subarrayv, eff_subarrayh)) # detect all the cells present in this frame (do it only on the selected channel and not both) if channel_index == self.settings.selected_channel.val: self.im.find_cell(self.settings.selected_channel.val) if self.settings['save_roi_h5']: # num_rois = len(self.im.contour) # create and initialize the roi h5 file if it does not exist yet if first_cycle: self.init_roi_h5() first_cycle = False num_rois = len(self.im.contours) num_active_rois = len(active_rois) if num_rois == num_active_rois: active_rois = self.im.roi_creation(channel_index, active_cx, active_cy) else: active_rois = self.im.roi_creation(channel_index, self.im.cx, self.im.cy) active_cx = self.im.cx active_cy = self.im.cy for i, roi in enumerate(active_rois): # create a new dataset if we are dealing with a new cell self.roi_h5_dataset(roi_index, channel_index) # dynamically increment the dimension of the "box" in which we are going to put a new roi image if z_index_roi[channel_index] != 0: self.roi_h5[channel_index].resize(self.roi_h5[channel_index].shape[0]+1, axis = 0) self.roi_h5[channel_index][z_index_roi[channel_index], :, :] = roi self.h5_roi_file.flush() # this allow us to open the h5 file also while it is not completely created yet z_index_roi[channel_index] += 1 if num_rois < num_active_rois: roi_index += 1 z_index_roi = [0]*len(self.channels) # one roi is disapperead: update indexes, ready for a new cell # this is thought for a single cell, multi rois are not always managed self.settings['captured_cells'] = roi_index if self.settings['save_h5']: progress_index = 0 # temporarily stop the acquisition in order not to overwrite the camera buffer self.pause_triggered_Acquisition() print("\n \n ******* \n \n Saving :D !\n \n *******") [frames, _dims] = self.camera.hamamatsu.getLastTotFrames() # create and initialize h5file self.initH5() z_index = [0]*len(self.channels) # list of indexes in which each element is the z index corresponding to a different channel buffer_index = self.camera.hamamatsu.buffer_index + 1 for aframe in frames: self.np_data = aframe.getData() image_on_the_run = np.reshape(self.np_data, (eff_subarrayv, eff_subarrayh)) ch_on_the_run = buffer_index%2 # 0 if the image is even, 1 if the image is odd, in the image stack self.image_h5[ch_on_the_run][z_index[ch_on_the_run], :, :] = image_on_the_run # saving to the h5 dataset z_index[ch_on_the_run] += 1 self.h5file.flush() self.settings['progress'] = progress_index*100./self.camera.hamamatsu.number_image_buffers progress_index += 1 buffer_index += 1 self.h5file.close() # finally save and close the h5 file created self.settings['save_h5'] = False # update the value to False, so the save_h5 can be called again self.settings['progress'] = 0 # restart the acquisition self.restart_triggered_Acquisition(freq1) finally: self.stop_triggered_Acquisition() # close all the h5 file still open if self.settings['save_h5']: self.h5file.close() self.settings['save_h5'] = False if self.settings['save_roi_h5']: self.h5_roi_file.close() self.settings['save_roi_h5'] = False def post_run(self): ''' Close all the figures after the run ended ''' self.imv0.close() self.imv1.close() def update_display(self): """ Displays the numpy array called displayed_image for each channel. This function runs repeatedly and automatically during the measurement run. Its update frequency is defined by self.display_update_period. """ for ch in self.channels: # choose the setting keys according to the channel to update autorange_key = f'auto_range_{ch}' autolevel_key = f'auto_levels_{ch}' level_min_key = f'level_min_{ch}' level_max_key = f'level_max_{ch}' if self.settings[autolevel_key]: # if autolevel is ON, normalize the image to its max and min level_min = np.amin(self.im.image[ch]) level_max = np.amax(self.im.image[ch]) self.settings[level_min_key] = level_min self.settings[level_max_key] = level_max else: # if autolevel is OFF, normalize the image to the choosen values level_min = self.settings[level_min_key] level_max = self.settings[level_max_key] # note that these levels are uint16, but the visulaized image is uint8, for compatibility with opencv processing (contours and rectangles annotations) # thresolding is required if autolevel is OFF; it could be avoided if autolevel is ON img_thres = np.clip(self.im.image[ch], level_min, level_max) # conversion to 8bit is done here for compatibility with opencv image8bit_normalized = ((img_thres-level_min+1)/(level_max-level_min+1)*255).astype('uint8') # creation of the image with open cv annotations, ready to be displayed displayed_image = self.im.draw_contours_on_image(image8bit_normalized) # display the image with a frame around the figure corresponding to the channel selected to do the find_cell operation (selected_channel) if ch == self.settings.selected_channel.val: #cv2.rectangle(displayed_image,(0,0),(self.eff_subarrayh-1,self.eff_subarrayv-1),(255,255,0),3) self.im.highlight_channel(displayed_image) imv_key = f'imv{ch}' imv = getattr(self, imv_key) imv.setImage(displayed_image, autoLevels=False, autoRange=self.settings[autorange_key], levels=(0,255)) def updateIndex(self, last_frame_index): """ Update the index of the image to fetch from buffer. If we reach the end of the buffer, we reset the index. """ last_frame_index += 1 if last_frame_index > self.camera.hamamatsu.number_image_buffers - 1: # if we reach the end of the buffer last_frame_index = 0 # reset return last_frame_index def start_laser(self, laserHW): ''' Laser is prepared for digital modulation at the power specified. Laser is turned OFF before''' if laserHW.laser_status.val == 'ON': self.stop_laser(laserHW) if laserHW.connected.val: # do this only if the laser was connected by the user! if laserHW.operating_mode.val != 'DIGITAL': laserHW.operating_mode.val = 'DIGITAL' laserHW.operating_mode.write_to_hardware() laserHW.laser_status.val = 'ON' laserHW.laser_status.write_to_hardware() laserHW.read_from_hardware() def stop_laser(self, laserHW): ''' Laser is turned off ''' if laserHW.connected.val: # do this only if the laser was connected by the user! laserHW.laser_status.val = 'OFF' laserHW.laser_status.write_to_hardware() def start_digital_rising_edge(self, digitalHW): '''The digital output of the DAQ start a rising edge procedure at the channel set by the user ''' digitalHW.value.val=0 digitalHW.write_value() digitalHW.value.val=1 digitalHW.write_value() # Trigger digitalHW.read_from_hardware() def start_triggered_counter_task(self, counterHW, initial_delay, freq, duty_cycle): if counterHW.connected.val: # do this only if the counter was connected by the user counterHW.settings['freq'] = freq counterHW.settings['duty_cycle'] = duty_cycle counterHW.settings['trigger'] = True counterHW.settings['initial_delay'] = initial_delay counterHW.start() counterHW.read_from_hardware() def start_triggered_AO_task(self, analog_output_HW, freq): if analog_output_HW.connected.val: # do this only if the AO was connected by the user analog_output_HW.settings['frequency'] = freq analog_output_HW.settings['trigger'] = True analog_output_HW.start() analog_output_HW.read_from_hardware() def stop_counter_task(self, counterHW): ''' Stop counter output task ''' if counterHW.connected.val: # do this only if the counter was connected by the user! counterHW.stop() def stop_analog_task(self, analog_output_HW): ''' Stop analog output task ''' if analog_output_HW.connected.val: # do this only if the AO was connected by the user! analog_output_HW.stop() def start_triggered_Acquisition(self,freq1): ''' The camera is operated with external start trigger mode and will be triggered by one of the DAQ counters ''' self.camera.settings.trigger_source.update_value(new_val = 'external') self.camera.settings.acquisition_mode.update_value(new_val = 'run_till_abort') self.camera.trmode.val='normal' self.camera.trmode.write_to_hardware() self.camera.tractive.val='syncreadout' self.camera.tractive.write_to_hardware() self.camera.read_from_hardware() self.camera.hamamatsu.startAcquisition() self.start_laser(self.laser_0) self.start_laser(self.laser_1) self.start_triggers(freq1) def start_triggers(self,freq1): t_readout = self.camera.hamamatsu.getPropertyValue("internal_line_interval")[0] camera_dutycycle = freq1*t_readout*self.camera.subarrayv.val # dutycycle for simple laser switch hardware camera_dutycycle=0.5 #TODO write correct sync to laser and camera self.start_triggered_counter_task(self.ni_co_0, initial_delay=0.0000, freq=freq1, duty_cycle=camera_dutycycle) # counterOutput2 used to control 2 lasers via 1 signal and a duplication port with a buffer and a NOT. self.start_triggered_counter_task(self.ni_co_1, initial_delay=0.00003896, freq=freq1/2, duty_cycle=0.5) self.start_triggered_AO_task(self.ni_ao_0, freq1) self.start_digital_rising_edge(self.ni_do_0) def pause_triggered_Acquisition(self): self.camera.hamamatsu.stopAcquisitionNotReleasing() self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) self.stop_analog_task(self.ni_ao_0) def restart_triggered_Acquisition(self, freq1): self.camera.hamamatsu.startAcquisitionWithoutAlloc() self.start_triggers(freq1) def stop_triggered_Acquisition(self): ''' Acquisition is terminated, laser and counters turned off ''' self.camera.hamamatsu.stopAcquisition() self.stop_laser(self.laser_0) self.stop_laser(self.laser_1) self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) self.stop_analog_task(self.ni_ao_0) self.camera.settings['trigger_source'] = 'internal' self.camera.trsource.write_to_hardware() self.camera.read_from_hardware() def create_saving_directory(self): if not os.path.isdir(self.app.settings['save_dir']): os.makedirs(self.app.settings['save_dir']) def initH5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}.h5' if sample == '': sample_name = '_'.join([timestamp, self.name]) else: sample_name = '_'.join([timestamp, self.name, sample]) fname = os.path.join(self.app.settings['save_dir'], sample_name + '.h5') # file creation self.h5file = h5_io.h5_base_file(app=self.app, measurement=self, fname = fname) self.h5_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5file) img_size=self.im.image[0].shape # both image[0] and image[1] are valid, since they have the same shape number_of_channels = len(self.channels) # take as third dimension of the file the total number of images collected in the buffer if self.camera.hamamatsu.last_frame_number < self.camera.hamamatsu.number_image_buffers: length = int((self.camera.hamamatsu.last_frame_number+1)/number_of_channels) else: length=self.camera.hamamatsu.number_image_buffers/number_of_channels #divided by two since we have the two channels for ch_index in self.channels: name = f't0/c{ch_index}/image' self.image_h5[ch_index] = self.h5_group.create_dataset( name = name, shape = ( length, img_size[0], img_size[1]), dtype = self.im.image[0].dtype, chunks = (1, img_size[0], img_size[1]) ) self.image_h5[ch_index].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.image_h5[ch_index].attrs['acq_time'] = timestamp self.roi_h5[ch_index].attrs['flowrate'] = self.settings['flowrate'] def init_roi_h5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}_ROI.h5' if sample == '': sample_name = '_'.join([timestamp, self.name, 'ROI.h5']) else: sample_name = '_'.join([timestamp, self.name, sample, 'ROI.h5']) fname = os.path.join(self.app.settings['save_dir'], sample_name) # file creation self.h5_roi_file = h5_io.h5_base_file(app=self.app, measurement=self, fname = fname) self.h5_roi_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5_roi_file) def roi_h5_dataset(self, t_index, c_index): """ Dataset creation function. It creates new datasets only when there are new cells detected by the algorithm """ roi_size = self.settings.roi_half_side.val*2 # roi dimension timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) if len(self.roi_h5) == 0: # creation of the first two datasets (one for each channel) for ch in self.channels: name = f't{0:04d}/c{ch}/roi' # data set name, initially with t index 0000 self.roi_h5.append(self.h5_roi_group.create_dataset( name = name, shape = (1, roi_size, roi_size), maxshape = ( None, roi_size, roi_size), dtype = np.uint16, chunks = (1, roi_size, roi_size) ) ) # dataset attributes self.roi_h5[ch].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.roi_h5[ch].attrs['flowrate'] = self.settings['flowrate'] self.roi_h5[ch].attrs['acq_time'] = timestamp # self.roi_h5[c_index].attrs['centroid_x'] = self.im.cx[0] # to be updated when multiple rois are saved # self.roi_h5[c_index].attrs['centroid_y'] = self.im.cy[0] self.roi_h5[ch].attrs['centroid_x'] = self.im.cx[0] # to be updated when multiple rois are saved self.roi_h5[ch].attrs['centroid_y'] = self.im.cy[0] else: name = f't{t_index:04d}/c{c_index}/roi' # dataset name fullname = self.h5_roi_group.name + '/' + name if self.roi_h5[c_index].name != fullname: # create a new dataset only if the name does not exist yet self.roi_h5[c_index] = self.h5_roi_group.create_dataset( name = name, shape = (1, roi_size, roi_size), maxshape = ( None, roi_size, roi_size), dtype = np.uint16, chunks = (1, roi_size, roi_size) ) # dataset attributes self.roi_h5[c_index].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.roi_h5[ch].attrs['flowrate'] = self.settings['flowrate'] self.roi_h5[c_index].attrs['acq_time'] = timestamp self.roi_h5[c_index].attrs['centroid_x'] = self.im.cx[0] # to be updated when multiple rois are saved self.roi_h5[c_index].attrs['centroid_y'] = self.im.cy[0]
class PROCHIP_Measurement(Measurement): name = "PROCHIP" #PROCHIP_Measurement def setup(self): "..." self.ui_filename = sibling_path(__file__, "DualColor.ui") self.ui = load_qt_ui_file(self.ui_filename) # settings creation self.settings.New('refresh_period', dtype=float, unit='s', spinbox_decimals = 4, initial=0.08, vmin = 0 ,vmax=10) self.settings.New('auto_range_0', dtype=bool, initial=True) self.settings.New('auto_levels_0', dtype=bool, initial=True) self.settings.New('level_min_0', dtype=int, initial=60) self.settings.New('level_max_0', dtype=int, initial=150) self.settings.New('auto_range_1', dtype=bool, initial=True) self.settings.New('auto_levels_1', dtype=bool, initial=True) self.settings.New('level_min_1', dtype=int, initial=60) self.settings.New('level_max_1', dtype=int, initial=150) self.settings.New('save_h5', dtype=bool, initial=False) self.settings.New('save_roi_h5', dtype=bool, initial=False) self.settings.New('roi_half_side', dtype=int, initial=100) self.settings.New('min_cell_size', dtype=int, initial=1600) self.settings.New('selected_channel', dtype=int, initial=0, vmin = 0, vmax = 1) self.settings.New('captured_cells', dtype=int, initial=0) self.settings.New('acq_freq', dtype=float, unit='Hz', initial=200) self.settings.New('xsampling', dtype=float, unit='um', initial=0.11) self.settings.New('ysampling', dtype=float, unit='um', initial=0.11) self.settings.New('zsampling', dtype=float, unit='um', initial=1.0) self.camera = self.app.hardware['HamamatsuHardware'] self.display_update_period = self.settings.refresh_period.val self.laser_0 = self.app.hardware['Laser_0'] self.laser_1 = self.app.hardware['Laser_1'] self.ni_co_0 = self.app.hardware['Counter_Output_0'] self.ni_co_1 = self.app.hardware['Counter_Output_1'] self.ni_do_0 = self.app.hardware['Digital_Output_0'] self.channels = [0,1] def setup_figure(self): """ Runs once during App initialization, after setup() This is the place to make all graphical interface initializations, build plots, etc. """ # connect ui widgets to measurement/hardware settings or functions self.ui.start_pushButton.clicked.connect(self.start) self.ui.interrupt_pushButton.clicked.connect(self.interrupt) self.settings.save_h5.connect_to_widget(self.ui.save_h5_checkBox) self.settings.save_roi_h5.connect_to_widget(self.ui.save_ROI_h5_checkBox) self.settings.auto_levels_0.connect_to_widget(self.ui.autoLevels_checkBox0) self.settings.auto_range_0.connect_to_widget(self.ui.autoRange_checkBox0) self.settings.level_min_0.connect_to_widget(self.ui.min_doubleSpinBox0) self.settings.level_max_0.connect_to_widget(self.ui.max_doubleSpinBox0) self.settings.auto_levels_1.connect_to_widget(self.ui.autoLevels_checkBox1) self.settings.auto_range_1.connect_to_widget(self.ui.autoRange_checkBox1) self.settings.level_min_1.connect_to_widget(self.ui.min_doubleSpinBox1) self.settings.level_max_1.connect_to_widget(self.ui.max_doubleSpinBox1) self.settings.selected_channel.connect_to_widget(self.ui.ch_doubleSpinBox) self.settings.captured_cells.connect_to_widget(self.ui.captured_doubleSpinBox) def pre_run(self): ''' Initialization of the ImageManager class and of figures ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val/self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val/self.camera.binning.val) self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val ) plot0 = pg.PlotItem(title="channel0") self.imv0 = pg.ImageView(view = plot0) self.imv0.ui.histogram.hide() self.imv0.ui.roiBtn.hide() self.imv0.ui.menuBtn.hide() self.imv0.show() plot1 = pg.PlotItem(title="channel1") self.imv1 = pg.ImageView(view = plot1) self.imv1.ui.histogram.hide() self.imv1.ui.roiBtn.hide() self.imv1.ui.menuBtn.hide() self.imv1.show() self.settings['captured_cells'] = 0 def run(self): eff_subarrayh = self.eff_subarrayh eff_subarrayv = self.eff_subarrayv RANGE = 20 # COSTANTE PROVVISORIA test_counter = 0 try: self.camera.read_from_hardware() freq1 = self.settings['acq_freq'] self.start_triggered_Acquisition(freq1) first_cycle = True # it will become False when the roi_h5_file will be created z_index = [] #list of z indexes, in which each element is the z index corresponding to a different channel old_z_index = [] roi_index = 0 num_rois = 0 active_rois = 0 self.roi_h5 = [] centroid_x_position = [] centroid_y_position = [] number_of_channels = len(self.channels) self.image_h5 = [None]*number_of_channels # prepare list to contain the image data to be saved in the h5file while not self.interrupt_measurement_called: # "run till abort" mode [frames, _dims] = self.camera.hamamatsu.getFrames() start = time.time() # processing of each acquired image for frame_index, frame in enumerate(frames): # define the correct channel in use for cells detection channel_index = (self.camera.hamamatsu.buffer_index - self.camera.hamamatsu.backlog + frame_index + 1) % 2 self.np_data = frame.getData() self.im.image[channel_index] = np.reshape(self.np_data, (eff_subarrayv, eff_subarrayh)) # detect all the cells present in this frame (do it only on the selected channel and not both) if channel_index == self.settings.selected_channel.val: self.im.find_cell(self.settings.selected_channel.val) if self.settings['save_roi_h5']: test_counter += 1 print('test_counter', test_counter) if first_cycle: self.init_roi_h5() first_cycle = False rois, selected_cx, selected_cy = self.im.roi_creation(channel_index) num_rois = len(rois) print('num_rois', num_rois) contained_rois = 0 new_cell = 0 #number_of_delayed_cells = 0 while len(z_index) < num_rois*number_of_channels: z_index.append(0) while len(centroid_x_position) < num_rois: centroid_x_position.append(None) # or .append(0) ??? centroid_y_position.append(None) # or .append(0) ??? #z_index_lenght = len(z_index) # GIUSTO??? SERVE??? for roi_idx, roi in enumerate(rois): print('roi_idx', roi_idx) comp_index = 0 empty_check = 0 comp_check = False # Centroid comparison for comp_index in range(int((len(old_z_index))/number_of_channels)): # OPPURE #for comp_index in range(active_rois): if z_index[number_of_channels*comp_index+channel_index] == 0: empty_check = 1 break #if selected_cx[roi_idx] in range(self.roi_h5[number_of_channels*comp_index+channel_index].centroid_x-RANGE,self.roi_h5[number_of_channels*comp_index+channel_index].centroid_x+RANGE) and selected_cy[roi_idx] in range(self.roi_h5[number_of_channels*comp_index+channel_index].centroid_y-RANGE,self.roi_h5[number_of_channels*comp_index+channel_index].centroid_y+RANGE): if selected_cx[roi_idx] in range(centroid_x_position[comp_index]-RANGE, centroid_x_position[comp_index]+RANGE) and selected_cy[roi_idx] in range(centroid_y_position[comp_index]-RANGE, centroid_y_position[comp_index]+RANGE): comp_check = True break #comp_index += 1 # if comp_index == (int((len(old_z_index))/number_of_channels) - 1): # comp_index += 1 #breakpoint() if comp_check == False: if comp_index == (int((len(old_z_index))/number_of_channels) - 1) and empty_check == 0: new_cell += 1 #if len(old_z_index) != 0: contained_rois = new_cell + comp_index self.roi_h5_double_dataset(roi_index + contained_rois, roi_idx)#, comp_index) elif empty_check == 1: #contained_rois = roi_idx + number_of_delayed_cells pass else: #if len(self.roi_h5) != 0: #if len(old_z_index) != 0: print('z_index lenght', len(z_index)) print('old_z_index lenght', len(old_z_index)) print('comp_index', comp_index) #contained_rois = int((z_index_lenght - len(old_z_index))/number_of_channels - roi_idx + comp_index) # OPPURE #contained_rois = int((len(z_index) - len(old_z_index))/number_of_channels - roi_idx + comp_index) #contained_rois = int((z_index_lenght - len(old_z_index))/number_of_channels - roi_idx) #contained_rois = roi_idx + empty_check contained_rois = roi_idx print('contained_roi', contained_rois) #SE CREO SINGOLO DATASET PER VOLTA PROBLEMA QUANDO HO UNA SECONDA CELLULA MA STO GIA ANALIZZANDO IL SECONDO CANALE QUINDI z_index==old_z_index... # self.roi_h5_dataset(roi_index+contained_rois, channel_index, roi_idx)#, contained_rois) self.roi_h5_double_dataset(roi_index + contained_rois, roi_idx)#, comp_index) print('elements in roi_h5', len(self.roi_h5)) while len(z_index) < len(self.roi_h5): z_index.append(0) while len(centroid_x_position) < int((len(self.roi_h5))/number_of_channels): centroid_x_position.append(0) centroid_y_position.append(0) #breakpoint() if empty_check == 1: insert_position = number_of_channels*comp_index+channel_index else: insert_position = len(self.roi_h5) - number_of_channels + channel_index if z_index[insert_position] != 0: self.roi_h5[insert_position].resize(self.roi_h5[insert_position].shape[0]+1, axis = 0) # print(self.roi_h5) # print('z_index', z_index) # print(roi.shape) print('insert_position', insert_position) print('z_index', z_index) self.roi_h5[insert_position][z_index[insert_position], :, :] = roi self.h5_roi_file.flush() z_index[insert_position] += 1 centroid_x_position[int(insert_position/number_of_channels)] = selected_cx[roi_idx] centroid_y_position[int(insert_position/number_of_channels)] = selected_cy[roi_idx] print('ROI saved in a dataset, comp_check == False') else: # comp_check == False if z_index[number_of_channels*comp_index+channel_index] != 0: self.roi_h5[number_of_channels*comp_index+channel_index].resize(self.roi_h5[number_of_channels*comp_index+channel_index].shape[0]+1, axis = 0) self.roi_h5[number_of_channels*comp_index+channel_index][z_index[number_of_channels*comp_index+channel_index], :, :] = roi self.h5_roi_file.flush() z_index[number_of_channels*comp_index+channel_index] += 1 centroid_x_position[int((number_of_channels*comp_index+channel_index)/number_of_channels)] = selected_cx[roi_idx] centroid_y_position[int((number_of_channels*comp_index+channel_index)/number_of_channels)] = selected_cy[roi_idx] print('ROI saved in a dataset, comp_check == True') #contained_rois += 1 # z_index_lenght = len(z_index) # delayed_cells_mask = [0]*int((len(old_z_index))/number_of_channels) for position in range(int((len(old_z_index))/number_of_channels)): # FARLO SEMPRE OPPURE SOLO SE: # if num_rois < active_rois: # Past dimensions comparison to see if we have new cells. If not, delate these elements #check_pos = number_of_channels*(active_rois - position -1) check_pos = number_of_channels*(int((len(old_z_index))/number_of_channels) - position -1) if old_z_index[check_pos + channel_index] == z_index[check_pos + channel_index]: del z_index[check_pos] del z_index[check_pos] del self.roi_h5[check_pos] del self.roi_h5[check_pos] del centroid_x_position[int(check_pos/number_of_channels)] del centroid_y_position[int(check_pos/number_of_channels)] roi_index += 1 # ELIMINARE ANCHE ELEMENTI CORRISPONDENTI DI roi_h5? SUPPONGO DI SI PER ORA... #delayed_cells_mask [position] = 1 # flag_index = 0 # if delayed_cells_mask [flag_index] == 1: # while delayed_cells_mask [flag_index] == 1: # flag_index += 1 # flag_index += 1 # while delayed_cells_mask [flag_index] == 0: # flag_index += 1 # flag_index += 1 # while delayed_cells_mask [flag_index] == 1: # number_of_delayed_cells += 1 print('WE ARE HERE!!!') print('z_index HERE', z_index) print('old_z_index HERE', old_z_index) print('roi_index', roi_index, '\n\n\n') active_rois = num_rois old_z_index = list(z_index) self.settings['captured_cells'] = roi_index if self.settings['save_h5']: progress_index = 0 # temporarily stop the acquisition in order not to overwrite the camera buffer self.pause_triggered_Acquisition() print("\n \n ******* \n \n Saving :D !\n \n *******") [frames, _dims] = self.camera.hamamatsu.getLastTotFrames() # create and initialize h5file self.initH5() z_index_h5 = [0]*number_of_channels # list of indexes in which each element is the z index corresponding to a different channel buffer_index = self.camera.hamamatsu.buffer_index + 1 for aframe in frames: self.np_data = aframe.getData() image_on_the_run = np.reshape(self.np_data, (eff_subarrayv, eff_subarrayh)) ch_on_the_run = buffer_index%2 # 0 if the image is even, 1 if the image is odd, in the image stack self.image_h5[ch_on_the_run][z_index_h5[ch_on_the_run], :, :] = image_on_the_run # saving to the h5 dataset z_index_h5[ch_on_the_run] += 1 self.h5file.flush() self.settings['progress'] = progress_index*100./self.camera.hamamatsu.number_image_buffers progress_index += 1 buffer_index += 1 self.h5file.close() # finally save and close the h5 file created self.settings['save_h5'] = False # update the value to False, so the save_h5 can be called again self.settings['progress'] = 0 # restart the acquisition self.restart_triggered_Acquisition(freq1) end = time.time() print('Time spent to process ' + str(len(frames)) + ' frames: ' + str((end-start)) + 'seconds') finally: self.stop_triggered_Acquisition() # close all the h5 file still open if self.settings['save_h5']: self.h5file.close() self.settings['save_h5'] = False if self.settings['save_roi_h5']: self.h5_roi_file.close() self.settings['save_roi_h5'] = False def post_run(self): ''' Close all the figures after the run ended ''' self.imv0.close() self.imv1.close() def update_display(self): """ Displays the numpy array called displayed_image for each channel. This function runs repeatedly and automatically during the measurement run. Its update frequency is defined by self.display_update_period. """ for ch in self.channels: # choose the setting keys according to the channel to update autorange_key = f'auto_range_{ch}' autolevel_key = f'auto_levels_{ch}' level_min_key = f'level_min_{ch}' level_max_key = f'level_max_{ch}' if self.settings[autolevel_key]: # if autolevel is ON, normalize the image to its max and min level_min = np.amin(self.im.image[ch]) level_max = np.amax(self.im.image[ch]) self.settings[level_min_key] = level_min self.settings[level_max_key] = level_max else: # if autolevel is OFF, normalize the image to the choosen values level_min = self.settings[level_min_key] level_max = self.settings[level_max_key] # note that these levels are uint16, but the visulaized image is uint8, for compatibility with opencv processing (contours and rectangles annotations) # thresolding is required if autolevel is OFF; it could be avoided if autolevel is ON img_thres = np.clip(self.im.image[ch], level_min, level_max) # conversion to 8bit is done here for compatibility with opencv image8bit_normalized = ((img_thres-level_min+1)/(level_max-level_min+1)*255).astype('uint8') # creation of the image with open cv annotations, ready to be displayed displayed_image = self.im.draw_contours_on_image(image8bit_normalized) # display the image with a frame around the figure corresponding to the channel selected to do the find_cell operation (selected_channel) if ch == self.settings.selected_channel.val: #cv2.rectangle(displayed_image,(0,0),(self.eff_subarrayh-1,self.eff_subarrayv-1),(255,255,0),3) self.im.highlight_channel(displayed_image) imv_key = f'imv{ch}' imv = getattr(self, imv_key) imv.setImage(displayed_image, autoLevels=False, autoRange=self.settings[autorange_key], levels=(0,255)) def updateIndex(self, last_frame_index): """ Update the index of the image to fetch from buffer. If we reach the end of the buffer, we reset the index. """ last_frame_index += 1 if last_frame_index > self.camera.hamamatsu.number_image_buffers - 1: # if we reach the end of the buffer last_frame_index = 0 # reset return last_frame_index def start_laser(self, laserHW): ''' Laser is prepared for digital modulation at the power specified. Laser is turned OFF before''' if laserHW.laser_status.val == 'ON': self.stop_laser(laserHW) if laserHW.connected.val: # do this only if the laser was connected by the user! if laserHW.operating_mode.val != 'DIGITAL': laserHW.operating_mode.val = 'DIGITAL' laserHW.operating_mode.write_to_hardware() laserHW.laser_status.val = 'ON' laserHW.laser_status.write_to_hardware() laserHW.read_from_hardware() def stop_laser(self, laserHW): ''' Laser is turned off ''' if laserHW.connected.val: # do this only if the laser was connected by the user! laserHW.laser_status.val = 'OFF' laserHW.laser_status.write_to_hardware() def start_digital_rising_edge(self, digitalHW): '''The digital output of the DAQ start a rising edge procedure at the channel set by the user ''' digitalHW.value.val=0 digitalHW.write_value() digitalHW.value.val=1 digitalHW.write_value() # Trigger digitalHW.read_from_hardware() def start_triggered_counter_task(self, counterHW, initial_delay, freq, duty_cycle): ''' DAQ Counter set to be at 5V and 0V with a frequency of half the exposure time of the camera (1 frame OFF and 1 frame ON). It will be triggered by the digital output of the DAQ at the channel set by the user (please check that is equal to the one used for the ni_do_HW) ''' if counterHW.connected.val: # do this only if the counter was connected by the user! counterHW.freq.val=freq counterHW.freq.write_to_hardware() # if exposure time is less 1/fps then the laser should not be ON # for all the period but only when the camera is exposing pixels #=================================================================== # if self.camera.exposure_time.val < 1/self.camera.internal_frame_rate.val: # counterHW.duty_cycle.val=self.camera.exposure_time.val*counterHW.freq.val # counterHW.duty_cycle.write_to_hardware() #=================================================================== counterHW.duty_cycle.val=duty_cycle counterHW.duty_cycle.write_to_hardware() counterHW.trigger.val=True counterHW.trigger.write_to_hardware() counterHW.initial_delay.val=initial_delay # probably in start trigger mode there is no delay so counter 1 should have an initial delay = 0 #counterHW.initial_delay.val=0.00003896 #counterHW.initial_delay.val=0.0000877 # initial delay to be synchronized with the camera( check camera documentation at start trigger mode...) counterHW.initial_delay.write_to_hardware() counterHW.start() counterHW.read_from_hardware() def stop_counter_task(self, counterHW): ''' Stop counter output task ''' if counterHW.connected.val: # do this only if the counter was connected by the user! counterHW.stop() def start_triggered_Acquisition(self,freq1): ''' The camera is operated with external start trigger mode and will be triggered by the digital output of the DAQ ''' self.camera.settings.trigger_source.update_value(new_val = 'external') self.camera.settings.acquisition_mode.update_value(new_val = 'run_till_abort') self.camera.trmode.val='normal' self.camera.trmode.write_to_hardware() self.camera.tractive.val='syncreadout' self.camera.tractive.write_to_hardware() self.camera.read_from_hardware() self.camera.hamamatsu.startAcquisition() self.start_laser(self.laser_0) self.start_laser(self.laser_1) # dutycycle for advanced laser switch hardware t_readout = self.camera.hamamatsu.getPropertyValue("internal_line_interval")[0] camera_dutycycle = freq1*t_readout*self.camera.subarrayv.val # dutycycle for simple laser switch hardware camera_dutycycle=0.5 self.start_triggered_counter_task(self.ni_co_0, initial_delay=0.0000, freq=freq1, duty_cycle=camera_dutycycle) # counterOutput2 used to control 2 lasers via 1 signal and a duplication port with a buffer and a NOT. self.start_triggered_counter_task(self.ni_co_1, initial_delay=0.00003896, freq=freq1/2, duty_cycle=0.5) self.start_digital_rising_edge(self.ni_do_0) def pause_triggered_Acquisition(self): self.camera.hamamatsu.stopAcquisitionNotReleasing() self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) def restart_triggered_Acquisition(self, freq1): self.camera.hamamatsu.startAcquisitionWithoutAlloc() # dutycycle for advanced laser switch hardware t_readout = self.camera.hamamatsu.getPropertyValue("internal_line_interval")[0] camera_dutycycle = freq1*t_readout*self.camera.subarrayv.val # dutycycle for simple laser switch hardware camera_dutycycle=0.5 self.start_triggered_counter_task(self.ni_co_0, initial_delay=0.0000, freq=freq1, duty_cycle=camera_dutycycle) # counterOutput2 used to control 2 lasers via 1 signal and a duplication port with a buffer and a NOT. self.start_triggered_counter_task(self.ni_co_1, initial_delay=0.00003896, freq=freq1/2, duty_cycle=0.5) self.start_digital_rising_edge(self.ni_do_0) def stop_triggered_Acquisition(self): ''' Acquisition is terminated, laser and counters turned off ''' self.camera.hamamatsu.stopAcquisition() self.stop_laser(self.laser_0) self.stop_laser(self.laser_1) self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) self.camera.settings['trigger_source'] = 'internal' self.camera.trsource.write_to_hardware() self.camera.read_from_hardware() def create_saving_directory(self): if not os.path.isdir(self.app.settings['save_dir']): os.makedirs(self.app.settings['save_dir']) def initH5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}.h5' sample_name = '_'.join([timestamp, self.name, sample]) fname = os.path.join(self.app.settings['save_dir'], sample_name + '.h5') # file creation self.h5file = h5_io.h5_base_file(app=self.app, measurement=self, fname = fname) self.h5_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5file) img_size=self.im.image[0].shape # both image[0] and image[1] are valid, since they have the same shape number_of_channels = len(self.channels) # take as third dimension of the file the total number of images collected in the buffer if self.camera.hamamatsu.last_frame_number < self.camera.hamamatsu.number_image_buffers: length = int((self.camera.hamamatsu.last_frame_number+1)/number_of_channels) else: length=self.camera.hamamatsu.number_image_buffers/number_of_channels #divided by two since we have the two channels # dataset creation for ch_index in self.channels: name = f't0/c{ch_index}/image' self.image_h5[ch_index] = self.h5_group.create_dataset( name = name, shape = ( length, img_size[0], img_size[1]), dtype = self.im.image[0].dtype, chunks = (1, img_size[0], img_size[1]) ) self.image_h5[ch_index].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.image_h5[ch_index].attrs['acq_time'] = timestamp def init_roi_h5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}_ROI.h5' sample_name = '_'.join([timestamp, self.name, sample, 'ROI.h5']) fname = os.path.join(self.app.settings['save_dir'], sample_name) # file creation self.h5_roi_file = h5_io.h5_base_file(app=self.app, measurement=self, fname = fname) self.h5_roi_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5_roi_file) def roi_h5_dataset(self, t_index, c_index): """ Dataset creation function. It creates new datasets only when there are new cells detected by the algorithm """ roi_size = self.settings.roi_half_side.val*2 # roi dimension timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) if len(self.roi_h5) == 0: # creation of the first two datasets (one for each channel) for ch in self.channels: name = f't{0:04d}/c{ch}/roi' # data set name, initially with t index 0000 self.roi_h5.append(self.h5_roi_group.create_dataset( name = name, shape = (1, roi_size, roi_size), maxshape = ( None, roi_size, roi_size), dtype = np.uint16, chunks = (1, roi_size, roi_size) ) ) # dataset attributes self.roi_h5[ch].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.roi_h5[ch].attrs['acq_time'] = timestamp self.roi_h5[c_index].attrs['centroid_x'] = self.im.cx[0] # to be updated when multiple rois are saved self.roi_h5[c_index].attrs['centroid_y'] = self.im.cy[0] # DA CAMBIARE CON selected_cx... else: name = f't{t_index:04d}/c{c_index}/roi' # dataset name fullname = self.h5_roi_group.name + '/' + name if self.roi_h5[c_index].name != fullname: # create a new dataset only if the name does not exist yet self.roi_h5[c_index] = self.h5_roi_group.create_dataset( name = name, shape = (1, roi_size, roi_size), maxshape = ( None, roi_size, roi_size), dtype = np.uint16, chunks = (1, roi_size, roi_size) ) # dataset attributes self.roi_h5[c_index].attrs['element_size_um'] = [self.settings['zsampling'],self.settings['ysampling'],self.settings['xsampling']] self.roi_h5[c_index].attrs['acq_time'] = timestamp self.roi_h5[c_index].attrs['centroid_x'] = self.im.cx[0] # to be updated when multiple rois are saved self.roi_h5[c_index].attrs['centroid_y'] = self.im.cy[0] # DA CAMBIARE CON selected_cx... def roi_h5_double_dataset(self, t_index, roi_idx):#, comp_index):#, contained_rois): print('t_index passed to roi_h5_double_dataset: ', t_index) roi_size = self.settings.roi_half_side.val*2 for ch in range(len(self.channels)): name = 't' + str(t_index) + '/c' + str(ch) + '/roi' fullname = self.h5_roi_group.name +'/'+ name for name_check_position in range (int((len(self.roi_h5))/len(self.channels))): # FARLO SEMPRE OPPURE SOLO SE: #if len(self.roi_h5) > t_index + ch: if self.roi_h5[name_check_position*len(self.channels) + ch].name == fullname: #INDICE NON CORRETTO...CAMBIARE!!! print('return from the dataset function!!!') return # if self.roi_h5[ch + comp_index].name == fullname: #INDICE NON CORRETTO...CAMBIARE!!! # print('return from the dataset function!!!') # return self.roi_h5.append(self.h5_roi_group.create_dataset( name = name, shape = (1, roi_size, roi_size), maxshape = ( None, roi_size, roi_size), dtype = np.uint16, chunks = (1, roi_size, roi_size) ) ) last_position = len(self.roi_h5)-1 self.roi_h5[last_position].dims[0].label = "z" self.roi_h5[last_position].dims[1].label = "y" self.roi_h5[last_position].dims[2].label = "x" self.roi_h5[last_position].attrs['element_size_um'] = [self.settings['xsampling'],self.settings['ysampling'],self.settings['zsampling']] self.roi_h5[last_position].attrs['acq_time'] = time.time() self.roi_h5[last_position].attrs['centroid_x'] = self.im.cx[roi_idx] # to be updated when multiple rois are saved self.roi_h5[last_position].attrs['centroid_y'] = self.im.cy[roi_idx] # DA CAMBIARE CON selected_cx... print(name)
class PROCHIP_Single_Color_Measurement(Measurement): name = "PROCHIP" #PROCHIP_Measurement def setup(self): "..." self.ui_filename = sibling_path(__file__, "DualColor.ui") self.ui = load_qt_ui_file(self.ui_filename) # settings creation self.settings.New('refresh_period', dtype=float, unit='s', spinbox_decimals=4, initial=0.08, vmin=0, vmax=10) self.settings.New('auto_range_0', dtype=bool, initial=True) self.settings.New('auto_levels_0', dtype=bool, initial=True) self.settings.New('level_min_0', dtype=int, initial=60) self.settings.New('level_max_0', dtype=int, initial=150) #self.settings.New('auto_range_1', dtype=bool, initial=True) #self.settings.New('auto_levels_1', dtype=bool, initial=True) #self.settings.New('level_min_1', dtype=int, initial=60) #self.settings.New('level_max_1', dtype=int, initial=150) self.settings.New('save_h5', dtype=bool, initial=False) self.settings.New('save_roi_h5', dtype=bool, initial=False) self.settings.New('roi_half_side', dtype=int, initial=100) self.settings.New('min_cell_size', dtype=int, initial=1600) self.settings.New('selected_channel', dtype=int, initial=0, vmin=0, vmax=1) self.settings.New('captured_cells', dtype=int, initial=0) self.settings.New('acq_freq', dtype=float, unit='Hz', initial=200) self.settings.New('xsampling', dtype=float, unit='um', initial=0.11) self.settings.New('ysampling', dtype=float, unit='um', initial=0.11) self.settings.New('zsampling', dtype=float, unit='um', initial=1.0) self.camera = self.app.hardware['HamamatsuHardware'] self.display_update_period = self.settings.refresh_period.val self.channels = [0] def setup_figure(self): """ Runs once during App initialization, after setup() This is the place to make all graphical interface initializations, build plots, etc. """ # connect ui widgets to measurement/hardware settings or functions self.ui.start_pushButton.clicked.connect(self.start) self.ui.interrupt_pushButton.clicked.connect(self.interrupt) self.settings.save_h5.connect_to_widget(self.ui.save_h5_checkBox) self.settings.save_roi_h5.connect_to_widget( self.ui.save_ROI_h5_checkBox) self.settings.auto_levels_0.connect_to_widget( self.ui.autoLevels_checkBox0) self.settings.auto_range_0.connect_to_widget( self.ui.autoRange_checkBox0) self.settings.level_min_0.connect_to_widget(self.ui.min_doubleSpinBox0) self.settings.level_max_0.connect_to_widget(self.ui.max_doubleSpinBox0) self.settings.captured_cells.connect_to_widget( self.ui.captured_doubleSpinBox) def pre_run(self): ''' Initialization of the ImageManager class and of figures ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val / self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val / self.camera.binning.val) self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val) plot0 = pg.PlotItem(title="channel0") self.imv0 = pg.ImageView(view=plot0) self.imv0.ui.histogram.hide() self.imv0.ui.roiBtn.hide() self.imv0.ui.menuBtn.hide() self.imv0.show() self.settings['captured_cells'] = 0 def run(self): eff_subarrayh = self.eff_subarrayh eff_subarrayv = self.eff_subarrayv try: self.camera.read_from_hardware() freq1 = self.settings['acq_freq'] self.start_Acquisition(freq1) first_cycle = True # it will become False when the roi_h5_file will be created number_of_channels = len(self.channels) z_index_roi = [0] * len( self.channels ) # list of z indexes in which each element is the z index corresponding to a different channel roi_index = 0 # absolute index of rois num_rois = 0 # number of rois detected in the current image active_rois = 0 # number of rois detected in the previous image self.roi_h5 = [] # content to be saved in the roi_h5_file self.image_h5 = [None] * len( self.channels ) # prepare list to contain the image data to be saved in the h5file while not self.interrupt_measurement_called: # "run till abort" mode [frames, _dims] = self.camera.hamamatsu.getFrames() # processing of each acquired image for frame_index, frame in enumerate(frames): # define the correct channel in use for cells detection channel_index = 0 #(self.camera.hamamatsu.buffer_index - self.camera.hamamatsu.backlog + frame_index + 1) % 2 self.np_data = frame.getData() self.im.image[channel_index] = np.reshape( self.np_data, (eff_subarrayv, eff_subarrayh)) self.im.find_cell(self.settings.selected_channel.val) if self.settings['save_roi_h5']: # num_rois = len(self.im.contour) # create and initialize the roi h5 file if it does not exist yet if first_cycle: self.init_roi_h5() first_cycle = False # create a roi for each cell in the frame rois = self.im.roi_creation(channel_index) num_rois = len(rois) for i, roi in enumerate(rois): # create a new dataset if we are dealing with a new cell self.roi_h5_dataset(roi_index, channel_index) # dynamically increment the dimension of the "box" in which we are going to put a new roi image if z_index_roi[channel_index] != 0: self.roi_h5[channel_index].resize( self.roi_h5[channel_index].shape[0] + 1, axis=0) self.roi_h5[channel_index][ z_index_roi[channel_index], :, :] = roi self.h5_roi_file.flush( ) # this allow us to open the h5 file also while it is not completely created yet z_index_roi[channel_index] += 1 # one cell is passed and finished, so update indexes for a new cell # this is thought for a single cell, multi rois are not managed if num_rois < active_rois: roi_index += 1 z_index_roi = [0] * len(self.channels) active_rois = num_rois self.settings['captured_cells'] = roi_index if self.settings['save_h5']: progress_index = 0 # temporarily stop the acquisition in order not to overwrite the camera buffer self.pause_Acquisition() [frames, _dims] = self.camera.hamamatsu.getLastTotFrames() # create and initialize h5file self.initH5() z_index_h5 = [ 0 ] * number_of_channels # list of indexes in which each element is the z index corresponding to a different channel buffer_index = self.camera.hamamatsu.buffer_index + 1 for aframe in frames: self.np_data = aframe.getData() image_on_the_run = np.reshape( self.np_data, (eff_subarrayv, eff_subarrayh)) ch_on_the_run = 0 # 0 if the image is even, 1 if the image is odd, in the image stack self.image_h5[ch_on_the_run][z_index_h5[ ch_on_the_run], :, :] = image_on_the_run # saving to the h5 dataset z_index_h5[ch_on_the_run] += 1 self.h5file.flush() self.settings[ 'progress'] = progress_index * 100. / self.camera.hamamatsu.number_image_buffers progress_index += 1 buffer_index += 1 self.h5file.close( ) # finally save and close the h5 file created self.settings[ 'save_h5'] = False # update the value to False, so the save_h5 can be called again self.settings['progress'] = 0 print("\n \n ******* \n \n Stack saved :D !\n \n *******") # restart the acquisition self.restart_Acquisition(freq1) finally: self.stop_Acquisition() # close all the h5 file still open if self.settings['save_h5']: self.h5file.close() self.settings['save_h5'] = False if self.settings['save_roi_h5']: self.h5_roi_file.close() self.settings['save_roi_h5'] = False def post_run(self): ''' Close all the figures after the run ended ''' self.imv0.close() def update_display(self): """ Displays the numpy array called displayed_image for each channel. This function runs repeatedly and automatically during the measurement run. Its update frequency is defined by self.display_update_period. """ for ch in self.channels: # choose the setting keys according to the channel to update autorange_key = f'auto_range_{ch}' autolevel_key = f'auto_levels_{ch}' level_min_key = f'level_min_{ch}' level_max_key = f'level_max_{ch}' if self.settings[autolevel_key]: # if autolevel is ON, normalize the image to its max and min level_min = np.amin(self.im.image[ch]) level_max = np.amax(self.im.image[ch]) self.settings[level_min_key] = level_min self.settings[level_max_key] = level_max else: # if autolevel is OFF, normalize the image to the choosen values level_min = self.settings[level_min_key] level_max = self.settings[level_max_key] # note that these levels are uint16, but the visulaized image is uint8, for compatibility with opencv processing (contours and rectangles annotations) # thresolding is required if autolevel is OFF; it could be avoided if autolevel is ON img_thres = np.clip(self.im.image[ch], level_min, level_max) # conversion to 8bit is done here for compatibility with opencv image8bit_normalized = ((img_thres - level_min + 1) / (level_max - level_min + 1) * 255).astype('uint8') # creation of the image with open cv annotations, ready to be displayed displayed_image = self.im.draw_contours_on_image( image8bit_normalized) # display the image with a frame around the figure corresponding to the channel selected to do the find_cell operation (selected_channel) #if ch == self.settings.selected_channel.val: # self.im.highlight_channel(displayed_image) imv_key = f'imv{ch}' imv = getattr(self, imv_key) imv.setImage(displayed_image, autoLevels=False, autoRange=self.settings[autorange_key], levels=(0, 255)) def updateIndex(self, last_frame_index): """ Update the index of the image to fetch from buffer. If we reach the end of the buffer, we reset the index. """ last_frame_index += 1 if last_frame_index > self.camera.hamamatsu.number_image_buffers - 1: # if we reach the end of the buffer last_frame_index = 0 # reset return last_frame_index def start_Acquisition(self, freq1): ''' The camera is operated with external start trigger mode and will be triggered by the digital output of the DAQ ''' self.camera.settings.trigger_source.update_value(new_val='internal') self.camera.settings.acquisition_mode.update_value( new_val='run_till_abort') self.camera.trmode.val = 'normal' self.camera.trmode.write_to_hardware() # self.camera.tractive.val='syncreadout' # self.camera.tractive.write_to_hardware() self.camera.read_from_hardware() self.camera.hamamatsu.startAcquisition() #t_readout = self.camera.hamamatsu.getPropertyValue("internal_line_interval")[0] def pause_Acquisition(self): self.camera.hamamatsu.stopAcquisitionNotReleasing() def restart_Acquisition(self, freq1): self.camera.hamamatsu.startAcquisitionWithoutAlloc() def stop_Acquisition(self): self.camera.hamamatsu.stopAcquisition() def create_saving_directory(self): if not os.path.isdir(self.app.settings['save_dir']): os.makedirs(self.app.settings['save_dir']) def initH5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}.h5' if sample == '': sample_name = '_'.join([timestamp, self.name]) else: sample_name = '_'.join([timestamp, self.name, sample]) fname = os.path.join(self.app.settings['save_dir'], sample_name + '.h5') # file creation self.h5file = h5_io.h5_base_file(app=self.app, measurement=self, fname=fname) self.h5_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5file) img_size = self.im.image[ 0].shape # both image[0] and image[1] are valid, since they have the same shape number_of_channels = len(self.channels) # take as third dimension of the file the total number of images collected in the buffer if self.camera.hamamatsu.last_frame_number < self.camera.hamamatsu.number_image_buffers: length = int((self.camera.hamamatsu.last_frame_number + 1) / number_of_channels) else: length = self.camera.hamamatsu.number_image_buffers / number_of_channels #divided by two since we have the two channels # dataset creation for ch_index in self.channels: name = f't0/c{ch_index}/image' self.image_h5[ch_index] = self.h5_group.create_dataset( name=name, shape=(length, img_size[0], img_size[1]), dtype=self.im.image[0].dtype, chunks=(1, img_size[0], img_size[1])) self.image_h5[ch_index].attrs['element_size_um'] = [ self.settings['zsampling'], self.settings['ysampling'], self.settings['xsampling'] ] self.image_h5[ch_index].attrs['acq_time'] = timestamp def init_roi_h5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}_ROI.h5' if sample == '': sample_name = '_'.join([timestamp, self.name, 'ROI.h5']) else: sample_name = '_'.join([timestamp, self.name, sample, 'ROI.h5']) fname = os.path.join(self.app.settings['save_dir'], sample_name) # file creation self.h5_roi_file = h5_io.h5_base_file(app=self.app, measurement=self, fname=fname) self.h5_roi_group = h5_io.h5_create_measurement_group( measurement=self, h5group=self.h5_roi_file) def roi_h5_dataset(self, t_index, roi_index): """ Dataset creation """ roi_size = self.settings.roi_half_side.val * 2 number_of_channels = len(self.channels) # creation of one dataset for each channel for ch in range(number_of_channels): name = f't{t_index:04d}/c{ch}/roi' fullname = self.h5_roi_group.name + '/' + name #if len(self.roi_h5) > t_index + ch: for name_check_position in range( int(len(self.roi_h5) / number_of_channels) ): # can we do it only if len(self.roi_h5) > t_index + ch ??? MAYBE YES...TRY !!! if self.roi_h5[name_check_position * number_of_channels + ch].name == fullname: return self.roi_h5.append( self.h5_roi_group.create_dataset(name=name, shape=(1, roi_size, roi_size), maxshape=(None, roi_size, roi_size), dtype=np.uint16, chunks=(1, roi_size, roi_size))) last_position = len(self.roi_h5) - 1 # assign attributes self.roi_h5[last_position].dims[0].label = "z" self.roi_h5[last_position].dims[1].label = "y" self.roi_h5[last_position].dims[2].label = "x" self.roi_h5[last_position].attrs['element_size_um'] = [ self.settings['zsampling'], self.settings['ysampling'], self.settings['xsampling'] ] self.roi_h5[last_position].attrs['acq_time'] = time.time() self.roi_h5[last_position].attrs[ 'centroid_x'] = self.im.selected_cx[roi_index] self.roi_h5[last_position].attrs[ 'centroid_y'] = self.im.selected_cy[roi_index]
class PROCHIP_Measurement(Measurement): name = "PROCHIP" #PROCHIP_Measurement def setup(self): "..." self.ui_filename = sibling_path(__file__, "DualColor.ui") self.ui = load_qt_ui_file(self.ui_filename) # settings creation self.settings.New('refresh_period', dtype=float, unit='s', spinbox_decimals=4, initial=0.08, vmin=0, vmax=10) self.settings.New('auto_range_0', dtype=bool, initial=True) self.settings.New('auto_levels_0', dtype=bool, initial=True) self.settings.New('level_min_0', dtype=int, initial=60) self.settings.New('level_max_0', dtype=int, initial=150) self.settings.New('auto_range_1', dtype=bool, initial=True) self.settings.New('auto_levels_1', dtype=bool, initial=True) self.settings.New('level_min_1', dtype=int, initial=60) self.settings.New('level_max_1', dtype=int, initial=150) self.settings.New('save_h5', dtype=bool, initial=False) self.settings.New('save_roi_h5', dtype=bool, initial=False) self.settings.New('roi_half_side', dtype=int, initial=100) self.settings.New('min_cell_size', dtype=int, initial=1600) self.settings.New('selected_channel', dtype=int, initial=0, vmin=0, vmax=1) self.settings.New('captured_cells', dtype=int, initial=0) self.settings.New('acq_freq', dtype=float, unit='Hz', initial=200) self.settings.New('xsampling', dtype=float, unit='um', initial=0.11) self.settings.New('ysampling', dtype=float, unit='um', initial=0.11) self.settings.New('zsampling', dtype=float, unit='um', initial=1.0) self.camera = self.app.hardware['HamamatsuHardware'] self.display_update_period = self.settings.refresh_period.val self.laser_0 = self.app.hardware['Laser_0'] self.laser_1 = self.app.hardware['Laser_1'] self.ni_co_0 = self.app.hardware['Counter_Output_0'] self.ni_co_1 = self.app.hardware['Counter_Output_1'] self.ni_do_0 = self.app.hardware['Digital_Output_0'] self.channels = [0, 1] def setup_figure(self): """ Runs once during App initialization, after setup() This is the place to make all graphical interface initializations, build plots, etc. """ # connect ui widgets to measurement/hardware settings or functions self.ui.start_pushButton.clicked.connect(self.start) self.ui.interrupt_pushButton.clicked.connect(self.interrupt) self.settings.save_h5.connect_to_widget(self.ui.save_h5_checkBox) self.settings.save_roi_h5.connect_to_widget( self.ui.save_ROI_h5_checkBox) self.settings.auto_levels_0.connect_to_widget( self.ui.autoLevels_checkBox0) self.settings.auto_range_0.connect_to_widget( self.ui.autoRange_checkBox0) self.settings.level_min_0.connect_to_widget(self.ui.min_doubleSpinBox0) self.settings.level_max_0.connect_to_widget(self.ui.max_doubleSpinBox0) self.settings.auto_levels_1.connect_to_widget( self.ui.autoLevels_checkBox1) self.settings.auto_range_1.connect_to_widget( self.ui.autoRange_checkBox1) self.settings.level_min_1.connect_to_widget(self.ui.min_doubleSpinBox1) self.settings.level_max_1.connect_to_widget(self.ui.max_doubleSpinBox1) self.settings.selected_channel.connect_to_widget( self.ui.ch_doubleSpinBox) self.settings.captured_cells.connect_to_widget( self.ui.captured_doubleSpinBox) def pre_run(self): ''' Initialization of the ImageManager class and of figures ''' self.display_update_period = self.settings.refresh_period.val eff_subarrayh = self.eff_subarrayh = int(self.camera.subarrayh.val / self.camera.binning.val) eff_subarrayv = self.eff_subarrayv = int(self.camera.subarrayv.val / self.camera.binning.val) self.im = ImageManager(eff_subarrayh, eff_subarrayv, self.settings.roi_half_side.val, self.settings.min_cell_size.val) plot0 = pg.PlotItem(title="channel0") self.imv0 = pg.ImageView(view=plot0) self.imv0.ui.histogram.hide() self.imv0.ui.roiBtn.hide() self.imv0.ui.menuBtn.hide() self.imv0.show() plot1 = pg.PlotItem(title="channel1") self.imv1 = pg.ImageView(view=plot1) self.imv1.ui.histogram.hide() self.imv1.ui.roiBtn.hide() self.imv1.ui.menuBtn.hide() self.imv1.show() self.settings['captured_cells'] = 0 def run(self): eff_subarrayh = self.eff_subarrayh eff_subarrayv = self.eff_subarrayv RANGE = 20 # a cell must have a centroid position in the range of the centroid position of the cell at the previous frame +/- RANGE to be considered the same cell # in alternative, instead to be a constant, RANGE can become a dependent variable of the roi dimension (e.g.: RANGE = roi_half_side/5) try: self.camera.read_from_hardware() freq1 = self.settings['acq_freq'] self.start_triggered_Acquisition(freq1) first_cycle = True # it will become False when the roi_h5_file will be created z_index = [ ] # dynamic list of z indexes corresponding to the number of frames contained in each dataset for each channel old_z_index = [] # z_index at the previous cycle number_of_saved_roi = 0 # total number of saved cells num_rois = 0 # number of valid rois in the specific frame at that cycle active_rois = 0 # num_rois at the previous cycle self.roi_h5 = [ ] # dynamic list containing all the dataset under saving procedure centroid_x_position = [ ] # dynamic list containing the centroid positions in x of the cells under saving procedure centroid_y_position = [ ] # dynamic list containing the centroid positions in y of the cells under saving procedure number_of_channels = len(self.channels) self.image_h5 = [ None ] * number_of_channels # prepare list to contain the image data to be saved in the h5file while not self.interrupt_measurement_called: # "run till abort" mode [frames, _dims] = self.camera.hamamatsu.getFrames() # processing of each acquired image for frame_index, frame in enumerate(frames): # define the correct channel in use for cells detection channel_index = (self.camera.hamamatsu.buffer_index - self.camera.hamamatsu.backlog + frame_index + 1) % 2 self.np_data = frame.getData() self.im.image[channel_index] = np.reshape( self.np_data, (eff_subarrayv, eff_subarrayh)) # detect all the cells present in this frame (do it only on the selected channel and not both) if channel_index == self.settings.selected_channel.val: self.im.find_cell(self.settings.selected_channel.val) if self.settings['save_roi_h5']: # H5 file creation if first_cycle: self.init_roi_h5() first_cycle = False rois = self.im.roi_creation(channel_index) num_rois = len(rois) contained_rois = 0 # variable useful to allow the correct incrementation of the sample index in the dataset creation new_cell = 0 # number of new cells recognized in the current frame # creation of the needed elements in the lists while len(z_index) < num_rois * number_of_channels: z_index.append(0) while len(centroid_x_position) < num_rois: centroid_x_position.append(None) centroid_y_position.append(None) for roi_index, roi in enumerate(rois): comp_index = 0 # position of the dataset whose centroid position matches with the centroid position of the current cell empty_check = False # True when the first roi frame of the current channel must be put into a dataset comp_check = False # True if there is a match between centroid positions # centroid comparison for comp_index in range(active_rois): if z_index[number_of_channels * comp_index + channel_index] == 0: empty_check = True break if self.im.selected_cx[roi_index] in range( centroid_x_position[comp_index] - RANGE, centroid_x_position[comp_index] + RANGE ) and self.im.selected_cy[roi_index] in range( centroid_y_position[comp_index] - RANGE, centroid_y_position[comp_index] + RANGE): comp_check = True insert_position = number_of_channels * comp_index + channel_index break if comp_check == False: if comp_index == (active_rois - 1) and empty_check == False: new_cell += 1 contained_rois = new_cell + comp_index self.roi_h5_dataset( number_of_saved_roi + contained_rois, roi_index) elif empty_check == True: pass else: contained_rois = roi_index self.roi_h5_dataset( number_of_saved_roi + contained_rois, roi_index) number_of_dataset = len(self.roi_h5) # check again if there are all the needed elements in the lists, otherwise add them while len(z_index) < number_of_dataset: z_index.append(0) while len(centroid_x_position) < ( number_of_dataset / number_of_channels): centroid_x_position.append(None) centroid_y_position.append(None) if empty_check == True: insert_position = number_of_channels * comp_index + channel_index else: insert_position = number_of_dataset - number_of_channels + channel_index # add an empty element where put the next roi frame if z_index[insert_position] != 0: self.roi_h5[insert_position].resize( self.roi_h5[insert_position].shape[0] + 1, axis=0) self.roi_h5[insert_position][ z_index[insert_position], :, :] = roi self.h5_roi_file.flush() z_index[insert_position] += 1 centroid_x_position[int( insert_position / number_of_channels )] = self.im.selected_cx[roi_index] centroid_y_position[int( insert_position / number_of_channels )] = self.im.selected_cy[roi_index] # past dimensions comparison to see if we have new cells. If not, delate these elements for position in range( active_rois ): # can be done only if num_rois < active_rois ??? IT SEEMS NOT !!! check_pos = number_of_channels * (active_rois - position - 1) if old_z_index[check_pos + channel_index] == z_index[ check_pos + channel_index]: del z_index[check_pos] del z_index[check_pos] del self.roi_h5[check_pos] del self.roi_h5[check_pos] del centroid_x_position[int( check_pos / number_of_channels)] del centroid_y_position[int( check_pos / number_of_channels)] number_of_saved_roi += 1 # update useful parameters for the next cycle active_rois = num_rois old_z_index = list(z_index) self.settings['captured_cells'] = number_of_saved_roi if self.settings['save_h5']: progress_index = 0 # temporarily stop the acquisition in order not to overwrite the camera buffer self.pause_triggered_Acquisition() [frames, _dims] = self.camera.hamamatsu.getLastTotFrames() # create and initialize h5file self.initH5() z_index_h5 = [ 0 ] * number_of_channels # list of indexes in which each element is the z index corresponding to a different channel buffer_index = self.camera.hamamatsu.buffer_index + 1 for aframe in frames: self.np_data = aframe.getData() image_on_the_run = np.reshape( self.np_data, (eff_subarrayv, eff_subarrayh)) ch_on_the_run = buffer_index % 2 # 0 if the image is even, 1 if the image is odd, in the image stack self.image_h5[ch_on_the_run][z_index_h5[ ch_on_the_run], :, :] = image_on_the_run # saving to the h5 dataset z_index_h5[ch_on_the_run] += 1 self.h5file.flush() self.settings[ 'progress'] = progress_index * 100. / self.camera.hamamatsu.number_image_buffers progress_index += 1 buffer_index += 1 self.h5file.close( ) # finally save and close the h5 file created self.settings[ 'save_h5'] = False # update the value to False, so the save_h5 can be called again self.settings['progress'] = 0 print("\n \n ******* \n \n Stack saved :D !\n \n *******") # restart the acquisition self.restart_triggered_Acquisition(freq1) finally: self.stop_triggered_Acquisition() # close all the h5 file still open if self.settings['save_h5']: self.h5file.close() self.settings['save_h5'] = False if self.settings['save_roi_h5']: self.h5_roi_file.close() self.settings['save_roi_h5'] = False def post_run(self): ''' Close all the figures after the run ended ''' self.imv0.close() self.imv1.close() def update_display(self): """ Displays the numpy array called displayed_image for each channel. This function runs repeatedly and automatically during the measurement run. Its update frequency is defined by self.display_update_period. """ for ch in self.channels: # choose the setting keys according to the channel to update autorange_key = f'auto_range_{ch}' autolevel_key = f'auto_levels_{ch}' level_min_key = f'level_min_{ch}' level_max_key = f'level_max_{ch}' if self.settings[autolevel_key]: # if autolevel is ON, normalize the image to its max and min level_min = np.amin(self.im.image[ch]) level_max = np.amax(self.im.image[ch]) self.settings[level_min_key] = level_min self.settings[level_max_key] = level_max else: # if autolevel is OFF, normalize the image to the choosen values level_min = self.settings[level_min_key] level_max = self.settings[level_max_key] # note that these levels are uint16, but the visulaized image is uint8, for compatibility with opencv processing (contours and rectangles annotations) # thresolding is required if autolevel is OFF; it could be avoided if autolevel is ON img_thres = np.clip(self.im.image[ch], level_min, level_max) # conversion to 8bit is done here for compatibility with opencv image8bit_normalized = ((img_thres - level_min + 1) / (level_max - level_min + 1) * 255).astype('uint8') # creation of the image with open cv annotations, ready to be displayed displayed_image = self.im.draw_contours_on_image( image8bit_normalized) # display the image with a frame around the figure corresponding to the channel selected to do the find_cell operation (selected_channel) if ch == self.settings.selected_channel.val: #cv2.rectangle(displayed_image,(0,0),(self.eff_subarrayh-1,self.eff_subarrayv-1),(255,255,0),3) self.im.highlight_channel(displayed_image) imv_key = f'imv{ch}' imv = getattr(self, imv_key) imv.setImage(displayed_image, autoLevels=False, autoRange=self.settings[autorange_key], levels=(0, 255)) def updateIndex(self, last_frame_index): """ Update the index of the image to fetch from buffer. If we reach the end of the buffer, we reset the index. """ last_frame_index += 1 if last_frame_index > self.camera.hamamatsu.number_image_buffers - 1: # if we reach the end of the buffer last_frame_index = 0 # reset return last_frame_index def start_laser(self, laserHW): ''' Laser is prepared for digital modulation at the power specified. Laser is turned OFF before''' if laserHW.laser_status.val == 'ON': self.stop_laser(laserHW) if laserHW.connected.val: # do this only if the laser was connected by the user! if laserHW.operating_mode.val != 'DIGITAL': laserHW.operating_mode.val = 'DIGITAL' laserHW.operating_mode.write_to_hardware() laserHW.laser_status.val = 'ON' laserHW.laser_status.write_to_hardware() laserHW.read_from_hardware() def stop_laser(self, laserHW): ''' Laser is turned off ''' if laserHW.connected.val: # do this only if the laser was connected by the user! laserHW.laser_status.val = 'OFF' laserHW.laser_status.write_to_hardware() def start_digital_rising_edge(self, digitalHW): '''The digital output of the DAQ start a rising edge procedure at the channel set by the user ''' digitalHW.value.val = 0 digitalHW.write_value() digitalHW.value.val = 1 digitalHW.write_value() # Trigger digitalHW.read_from_hardware() def start_triggered_counter_task(self, counterHW, initial_delay, freq, duty_cycle): ''' DAQ Counter set to be at 5V and 0V with a frequency of half the exposure time of the camera (1 frame OFF and 1 frame ON). It will be triggered by the digital output of the DAQ at the channel set by the user (please check that is equal to the one used for the ni_do_HW) ''' if counterHW.connected.val: # do this only if the counter was connected by the user! counterHW.freq.val = freq counterHW.freq.write_to_hardware() # if exposure time is less 1/fps then the laser should not be ON # for all the period but only when the camera is exposing pixels #=================================================================== # if self.camera.exposure_time.val < 1/self.camera.internal_frame_rate.val: # counterHW.duty_cycle.val=self.camera.exposure_time.val*counterHW.freq.val # counterHW.duty_cycle.write_to_hardware() #=================================================================== counterHW.duty_cycle.val = duty_cycle counterHW.duty_cycle.write_to_hardware() counterHW.trigger.val = True counterHW.trigger.write_to_hardware() counterHW.initial_delay.val = initial_delay # probably in start trigger mode there is no delay so counter 1 should have an initial delay = 0 #counterHW.initial_delay.val=0.00003896 #counterHW.initial_delay.val=0.0000877 # initial delay to be synchronized with the camera( check camera documentation at start trigger mode...) counterHW.initial_delay.write_to_hardware() counterHW.start() counterHW.read_from_hardware() def stop_counter_task(self, counterHW): ''' Stop counter output task ''' if counterHW.connected.val: # do this only if the counter was connected by the user! counterHW.stop() def start_triggered_Acquisition(self, freq1): ''' The camera is operated with external start trigger mode and will be triggered by the digital output of the DAQ ''' self.camera.settings.trigger_source.update_value(new_val='external') self.camera.settings.acquisition_mode.update_value( new_val='run_till_abort') self.camera.trmode.val = 'normal' self.camera.trmode.write_to_hardware() self.camera.tractive.val = 'syncreadout' self.camera.tractive.write_to_hardware() self.camera.read_from_hardware() self.camera.hamamatsu.startAcquisition() self.start_laser(self.laser_0) self.start_laser(self.laser_1) # dutycycle for advanced laser switch hardware t_readout = self.camera.hamamatsu.getPropertyValue( "internal_line_interval")[0] camera_dutycycle = freq1 * t_readout * self.camera.subarrayv.val # dutycycle for simple laser switch hardware camera_dutycycle = 0.5 self.start_triggered_counter_task(self.ni_co_0, initial_delay=0.0000, freq=freq1, duty_cycle=camera_dutycycle) # counterOutput2 used to control 2 lasers via 1 signal and a duplication port with a buffer and a NOT. self.start_triggered_counter_task(self.ni_co_1, initial_delay=0.00003896, freq=freq1 / 2, duty_cycle=0.5) self.start_digital_rising_edge(self.ni_do_0) def pause_triggered_Acquisition(self): self.camera.hamamatsu.stopAcquisitionNotReleasing() self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) def restart_triggered_Acquisition(self, freq1): self.camera.hamamatsu.startAcquisitionWithoutAlloc() # dutycycle for advanced laser switch hardware t_readout = self.camera.hamamatsu.getPropertyValue( "internal_line_interval")[0] camera_dutycycle = freq1 * t_readout * self.camera.subarrayv.val # dutycycle for simple laser switch hardware camera_dutycycle = 0.5 self.start_triggered_counter_task(self.ni_co_0, initial_delay=0.0000, freq=freq1, duty_cycle=camera_dutycycle) # counterOutput2 used to control 2 lasers via 1 signal and a duplication port with a buffer and a NOT. self.start_triggered_counter_task(self.ni_co_1, initial_delay=0.00003896, freq=freq1 / 2, duty_cycle=0.5) self.start_digital_rising_edge(self.ni_do_0) def stop_triggered_Acquisition(self): ''' Acquisition is terminated, laser and counters turned off ''' self.camera.hamamatsu.stopAcquisition() self.stop_laser(self.laser_0) self.stop_laser(self.laser_1) self.stop_counter_task(self.ni_co_0) self.stop_counter_task(self.ni_co_1) self.camera.settings['trigger_source'] = 'internal' self.camera.trsource.write_to_hardware() self.camera.read_from_hardware() def create_saving_directory(self): if not os.path.isdir(self.app.settings['save_dir']): os.makedirs(self.app.settings['save_dir']) def initH5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}.h5' if sample == '': sample_name = '_'.join([timestamp, self.name]) else: sample_name = '_'.join([timestamp, self.name, sample]) fname = os.path.join(self.app.settings['save_dir'], sample_name + '.h5') # file creation self.h5file = h5_io.h5_base_file(app=self.app, measurement=self, fname=fname) self.h5_group = h5_io.h5_create_measurement_group(measurement=self, h5group=self.h5file) img_size = self.im.image[ 0].shape # both image[0] and image[1] are valid, since they have the same shape number_of_channels = len(self.channels) # take as third dimension of the file the total number of images collected in the buffer if self.camera.hamamatsu.last_frame_number < self.camera.hamamatsu.number_image_buffers: length = int((self.camera.hamamatsu.last_frame_number + 1) / number_of_channels) else: length = self.camera.hamamatsu.number_image_buffers / number_of_channels #divided by two since we have the two channels # dataset creation for ch_index in self.channels: name = f't0/c{ch_index}/image' self.image_h5[ch_index] = self.h5_group.create_dataset( name=name, shape=(length, img_size[0], img_size[1]), dtype=self.im.image[0].dtype, chunks=(1, img_size[0], img_size[1])) self.image_h5[ch_index].attrs['element_size_um'] = [ self.settings['zsampling'], self.settings['ysampling'], self.settings['xsampling'] ] self.image_h5[ch_index].attrs['acq_time'] = timestamp def init_roi_h5(self): """ Initialization operations for the h5 file """ self.create_saving_directory() # file name creation timestamp = time.strftime("%y%m%d_%H%M%S", time.localtime()) sample = self.app.settings['sample'] #sample_name = f'{timestamp}_{self.name}_{sample}_ROI.h5' if sample == '': sample_name = '_'.join([timestamp, self.name, 'ROI.h5']) else: sample_name = '_'.join([timestamp, self.name, sample, 'ROI.h5']) fname = os.path.join(self.app.settings['save_dir'], sample_name) # file creation self.h5_roi_file = h5_io.h5_base_file(app=self.app, measurement=self, fname=fname) self.h5_roi_group = h5_io.h5_create_measurement_group( measurement=self, h5group=self.h5_roi_file) def roi_h5_dataset(self, t_index, roi_index): """ Dataset creation """ roi_size = self.settings.roi_half_side.val * 2 number_of_channels = len(self.channels) # creation of one dataset for each channel for ch in range(number_of_channels): name = f't{t_index:04d}/c{ch}/roi' fullname = self.h5_roi_group.name + '/' + name #if len(self.roi_h5) > t_index + ch: for name_check_position in range( int(len(self.roi_h5) / number_of_channels) ): # can we do it only if len(self.roi_h5) > t_index + ch ??? MAYBE YES...TRY !!! if self.roi_h5[name_check_position * number_of_channels + ch].name == fullname: return self.roi_h5.append( self.h5_roi_group.create_dataset(name=name, shape=(1, roi_size, roi_size), maxshape=(None, roi_size, roi_size), dtype=np.uint16, chunks=(1, roi_size, roi_size))) last_position = len(self.roi_h5) - 1 # assign attributes self.roi_h5[last_position].dims[0].label = "z" self.roi_h5[last_position].dims[1].label = "y" self.roi_h5[last_position].dims[2].label = "x" self.roi_h5[last_position].attrs['element_size_um'] = [ self.settings['zsampling'], self.settings['ysampling'], self.settings['xsampling'] ] self.roi_h5[last_position].attrs['acq_time'] = time.time() self.roi_h5[last_position].attrs[ 'centroid_x'] = self.im.selected_cx[roi_index] self.roi_h5[last_position].attrs[ 'centroid_y'] = self.im.selected_cy[roi_index]