示例#1
0
    def start_aqcuisition(self):
        global_pos = np.array(
            ((-5000, -5000), (-5000, 5000), (5000, -5000), (5000, 5000)))
        global_pos_name = ['A', 'B', 'C', 'D']

        delta = 200
        local_pos = np.transpose(
            np.reshape(
                np.meshgrid(np.array((-delta, 0, delta)),
                            np.array((-delta, 0, delta))), (2, -1)))
        local_pos_name = [str(i) for i in range(9)]

        self.cam = CamActuator()
        self.cam.initializeCamera()

        # Offset variables are used to generate replicates for good statistics
        offset_y = offset_x = delta

        cnt = 0
        for i in range(global_pos.shape[0]):
            for j in range(local_pos.shape[0]):
                x = global_pos[i, 0] + local_pos[j, 0] + offset_x
                y = global_pos[i, 1] + local_pos[j, 1] + offset_y

                stage_movement_thread = StagemovementAbsoluteThread(x, y)
                stage_movement_thread.start()
                time.sleep(2)
                stage_movement_thread.quit()
                stage_movement_thread.wait()

                image = self.cam.SnapImage(0.04)
                filename = global_pos_name[i] + local_pos_name[j]

                self.save_image(filename, image)

                cnt += 1
                print(
                    str(cnt) + '/' +
                    str(len(local_pos_name) * len(global_pos_name)))

        self.cam.Exit()
    def run(self):
        #Make sure registration can only start when camera is connected
        try:
            self.cam = CamActuator()
            self.cam.initializeCamera()
        except:
            print(sys.exc_info())
            self.backend.ui_widget.normalOutputWritten(
                'Unable to connect Hamamatsu camera')
            return

        self.cam.setROI(0, 0, 2048, 2048)

        if self.device_1 == 'galvos':
            reference_coordinates = self.gather_reference_coordinates_galvos()
            self.dict_transformations['camera-galvos'] = findTransform(reference_coordinates[0], \
                                                                       reference_coordinates[1])
        elif self.device_1 == 'dmd':
            reference_coordinates = self.gather_reference_coordinates_dmd()
            for laser in self.laser_list:
                self.dict_transformations['camera-dmd-'+laser] = findTransform(reference_coordinates[0], \
                                                                               reference_coordinates[1])

        elif self.device_1 == 'stage':
            reference_coordinates = self.gather_reference_coordinates_stage()
            self.dict_transformations['camera-stage'] = findTransform(reference_coordinates[0], \
                                                                      reference_coordinates[1])

        self.cam.Exit()

        ## Save transformation to file
        with open('CoordinatesManager/Registration/transformation.txt',
                  'w') as json_file:

            dict_transformations_list_format = {}
            for key, value in self.dict_transformations.items():
                dict_transformations_list_format[key] = value.tolist()

            json.dump(dict_transformations_list_format, json_file)

        self.sig_finished_registration.emit(self.dict_transformations)
class DMDRegistator:
    def __init__(self, parent):
        self.DMD = DMDActuator.DMDActuator()
        self.cam = CamActuator()
        self.cam.initializeCamera()

    def registration(self, laser='640', points=6):
        x_coords = np.linspace(0, 1024, 5)[1:-1]
        y_coords = np.linspace(0, 768, 4)[1:-1]

        dmd_coordinates = np.vstack((x_coords, y_coords))

        camera_coordinates = np.zeros(dmd_coordinates.shape)

        cnt = 0
        for i in range(points):
            for j in range(y_coords.shape[0]):
                mask = create_registration_image(i, j)
                self.DMD.send_data_to_DMD(mask)
                self.DMD.start_projection()

                image = self.cam.SnapImage(0.4)
                camera_coordinates[cnt, :] = touchingCoordinateFinder(
                    image, method='curvefit')
                cnt += 1

                self.DMD.stop_projection()
                self.DMD.free_memory()

        self.DMD.disconnect_DMD()
        transformation = findTransformationCurvefit(camera_coordinates,
                                                    dmd_coordinates,
                                                    kx=3,
                                                    ky=2)
        return transformation

    def create_registration_image(x, y, sigma=75):
        array = np.zeros((1024, 768))
        array[skd.draw.rectangle((x - sigma, y - sigma), (x, y))] = 255
        array[skd.draw.rectangle((x + sigma, y + sigma), (x, y))] = 255
        return array
示例#4
0
class RegistrationThread(QThread):

    sig_finished_registration = pyqtSignal(dict)

    def __init__(self, parent, laser=None):
        QThread.__init__(self)
        self.flag_finished = [0, 0, 0]
        self.backend = parent
        self.dmd = self.backend.DMD

        if not isinstance(laser, list):
            self.laser_list = [laser]
        else:
            self.laser_list = laser

        self.dict_transformators = {}

        self.dict_transformations = {}
        self.dtype_ref_co = np.dtype([('camera', int, (3, 2)),
                                      ('dmd', int, (3, 2)),
                                      ('galvos', int, (3, 2)),
                                      ('stage', int, (3, 2))])
        self.reference_coordinates = {}

    def set_device_to_register(self, device_1, device_2='camera'):
        self.device_1 = device_1
        self.device_2 = device_2

    def run(self):
        #Make sure registration can only start when camera is connected
        try:
            self.cam = CamActuator()
            self.cam.initializeCamera()
        except:
            print(sys.exc_info())
            self.backend.ui_widget.normalOutputWritten(
                'Unable to connect Hamamatsu camera')
            return

        self.cam.setROI(0, 0, 2048, 2048)

        if self.device_1 == 'galvos':
            reference_coordinates = self.gather_reference_coordinates_galvos()
            self.dict_transformations['camera-galvos'] = findTransform(reference_coordinates[0], \
                                                                       reference_coordinates[1])
        elif self.device_1 == 'dmd':
            reference_coordinates = self.gather_reference_coordinates_dmd()
            for laser in self.laser_list:
                self.dict_transformations['camera-dmd-'+laser] = findTransform(reference_coordinates[0], \
                                                                               reference_coordinates[1])

        elif self.device_1 == 'stage':
            reference_coordinates = self.gather_reference_coordinates_stage()
            self.dict_transformations['camera-stage'] = findTransform(reference_coordinates[0], \
                                                                      reference_coordinates[1])

        self.cam.Exit()

        ## Save transformation to file
        with open('CoordinatesManager/Registration/transformation.txt',
                  'w') as json_file:

            dict_transformations_list_format = {}
            for key, value in self.dict_transformations.items():
                dict_transformations_list_format[key] = value.tolist()

            json.dump(dict_transformations_list_format, json_file)

        self.sig_finished_registration.emit(self.dict_transformations)

    def gather_reference_coordinates_stage(self):
        image = np.zeros((2048, 2048, 3))
        stage_coordinates = np.array([[-2800, 100], [-2500, 400],
                                      [-1900, -200]])

        self.backend.loadMask(mask=np.ones((768, 1024)))
        self.backend.startProjection()

        for idx, pos in enumerate(stage_coordinates):

            stage_movement_thread = StagemovementAbsoluteThread(pos[0], pos[1])
            stage_movement_thread.start()
            time.sleep(0.5)
            stage_movement_thread.quit()
            stage_movement_thread.wait()

            image[:, :, idx] = self.cam.SnapImage(0.04)

        camera_coordinates = find_subimage_location(image, save=True)

        self.backend.stopProjection()
        self.backend.freeMemory()

        return np.array([camera_coordinates, stage_coordinates])

    def gather_reference_coordinates_galvos(self):
        galvothread = DAQmission()
        readinchan = []

        camera_coordinates = np.zeros((3, 2))
        galvo_coordinates = np.array([[0, 3], [3, -3], [-3, -3]])

        for i in range(3):
            pos_x = galvo_coordinates[i, 0]
            pos_y = galvo_coordinates[i, 1]

            galvothread.sendSingleAnalog('galvosx', pos_x)
            galvothread.sendSingleAnalog('galvosy', pos_y)

            image = self.cam.SnapImage(0.04)

            camera_coordinates[i, :] = gaussian_fitting(image)

        del galvothread
        return np.array([camera_coordinates, galvo_coordinates])

    def gather_reference_coordinates_dmd(self):
        galvo_coordinates = np.zeros((3, 2))

        for laser in self.laser_list:
            self.flag_finished = [0, 0, 0]

            self.backend.ui_widget.sig_control_laser.emit(laser, 5)

            self.registration_single_laser(laser)

            self.backend.ui_widget.sig_control_laser.emit(laser, 0)

        return np.array(
            [self.camera_coordinates, self.dmd_coordinates, galvo_coordinates])

    def registration_single_laser(self, laser):
        date_time = datetime.datetime.now().timetuple()
        image_id = ''
        for i in range(5):
            image_id += str(date_time[i]) + '_'
        image_id += str(date_time[5]) + '_l' + laser

        self.camera_coordinates = np.zeros((3, 2))
        self.touchingCoordinateFinder = []

        for i in range(3):
            self.touchingCoordinateFinder.append(
                touchingCoordinateFinder_Thread(i, method='curvefit'))
            self.touchingCoordinateFinder[
                i].sig_finished_coordinatefinder.connect(
                    self.touchingCoordinateFinder_finished)

        for i in range(3):
            self.loadFileName = './CoordinatesManager/Registration_Images/TouchingSquares/registration_mask_' + str(
                i) + '.png'

            # Transpose because mask in file is rotated by 90 degrees.
            mask = np.transpose(plt.imread(self.loadFileName))

            self.backend.loadMask(mask)
            self.backend.startProjection()

            time.sleep(0.5)
            self.image = self.cam.SnapImage(0.0015)
            time.sleep(0.5)

            self.backend.stopProjection()
            self.backend.freeMemory()

            # Start touchingCoordinateFinder thread
            self.touchingCoordinateFinder[i].put_image(self.image)
            self.touchingCoordinateFinder[i].start()

        self.dmd_coordinates = self.read_dmd_coordinates_from_file()

        # Block till all touchingCoordinateFinder_Thread threads are finished
        while np.prod(self.flag_finished) == 0:
            time.sleep(0.1)

    def read_dmd_coordinates_from_file(self):
        file = open(
            './CoordinatesManager/Registration_Images/TouchingSquares/positions.txt',
            'r')

        self.dmd_coordinates = []
        for ln in file.readlines():
            self.dmd_coordinates.append(ln.strip().split(','))
        file.close()

        return np.asarray(self.dmd_coordinates).astype(int)

    def touchingCoordinateFinder_finished(self, sig):
        self.camera_coordinates[sig, :] = np.flip(
            self.touchingCoordinateFinder[sig].coordinates)
        self.flag_finished[sig] = 1
示例#5
0
 def __init__(self, parent):
     self.DMD = DMDActuator.DMDActuator()
     self.cam = CamActuator()
     self.cam.initializeCamera()
示例#6
0
class CameraThread(QThread):
    snapsignal = pyqtSignal(np.ndarray)
    livesignal = pyqtSignal(np.ndarray)
    
    def __init__(self, camerahandle=None):
        self.exposuretime = 0.02    # seconds
        self.GUIframerate = 25      # frames per second
        self.sleeptime = np.max([1/self.GUIframerate, self.exposuretime])
        self.frame = np.random.rand(2048, 2048)
        self.mutex = QMutex()
        
        # Camera attributes
        if camerahandle == None:
            self.camera = CamActuator()
            self.camera.initializeCamera()
        else:
            self.camera = camerahandle
        self.camera.hcam.setPropertyValue("exposure_time", self.exposuretime)
        
        # QThread attributes
        super().__init__()
        self.isrunning = False
        self.moveToThread(self)
        self.started.connect(self.live)
        
    def stop(self):
        self.isrunning = False
        self.quit()
        self.wait()
        
    def snap(self):
        time.sleep(self.sleeptime)
        snap = np.random.rand(2048, 2048)
        self.mutex.lock()
        snap = copy(self.frame)
        self.mutex.unlock()
        self.snapsignal.emit(snap)
        return snap
    
    @pyqtSlot()
    def live(self):
        print('camera thread started')
        
        self.camera.isLiving = True
        self.camera.hcam.acquisition_mode = "run_till_abort"
        
        # Wait a second for camera acquisition to start
        self.camera.hcam.startAcquisition()
        QThread.msleep(1000)
        
        # Emit and get frames from the camera at a rate of 1/sleeptime
        self.isrunning = True
        while self.isrunning:
            QThread.msleep(int(self.sleeptime *1000))
            try:
                [frames, dims] = self.camera.hcam.getFrames()
                self.mutex.lock()
                self.frame = np.resize(frames[-1].np_array, (dims[1], dims[0]))
                self.livesignal.emit(self.frame)
                self.mutex.unlock()
            except:
                pass
        
        self.camera.hcam.stopAcquisition()
        self.camera.isLiving = False
        self.camera.Exit()
        
        print('camera thread stopped')
示例#7
0
    def run(self):
        """
        # ==========================================================================================================================================================
        #                                                                       Initialization
        # ==========================================================================================================================================================
        """
        #%%
        """
        # =============================================================================
        #         connect the Objective motor
        # =============================================================================
        """
        print('----------------------Starting to connect the Objective motor-------------------------')
        self.pi_device_instance = PIMotor()
        print('Objective motor connected.')
        self.errornum = 0
        self.ObjCurrentPos = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)

        """
        # =============================================================================
        #         connect the Hmamatsu camera
        # =============================================================================
        """
        self._use_camera = False
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for waveform_and_cam_key in self.RoundQueueDict[key][1]:
                    if "CameraPackage_" in waveform_and_cam_key:
                        if len(self.RoundQueueDict[key][1][waveform_and_cam_key]) != 0:
                            self._use_camera = True
        
        if self._use_camera == True:
            print('Connecting camera...')
            self.HamamatsuCam = CamActuator()
            self.HamamatsuCam.initializeCamera()
        else:
            print('No camera involved.')
        
        """
        # =============================================================================
        #         connect the Insight X3
        # =============================================================================
        """        
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.Laserinstance = InsightX3('COM11')
            try:
                querygap = 1.1
                # self.Laserinstance.SetWatchdogTimer(0) # Disable the laser watchdog!
#                Status_watchdog_thread = threading.Thread(target = self.Status_watchdog, args=[querygap], daemon=True)
#                Status_watchdog_thread.start() 
                time.sleep(1)
                #-------------Initialize laser--------------
                self.watchdog_flag = False
                time.sleep(0.5)
        
                warmupstatus = 0
                while int(warmupstatus) != 100:
                    try:
                        warmupstatus = self.Laserinstance.QueryWarmupTime()
                        time.sleep(0.6)
                    except:
                        time.sleep(0.6)
                        
                # if int(warmupstatus) == 100:
                #     self.warmupstatus = True
                #     print('Laser fully warmed up.')
                #     if 'Laser state:Ready' in self.Status_list:
                #         self.Laserinstance.Turn_On_PumpLaser()
                #         self.laserRun = True
                #     elif 'Laser state:RUN' in self.Status_list:
                #         self.laserRun = True
                        
                self.watchdog_flag = True
                time.sleep(0.5)
            except:
                print('Laser not connected.')
                
            # If turn on the laser shutter in the beginning
            if 'Shutter_Open' in self.GeneralSettingDict['StartUpEvents']:
                time.sleep(0.5)
                while True:
                    try:
                        self.Laserinstance.Open_TunableBeamShutter()
                        break
                    except:
                        time.sleep(1)
                time.sleep(0.5)                
        #%%
        """
        # ==========================================================================================================================================================
        #                                                                       Execution
        # ==========================================================================================================================================================
        """
   
        GridSequence = 0
        TotalGridNumber = self.meshgridnumber**2
        ScanningMaxCoord = int(np.amax(self.RoundCoordsDict['CoordsPackage_1'])) # Get the largest coordinate
        for EachGrid in range(TotalGridNumber):
            """
            # =============================================================================
            #         For each small repeat unit in the scanning meshgrid
            # =============================================================================
            """
            ScanningGridOffset_Row = int(GridSequence % self.meshgridnumber) * (ScanningMaxCoord + self.GeneralSettingDict['Scanning step']) # Offset coordinate row value for each well.
            ScanningGridOffset_Col = int(GridSequence/self.meshgridnumber) * (ScanningMaxCoord + self.GeneralSettingDict['Scanning step']) # Offset coordinate colunm value for each well.
            GridSequence += 1
            time.sleep(0.5)
            
            for EachRound in range(int(len(self.RoundQueueDict)/2-1)): # EachRound is the round sequence number starting from 0, while the actual number used in dictionary is 1.
                print ('----------------------------------------------------------------------------')            
                print('Below is Grid {}, Round {}.'.format(EachGrid, EachRound+1)) # EachRound+1 is the corresponding round number when setting the dictionary starting from round 1.
                
                """
                # =============================================================================
                #         Unpack the settings for each round
                # =============================================================================
                """
                # Initialize variables
                CoordOrder = 0      # Counter for n th coordinates, for appending cell properties array.      
                CellPropertiesDict = {}
                ND_filter1_Pos = None
                ND_filter2_Pos = None
                EM_filter_Pos = None
                cp_end_index = -1
                self.IndexLookUpCellPropertiesDict = {} #look up dictionary for each cell properties
                
                #-------------Unpack the focus stack information.
                ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]
                self.ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
                self.ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
                
                #-------------Unpack infor for stage move.
                CoordsNum = int(len(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)])/2) #Each pos has 2 coords
                
                #-------------Unpack infor for filter event. In the list, the first one is for ND filter and the second one is for emission filter.
                FilterEventIndexList = [i for i,x in enumerate(self.RoundQueueDict['FilterEvents']) if 'Round_{}'.format(EachRound+1) in x]
                
                if len(FilterEventIndexList) > 0:
                    NDposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[0]]
                    NDnumber = NDposText[NDposText.index('ToPos_')+6:len(NDposText)]
                    
                    EMposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[1]]
                    EMprotein = EMposText[EMposText.index('ToPos_')+6:len(EMposText)]
                    
                    # "COM9" for filter 1 port, which has ND values from 0 to 3.
                    # "COM7" for filter 2 port, which has ND values from 0 to 0.5.
                    if NDnumber == '0':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 0
                    elif NDnumber == '1':
                        ND_filter1_Pos = 1
                        ND_filter2_Pos = 0
                    elif NDnumber == '2':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 0
                    elif NDnumber == '2.3':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 2
                    elif NDnumber == '2.5':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 3
                    elif NDnumber == '0.5':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 3        
                    elif NDnumber == '0.3':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 2
                    
                    if EMprotein == 'Arch':
                        EM_filter_Pos = 0
                    elif EMprotein == 'eGFP' or EMprotein == 'Citrine':
                        EM_filter_Pos = 1
                    
                #-------------Unpack infor for Insight X3. In the list, the first one is for shutter event and the second one is for wavelength event. 
                InsightX3EventIndexList = [i for i,x in enumerate(self.RoundQueueDict['InsightEvents']) if 'Round_{}'.format(EachRound+1) in x]
                
                """
                # =============================================================================
                #         Execute Insight event at the beginning of each round
                # =============================================================================
                """            
                if len(InsightX3EventIndexList) == 1:
                    print(InsightX3EventIndexList)
                    InsightText = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
                    if 'Shutter_Open' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        while True:
                            try:
                                self.Laserinstance.Open_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(0.5)
                        print('Laser shutter open.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
    
                    elif 'Shutter_Close' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        while True:
                            try:
                                self.Laserinstance.Close_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(0.5)
                        print('Laser shutter closed.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                    elif 'WavelengthTo' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText[InsightText.index('To_')+3:len(InsightText)])
                        print(TargetWavelen)
                        while True:
                            try:
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                elif len(InsightX3EventIndexList) == 2:
                    
                    InsightText_wl = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[1]]
                    InsightText_st = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
                    
                    if 'WavelengthTo' in InsightText_wl and 'Shutter_Open' in InsightText_st:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
                        while True:
                            try:
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        while True:
                            try:
                                self.Laserinstance.Open_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        print('Laser shutter open.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                    elif 'WavelengthTo' in InsightText_wl and 'Shutter_Close' in InsightText_st:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
                        while True:
                            try:                        
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        while True:
                            try:
                                self.Laserinstance.Close_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(1)
                        print('Laser shutter closed.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                    time.sleep(2)
                    
                """
                # =============================================================================
                #         Execute filter event at the beginning of each round
                # =============================================================================
                """            
                if ND_filter1_Pos != None and ND_filter2_Pos != None:
                    #Move filter 1
                    self.filter1 = ELL9Filter("COM9")
                    self.filter1.moveToPosition(ND_filter1_Pos)
                    time.sleep(1)
                    #Move filter 2
                    self.filter2 = ELL9Filter("COM7")
                    self.filter2.moveToPosition(ND_filter2_Pos)
                    time.sleep(1)
                if EM_filter_Pos != None:
                    self.filter3 = ELL9Filter("COM15")
                    self.filter3.moveToPosition(EM_filter_Pos)
                    time.sleep(1)
    
                    
                self.currentCoordsSeq = 0
                #%%
                for EachCoord in range(CoordsNum):

                    #-----------------------------------------------At each stage coordinate:--------------------------------------------

                    self.error_massage = None
                    
                    self.currentCoordsSeq += 1
                    
                    """
                    # =============================================================================
                    #         Stage movement
                    # =============================================================================
                    """
                    RowIndex = int(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord*2:EachCoord*2+2][0]) + ScanningGridOffset_Row
                    ColumnIndex = int(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord*2:EachCoord*2+2][1]) + ScanningGridOffset_Col
    
                    try:
                        self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                    except:
                        self.error_massage = 'Fail_MoveStage'
                        self.errornum += 1
                        print('Stage move failed! Error number: {}'.format(int(self.errornum)))
                    
                    print ('Round {}. Current index: {}.'.format(EachRound+1, [RowIndex,ColumnIndex]))

                    time.sleep(1)
                    
                    """
                    # =============================================================================
                    #         Get the z stack objective positions ready
                    # =============================================================================
                    """
                    #-------------------------------------------If focus correction applies----------------------------------------
                    if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
                        FocusPosArray = self.GeneralSettingDict['FocusCorrectionMatrixDict']['RoundPackage_{}_Grid_{}'.format(EachRound+1, EachGrid)]
    #                    print(FocusPosArray)
                        FocusPosArray = FocusPosArray.flatten('F')
                        FocusPos_fromCorrection = FocusPosArray[EachCoord]
                        print('Target focus pos: '.format(FocusPos_fromCorrection))
                    
                    # Without focus correction
                    if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) == 0:
                        ZStacklinspaceStart = self.ObjCurrentPos['1'] - (math.floor(self.ZStackNum/2)-1)*self.ZStackStep
                        ZStacklinspaceEnd = self.ObjCurrentPos['1'] + (self.ZStackNum - math.floor(self.ZStackNum/2))*self.ZStackStep
                    # With focus correction
                    elif len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
                        ZStacklinspaceStart = FocusPos_fromCorrection - (math.floor(self.ZStackNum/2)-1)*self.ZStackStep
                        ZStacklinspaceEnd = FocusPos_fromCorrection + (self.ZStackNum - math.floor(self.ZStackNum/2))*self.ZStackStep                    
                        
                    ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = self.ZStackNum)       
                    print(ZStackPosList)
                    
                    """
                    # =============================================================================
                    #         Execute waveform packages
                    # =============================================================================
                    """
                    self.WaveforpackageNum = int(len(self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]))
                    #Execute each individual waveform package
                    print('*******************************************Round {}. Current index: {}.**************************************************'.format(EachRound+1, [RowIndex,ColumnIndex]))
                    
                    #------------------Move to Z stack focus-------------------
                    for EachZStackPos in range(self.ZStackNum): 
                        print('--------------------------------------------Stack {}--------------------------------------------------'.format(EachZStackPos+1))
                        if self.ZStackNum > 1:
                            self.ZStackOrder = int(EachZStackPos +1) # Here the first one is 1, not starting from 0.
                            FocusPos = ZStackPosList[EachZStackPos]
                            print('Target focus pos: {}'.format(FocusPos))
    
                            pos = PIMotor.move(self.pi_device_instance.pidevice, FocusPos)
                            self.ObjCurrentPosInStack = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)
                            print("Current position: {:.4f}".format(self.ObjCurrentPosInStack['1']))
                            
                            time.sleep(0.3)
                        else:
                            self.ZStackOrder = 1
                        
                        #------------For waveforms in each coordinate----------
                        for EachWaveform in range(self.WaveforpackageNum):
                            # Extract information
                            WaveformPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]['WaveformPackage_{}'.format(EachWaveform+1)]
                            CameraPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][1]["CameraPackage_{}".format(EachWaveform+1)]                  
                            WaveformPackageGalvoInfor = self.RoundQueueDict['GalvoInforPackage_{}'.format(EachRound+1)]['GalvoInfor_{}'.format(EachWaveform+1)]

                            self.readinchan = WaveformPackageToBeExecute[3]
                            self.RoundWaveformIndex = [EachRound+1, EachWaveform+1] # first is current round number, second is current waveform package number.
                            self.CurrentPosIndex = [RowIndex, ColumnIndex]
                            
                            #----------------Camera operations-----------------
                            _camera_isUsed = False
                            if CameraPackageToBeExecute != {}: # if camera operations are configured
                                _camera_isUsed = True
                                CamSettigList = CameraPackageToBeExecute["Settings"]
                                self.HamamatsuCam.StartStreaming(BufferNumber = CameraPackageToBeExecute["Buffer_number"],
                                                                 trigger_source = CamSettigList[CamSettigList.index("trigger_source")+1],
                                                                 exposure_time = CamSettigList[CamSettigList.index("exposure_time")+1],
                                                                 trigger_active = CamSettigList[CamSettigList.index("trigger_active")+1])
                                # Make sure that the camera is prepared before waveform execution.
#                                while self.HamamatsuCam.isStreaming == False:
#                                    print('Waiting for camera...')
#                                    time.sleep(0.5)
                                time.sleep(1)
                            print('Now start waveforms')
                            #----------------Waveforms operations--------------
                            if WaveformPackageGalvoInfor != 'NoGalvo': # Unpack the information of galvo scanning.
                                self.readinchan = WaveformPackageGalvoInfor[0]
                                self.repeatnum = WaveformPackageGalvoInfor[1]
                                self.PMT_data_index_array = WaveformPackageGalvoInfor[2]
                                self.averagenum = WaveformPackageGalvoInfor[3]
                                self.lenSample_1 = WaveformPackageGalvoInfor[4]
                                self.ScanArrayXnum = WaveformPackageGalvoInfor[5]
        
                            if self.clock_source == 'Dev1 as clock source':
                                self.adcollector = execute_analog_readin_optional_digital_thread()
                                self.adcollector.set_waves(WaveformPackageToBeExecute[0], WaveformPackageToBeExecute[1], WaveformPackageToBeExecute[2], WaveformPackageToBeExecute[3]) #[0] = sampling rate, [1] = analogcontainer_array, [2] = digitalcontainer_array, [3] = readinchan
                                self.adcollector.collected_data.connect(self.ProcessData)
                                self.adcollector.run()
                                time.sleep(0.2)
                                #self.ai_dev_scaling_coeff = self.adcollector.get_ai_dev_scaling_coeff()
                            elif self.clock_source == 'Cam as clock source' :
                                self.adcollector = execute_analog_and_readin_digital_optional_camtrig_thread()
                                self.adcollector.set_waves(WaveformPackageToBeExecute[0], WaveformPackageToBeExecute[1], WaveformPackageToBeExecute[2], WaveformPackageToBeExecute[3])
                                self.adcollector.collected_data.connect(self.ProcessData)
                                self.adcollector.run()
                            
                            #------------------Camera saving-------------------
                            if _camera_isUsed == True:
                                self.HamamatsuCam.isSaving = True
                                tif_name = os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_Cam_'+'Zpos'+str(self.ZStackOrder)+'.tif')
                                self.HamamatsuCam.StopStreaming(saving_dir = tif_name)
                                # Make sure that the saving process is finished.
                                while self.HamamatsuCam.isSaving == True:
                                    print('Camera saving...')
                                    time.sleep(0.5)
                                time.sleep(1)                                
                                
                            
                        time.sleep(0.6) # Wait for receiving data to be done.
                    time.sleep(0.3)
                    print('*************************************************************************************************************************')
        #%%
        """
        # ==========================================================================================================================================================
        #                                                                       Finalizing
        # ==========================================================================================================================================================
        """                
        
        # Switch off laser
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.watchdog_flag = False
            time.sleep(0.5)
            
            while True:
                try:
                    self.Laserinstance.Close_TunableBeamShutter()
                    break
                except:
                    time.sleep(1)
            time.sleep(0.5)
            
            self.Laserinstance.SaveVariables()
            while True:
                try:                        
                    self.Laserinstance.Turn_Off_PumpLaser()
                    break
                except:
                    time.sleep(1)
                    
        # Disconnect camera
        if self._use_camera == True:
            self.HamamatsuCam.Exit()
        
        # Disconnect focus motor
        try:
            PIMotor.CloseMotorConnection(self.pi_device_instance.pidevice)
            print('Objective motor disconnected.')
        except:
            pass
示例#8
0
class ScanningExecutionThread(QThread):
    
    ScanningResult = pyqtSignal(np.ndarray, np.ndarray, object, object) #The signal for the measurement, we can connect to this signal
    
    def __init__(self, RoundQueueDict, RoundCoordsDict, GeneralSettingDict, *args, **kwargs):        
        super().__init__(*args, **kwargs)
        self.RoundQueueDict = RoundQueueDict
        self.RoundCoordsDict = RoundCoordsDict
        self.GeneralSettingDict = GeneralSettingDict
        self.Status_list = None
        self.ludlStage = LudlStage("COM12")
        self.watchdog_flag = True
        
#        self.PMTimageDict = {}
#        for i in range(int(len(self.RoundQueueDict)/2-1)): # initial the nested PMTimageDict dictionary. -2 because default keys for insight and filter.
#            self.PMTimageDict['RoundPackage_{}'.format(i+1)] = {}
        self.clock_source = 'Dev1 as clock source' # Should be set by GUI.
        
        self.scansavedirectory = self.GeneralSettingDict['savedirectory']
        self.meshgridnumber = int(self.GeneralSettingDict['Meshgrid'])
        
    def Try_until_Success(func):
        """ This is the decorator to try to execute the function until succeed.
        """
        def wrapper(*args, **kwargs):
            
            success = None
            failnumber = 0
            
            while success is None:
                if failnumber <8:
                    try:
                        returnValue = func(*args, **kwargs)
                        success = True

                    except:
    
                        failnumber += 1                    
                        print('Laser action failed, failnumber: {}'.format(failnumber))
                        time.sleep(0.2)
                else:
                    print('Fail for 8 times, give up - -')
                    success = False                    
                    
            return returnValue
        
        return wrapper
    
    def run(self):
        """
        # ==========================================================================================================================================================
        #                                                                       Initialization
        # ==========================================================================================================================================================
        """
        #%%
        """
        # =============================================================================
        #         connect the Objective motor
        # =============================================================================
        """
        print('----------------------Starting to connect the Objective motor-------------------------')
        self.pi_device_instance = PIMotor()
        print('Objective motor connected.')
        self.errornum = 0
        self.ObjCurrentPos = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)

        """
        # =============================================================================
        #         connect the Hmamatsu camera
        # =============================================================================
        """
        self._use_camera = False
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for waveform_and_cam_key in self.RoundQueueDict[key][1]:
                    if "CameraPackage_" in waveform_and_cam_key:
                        if len(self.RoundQueueDict[key][1][waveform_and_cam_key]) != 0:
                            self._use_camera = True
        
        if self._use_camera == True:
            print('Connecting camera...')
            self.HamamatsuCam = CamActuator()
            self.HamamatsuCam.initializeCamera()
        else:
            print('No camera involved.')
        
        """
        # =============================================================================
        #         connect the Insight X3
        # =============================================================================
        """        
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.Laserinstance = InsightX3('COM11')
            try:
                querygap = 1.1
                # self.Laserinstance.SetWatchdogTimer(0) # Disable the laser watchdog!
#                Status_watchdog_thread = threading.Thread(target = self.Status_watchdog, args=[querygap], daemon=True)
#                Status_watchdog_thread.start() 
                time.sleep(1)
                #-------------Initialize laser--------------
                self.watchdog_flag = False
                time.sleep(0.5)
        
                warmupstatus = 0
                while int(warmupstatus) != 100:
                    try:
                        warmupstatus = self.Laserinstance.QueryWarmupTime()
                        time.sleep(0.6)
                    except:
                        time.sleep(0.6)
                        
                # if int(warmupstatus) == 100:
                #     self.warmupstatus = True
                #     print('Laser fully warmed up.')
                #     if 'Laser state:Ready' in self.Status_list:
                #         self.Laserinstance.Turn_On_PumpLaser()
                #         self.laserRun = True
                #     elif 'Laser state:RUN' in self.Status_list:
                #         self.laserRun = True
                        
                self.watchdog_flag = True
                time.sleep(0.5)
            except:
                print('Laser not connected.')
                
            # If turn on the laser shutter in the beginning
            if 'Shutter_Open' in self.GeneralSettingDict['StartUpEvents']:
                time.sleep(0.5)
                while True:
                    try:
                        self.Laserinstance.Open_TunableBeamShutter()
                        break
                    except:
                        time.sleep(1)
                time.sleep(0.5)                
        #%%
        """
        # ==========================================================================================================================================================
        #                                                                       Execution
        # ==========================================================================================================================================================
        """
   
        GridSequence = 0
        TotalGridNumber = self.meshgridnumber**2
        ScanningMaxCoord = int(np.amax(self.RoundCoordsDict['CoordsPackage_1'])) # Get the largest coordinate
        for EachGrid in range(TotalGridNumber):
            """
            # =============================================================================
            #         For each small repeat unit in the scanning meshgrid
            # =============================================================================
            """
            ScanningGridOffset_Row = int(GridSequence % self.meshgridnumber) * (ScanningMaxCoord + self.GeneralSettingDict['Scanning step']) # Offset coordinate row value for each well.
            ScanningGridOffset_Col = int(GridSequence/self.meshgridnumber) * (ScanningMaxCoord + self.GeneralSettingDict['Scanning step']) # Offset coordinate colunm value for each well.
            GridSequence += 1
            time.sleep(0.5)
            
            for EachRound in range(int(len(self.RoundQueueDict)/2-1)): # EachRound is the round sequence number starting from 0, while the actual number used in dictionary is 1.
                print ('----------------------------------------------------------------------------')            
                print('Below is Grid {}, Round {}.'.format(EachGrid, EachRound+1)) # EachRound+1 is the corresponding round number when setting the dictionary starting from round 1.
                
                """
                # =============================================================================
                #         Unpack the settings for each round
                # =============================================================================
                """
                # Initialize variables
                CoordOrder = 0      # Counter for n th coordinates, for appending cell properties array.      
                CellPropertiesDict = {}
                ND_filter1_Pos = None
                ND_filter2_Pos = None
                EM_filter_Pos = None
                cp_end_index = -1
                self.IndexLookUpCellPropertiesDict = {} #look up dictionary for each cell properties
                
                #-------------Unpack the focus stack information.
                ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]
                self.ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
                self.ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
                
                #-------------Unpack infor for stage move.
                CoordsNum = int(len(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)])/2) #Each pos has 2 coords
                
                #-------------Unpack infor for filter event. In the list, the first one is for ND filter and the second one is for emission filter.
                FilterEventIndexList = [i for i,x in enumerate(self.RoundQueueDict['FilterEvents']) if 'Round_{}'.format(EachRound+1) in x]
                
                if len(FilterEventIndexList) > 0:
                    NDposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[0]]
                    NDnumber = NDposText[NDposText.index('ToPos_')+6:len(NDposText)]
                    
                    EMposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[1]]
                    EMprotein = EMposText[EMposText.index('ToPos_')+6:len(EMposText)]
                    
                    # "COM9" for filter 1 port, which has ND values from 0 to 3.
                    # "COM7" for filter 2 port, which has ND values from 0 to 0.5.
                    if NDnumber == '0':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 0
                    elif NDnumber == '1':
                        ND_filter1_Pos = 1
                        ND_filter2_Pos = 0
                    elif NDnumber == '2':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 0
                    elif NDnumber == '2.3':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 2
                    elif NDnumber == '2.5':
                        ND_filter1_Pos = 2
                        ND_filter2_Pos = 3
                    elif NDnumber == '0.5':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 3        
                    elif NDnumber == '0.3':
                        ND_filter1_Pos = 0
                        ND_filter2_Pos = 2
                    
                    if EMprotein == 'Arch':
                        EM_filter_Pos = 0
                    elif EMprotein == 'eGFP' or EMprotein == 'Citrine':
                        EM_filter_Pos = 1
                    
                #-------------Unpack infor for Insight X3. In the list, the first one is for shutter event and the second one is for wavelength event. 
                InsightX3EventIndexList = [i for i,x in enumerate(self.RoundQueueDict['InsightEvents']) if 'Round_{}'.format(EachRound+1) in x]
                
                """
                # =============================================================================
                #         Execute Insight event at the beginning of each round
                # =============================================================================
                """            
                if len(InsightX3EventIndexList) == 1:
                    print(InsightX3EventIndexList)
                    InsightText = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
                    if 'Shutter_Open' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        while True:
                            try:
                                self.Laserinstance.Open_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(0.5)
                        print('Laser shutter open.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
    
                    elif 'Shutter_Close' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        while True:
                            try:
                                self.Laserinstance.Close_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(0.5)
                        print('Laser shutter closed.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                    elif 'WavelengthTo' in InsightText:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText[InsightText.index('To_')+3:len(InsightText)])
                        print(TargetWavelen)
                        while True:
                            try:
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                elif len(InsightX3EventIndexList) == 2:
                    
                    InsightText_wl = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[1]]
                    InsightText_st = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
                    
                    if 'WavelengthTo' in InsightText_wl and 'Shutter_Open' in InsightText_st:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
                        while True:
                            try:
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        while True:
                            try:
                                self.Laserinstance.Open_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        print('Laser shutter open.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                    elif 'WavelengthTo' in InsightText_wl and 'Shutter_Close' in InsightText_st:
                        self.watchdog_flag = False
                        time.sleep(0.5)
                        TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
                        while True:
                            try:                        
                                self.Laserinstance.SetWavelength(TargetWavelen)
                                break
                            except:
                                time.sleep(1)
                        time.sleep(5)
                        while True:
                            try:
                                self.Laserinstance.Close_TunableBeamShutter()
                                break
                            except:
                                time.sleep(1)
                        time.sleep(1)
                        print('Laser shutter closed.')
                        self.watchdog_flag = True
                        time.sleep(0.5)
                        
                    time.sleep(2)
                    
                """
                # =============================================================================
                #         Execute filter event at the beginning of each round
                # =============================================================================
                """            
                if ND_filter1_Pos != None and ND_filter2_Pos != None:
                    #Move filter 1
                    self.filter1 = ELL9Filter("COM9")
                    self.filter1.moveToPosition(ND_filter1_Pos)
                    time.sleep(1)
                    #Move filter 2
                    self.filter2 = ELL9Filter("COM7")
                    self.filter2.moveToPosition(ND_filter2_Pos)
                    time.sleep(1)
                if EM_filter_Pos != None:
                    self.filter3 = ELL9Filter("COM15")
                    self.filter3.moveToPosition(EM_filter_Pos)
                    time.sleep(1)
    
                    
                self.currentCoordsSeq = 0
                #%%
                for EachCoord in range(CoordsNum):

                    #-----------------------------------------------At each stage coordinate:--------------------------------------------

                    self.error_massage = None
                    
                    self.currentCoordsSeq += 1
                    
                    """
                    # =============================================================================
                    #         Stage movement
                    # =============================================================================
                    """
                    RowIndex = int(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord*2:EachCoord*2+2][0]) + ScanningGridOffset_Row
                    ColumnIndex = int(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord*2:EachCoord*2+2][1]) + ScanningGridOffset_Col
    
                    try:
                        self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                    except:
                        self.error_massage = 'Fail_MoveStage'
                        self.errornum += 1
                        print('Stage move failed! Error number: {}'.format(int(self.errornum)))
                    
                    print ('Round {}. Current index: {}.'.format(EachRound+1, [RowIndex,ColumnIndex]))

                    time.sleep(1)
                    
                    """
                    # =============================================================================
                    #         Get the z stack objective positions ready
                    # =============================================================================
                    """
                    #-------------------------------------------If focus correction applies----------------------------------------
                    if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
                        FocusPosArray = self.GeneralSettingDict['FocusCorrectionMatrixDict']['RoundPackage_{}_Grid_{}'.format(EachRound+1, EachGrid)]
    #                    print(FocusPosArray)
                        FocusPosArray = FocusPosArray.flatten('F')
                        FocusPos_fromCorrection = FocusPosArray[EachCoord]
                        print('Target focus pos: '.format(FocusPos_fromCorrection))
                    
                    # Without focus correction
                    if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) == 0:
                        ZStacklinspaceStart = self.ObjCurrentPos['1'] - (math.floor(self.ZStackNum/2)-1)*self.ZStackStep
                        ZStacklinspaceEnd = self.ObjCurrentPos['1'] + (self.ZStackNum - math.floor(self.ZStackNum/2))*self.ZStackStep
                    # With focus correction
                    elif len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
                        ZStacklinspaceStart = FocusPos_fromCorrection - (math.floor(self.ZStackNum/2)-1)*self.ZStackStep
                        ZStacklinspaceEnd = FocusPos_fromCorrection + (self.ZStackNum - math.floor(self.ZStackNum/2))*self.ZStackStep                    
                        
                    ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = self.ZStackNum)       
                    print(ZStackPosList)
                    
                    """
                    # =============================================================================
                    #         Execute waveform packages
                    # =============================================================================
                    """
                    self.WaveforpackageNum = int(len(self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]))
                    #Execute each individual waveform package
                    print('*******************************************Round {}. Current index: {}.**************************************************'.format(EachRound+1, [RowIndex,ColumnIndex]))
                    
                    #------------------Move to Z stack focus-------------------
                    for EachZStackPos in range(self.ZStackNum): 
                        print('--------------------------------------------Stack {}--------------------------------------------------'.format(EachZStackPos+1))
                        if self.ZStackNum > 1:
                            self.ZStackOrder = int(EachZStackPos +1) # Here the first one is 1, not starting from 0.
                            FocusPos = ZStackPosList[EachZStackPos]
                            print('Target focus pos: {}'.format(FocusPos))
    
                            pos = PIMotor.move(self.pi_device_instance.pidevice, FocusPos)
                            self.ObjCurrentPosInStack = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)
                            print("Current position: {:.4f}".format(self.ObjCurrentPosInStack['1']))
                            
                            time.sleep(0.3)
                        else:
                            self.ZStackOrder = 1
                        
                        #------------For waveforms in each coordinate----------
                        for EachWaveform in range(self.WaveforpackageNum):
                            # Extract information
                            WaveformPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]['WaveformPackage_{}'.format(EachWaveform+1)]
                            CameraPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][1]["CameraPackage_{}".format(EachWaveform+1)]                  
                            WaveformPackageGalvoInfor = self.RoundQueueDict['GalvoInforPackage_{}'.format(EachRound+1)]['GalvoInfor_{}'.format(EachWaveform+1)]

                            self.readinchan = WaveformPackageToBeExecute[3]
                            self.RoundWaveformIndex = [EachRound+1, EachWaveform+1] # first is current round number, second is current waveform package number.
                            self.CurrentPosIndex = [RowIndex, ColumnIndex]
                            
                            #----------------Camera operations-----------------
                            _camera_isUsed = False
                            if CameraPackageToBeExecute != {}: # if camera operations are configured
                                _camera_isUsed = True
                                CamSettigList = CameraPackageToBeExecute["Settings"]
                                self.HamamatsuCam.StartStreaming(BufferNumber = CameraPackageToBeExecute["Buffer_number"],
                                                                 trigger_source = CamSettigList[CamSettigList.index("trigger_source")+1],
                                                                 exposure_time = CamSettigList[CamSettigList.index("exposure_time")+1],
                                                                 trigger_active = CamSettigList[CamSettigList.index("trigger_active")+1])
                                # Make sure that the camera is prepared before waveform execution.
#                                while self.HamamatsuCam.isStreaming == False:
#                                    print('Waiting for camera...')
#                                    time.sleep(0.5)
                                time.sleep(1)
                            print('Now start waveforms')
                            #----------------Waveforms operations--------------
                            if WaveformPackageGalvoInfor != 'NoGalvo': # Unpack the information of galvo scanning.
                                self.readinchan = WaveformPackageGalvoInfor[0]
                                self.repeatnum = WaveformPackageGalvoInfor[1]
                                self.PMT_data_index_array = WaveformPackageGalvoInfor[2]
                                self.averagenum = WaveformPackageGalvoInfor[3]
                                self.lenSample_1 = WaveformPackageGalvoInfor[4]
                                self.ScanArrayXnum = WaveformPackageGalvoInfor[5]
        
                            if self.clock_source == 'Dev1 as clock source':
                                self.adcollector = execute_analog_readin_optional_digital_thread()
                                self.adcollector.set_waves(WaveformPackageToBeExecute[0], WaveformPackageToBeExecute[1], WaveformPackageToBeExecute[2], WaveformPackageToBeExecute[3]) #[0] = sampling rate, [1] = analogcontainer_array, [2] = digitalcontainer_array, [3] = readinchan
                                self.adcollector.collected_data.connect(self.ProcessData)
                                self.adcollector.run()
                                time.sleep(0.2)
                                #self.ai_dev_scaling_coeff = self.adcollector.get_ai_dev_scaling_coeff()
                            elif self.clock_source == 'Cam as clock source' :
                                self.adcollector = execute_analog_and_readin_digital_optional_camtrig_thread()
                                self.adcollector.set_waves(WaveformPackageToBeExecute[0], WaveformPackageToBeExecute[1], WaveformPackageToBeExecute[2], WaveformPackageToBeExecute[3])
                                self.adcollector.collected_data.connect(self.ProcessData)
                                self.adcollector.run()
                            
                            #------------------Camera saving-------------------
                            if _camera_isUsed == True:
                                self.HamamatsuCam.isSaving = True
                                tif_name = os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_Cam_'+'Zpos'+str(self.ZStackOrder)+'.tif')
                                self.HamamatsuCam.StopStreaming(saving_dir = tif_name)
                                # Make sure that the saving process is finished.
                                while self.HamamatsuCam.isSaving == True:
                                    print('Camera saving...')
                                    time.sleep(0.5)
                                time.sleep(1)                                
                                
                            
                        time.sleep(0.6) # Wait for receiving data to be done.
                    time.sleep(0.3)
                    print('*************************************************************************************************************************')
        #%%
        """
        # ==========================================================================================================================================================
        #                                                                       Finalizing
        # ==========================================================================================================================================================
        """                
        
        # Switch off laser
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.watchdog_flag = False
            time.sleep(0.5)
            
            while True:
                try:
                    self.Laserinstance.Close_TunableBeamShutter()
                    break
                except:
                    time.sleep(1)
            time.sleep(0.5)
            
            self.Laserinstance.SaveVariables()
            while True:
                try:                        
                    self.Laserinstance.Turn_Off_PumpLaser()
                    break
                except:
                    time.sleep(1)
                    
        # Disconnect camera
        if self._use_camera == True:
            self.HamamatsuCam.Exit()
        
        # Disconnect focus motor
        try:
            PIMotor.CloseMotorConnection(self.pi_device_instance.pidevice)
            print('Objective motor disconnected.')
        except:
            pass
        
    #%%
    #--------------------------------------------------------------Reconstruct and save images from 1D recorded array.--------------------------------------------------------------------------------       
    def ProcessData(self, data_waveformreceived):    
        print('ZStackOrder is:'+str(self.ZStackOrder)+'numis_'+str(self.ZStackNum))
        self.adcollector.save_as_binary(self.scansavedirectory)
        
        self.channel_number = len(data_waveformreceived)
        if self.channel_number == 1:            
            if 'Vp' in self.readinchan:
                pass
            elif 'Ip' in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:  # repeatnum, PMT_data_index_array, averagenum, ScanArrayXnum

                self.data_collected_0 = data_waveformreceived[0]*-1
                self.data_collected_0 = self.data_collected_0[0:len(self.data_collected_0)-1]
                print(len(self.data_collected_0))                
                for imageSequence in range(self.repeatnum):
                    
                    try:
                        self.PMT_image_reconstructed_array = self.data_collected_0[np.where(self.PMT_data_index_array == imageSequence+1)]
#                        if imageSequence == int(self.repeatnum)-1:
#                            self.PMT_image_reconstructed_array = self.PMT_image_reconstructed_array[0:len(self.PMT_image_reconstructed_array)-1] # Extra one sample at the end.
#                        print(self.PMT_image_reconstructed_array.shape)
                        Dataholder_average = np.mean(self.PMT_image_reconstructed_array.reshape(self.averagenum, -1), axis=0)
#                        print(Dataholder_average.shape)
                        Value_yPixels = int(self.lenSample_1/self.ScanArrayXnum)
                        self.PMT_image_reconstructed = np.reshape(Dataholder_average, (Value_yPixels, self.ScanArrayXnum))
                        
                        self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 50:550] # Crop size based on: M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Xin\2019-12-30 2p beads area test 4um
#                        self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 70:326] # for 256*256 images
                        #---------------------------------------------For multiple images in one z pos, Stack the arrays into a 3d array--------------------------------------------------------------------------
                        if imageSequence == 0:
                            self.PMT_image_reconstructed_stack = self.PMT_image_reconstructed[np.newaxis, :, :] # Turns into 3d array
                        else:
                            self.PMT_image_reconstructed_stack = np.concatenate((self.PMT_image_reconstructed_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)
                            print(self.PMT_image_reconstructed_stack.shape)
                        #---------------------------------------------Calculate the z max projection-----------------------------------------------------------------------
                        if self.repeatnum == 1: # Consider one repeat image situlation 
                            if self.ZStackNum > 1:
                                if self.ZStackOrder == 1:
                                    self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]

                                else:

                                    self.PMT_image_maxprojection_stack = np.concatenate((self.PMT_image_maxprojection_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)

                            else:
                                self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                        # Save the max projection image
                        if self.ZStackOrder == self.ZStackNum:
                            self.PMT_image_maxprojection = np.max(self.PMT_image_maxprojection_stack, axis=0)
                            
                            LocalimgZprojection = Image.fromarray(self.PMT_image_maxprojection) #generate an image object
                            LocalimgZprojection.save(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zmax'+'.tif')) #save as tif                            
#                            
                        Localimg = Image.fromarray(self.PMT_image_reconstructed) #generate an image object
                        Localimg.save(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zpos'+str(self.ZStackOrder)+'.tif')) #save as tif
                        
                        plt.figure()
                        plt.imshow(self.PMT_image_reconstructed, cmap = plt.cm.gray) # For reconstructed image we pull out the first layer, getting 2d img.
                        plt.show()
                    except:
                        print('No.{} image failed to generate.'.format(imageSequence))
                    
        elif self.channel_number == 2: 
            if 'PMT' not in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:

                self.data_collected_0 = data_waveformreceived[0]*-1
                self.data_collected_0 = self.data_collected_0[0:len(self.data_collected_0)-1]
                print(len(self.data_collected_0)) 
                for imageSequence in range(self.repeatnum):
                    try:
                        self.PMT_image_reconstructed_array = self.data_collected_0[np.where(self.PMT_data_index_array == imageSequence+1)]
                        if imageSequence == int(self.repeatnum)-1:
                            self.PMT_image_reconstructed_array = self.PMT_image_reconstructed_array[0:len(self.PMT_image_reconstructed_array)-1] # Extra one sample at the end.

                        Dataholder_average = np.mean(self.PMT_image_reconstructed_array.reshape(self.averagenum, -1), axis=0)
                        Value_yPixels = int(self.lenSample_1/self.ScanArrayXnum)
                        self.PMT_image_reconstructed = np.reshape(Dataholder_average, (Value_yPixels, self.ScanArrayXnum))
                        
                        self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 50:550]
                        
                        # Stack the arrays into a 3d array
                        if imageSequence == 0:
                            self.PMT_image_reconstructed_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                        else:
                            self.PMT_image_reconstructed_stack = np.concatenate((self.PMT_image_reconstructed_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)
                        
                        #---------------------------------------------Calculate the z max projection-----------------------------------------------------------------------
                        if self.repeatnum == 1: # Consider one repeat image situlation 
                            if self.ZStackNum > 1:
                                if self.ZStackOrder == 1:
                                    self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                                else:
                                    self.PMT_image_maxprojection_stack = np.concatenate((self.PMT_image_maxprojection_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)
                            else:
                                self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                        # Save the max projection image
                        if self.ZStackOrder == self.ZStackNum:
                            self.PMT_image_maxprojection = np.max(self.PMT_image_maxprojection_stack, axis=0)
                            
                            LocalimgZprojection = Image.fromarray(self.PMT_image_maxprojection) #generate an image object
                            LocalimgZprojection.save(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zmax'+'.tif')) #save as tif                            
                        
                        Localimg = Image.fromarray(self.PMT_image_reconstructed) #generate an image object
                        Localimg.save(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zpos'+str(self.ZStackOrder)+'.tif')) #save as tif
                        
                        plt.figure()
                        plt.imshow(self.PMT_image_reconstructed, cmap = plt.cm.gray)
                        plt.show()
                    except:
                        print('No.{} image failed to generate.'.format(imageSequence))

        print('ProcessData executed.')
        
    #----------------------------------------------------------------WatchDog for laser----------------------------------------------------------------------------------
    def Status_watchdog(self, querygap):
        
        while True:
            if self.watchdog_flag == True:
                self.Status_list = self.Laserinstance.QueryStatus()
                time.sleep(querygap)
            else:
                print('Watchdog stopped')
                time.sleep(querygap)
class StageWidget(QWidget):
    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.main_application = parent

        self.set_image_saving_location(
            os.getcwd() +
            '/CoordinatesManager/Registration_Images/StageRegistration/')

        self.init_gui()

        self.ludlStage = LudlStage("COM12")

    def init_gui(self):
        layout = QGridLayout()

        # self.setFixedSize(320,100)

        self.box = roundQGroupBox()
        self.box.setTitle("Stage")
        box_layout = QGridLayout()
        self.box.setLayout(box_layout)

        self.setLayout(layout)

        self.collect_data_button = QPushButton('Collect data')
        self.collect_data_button.clicked.connect(self.start_aqcuisition)

        box_layout.addWidget(self.collect_data_button)

        layout.addWidget(self.box)

    def set_image_saving_location(self, filepath):
        self.image_file_path = filepath

    def start_aqcuisition(self):
        global_pos = np.array(
            ((-5000, -5000), (-5000, 5000), (5000, -5000), (5000, 5000)))
        global_pos_name = ['A', 'B', 'C', 'D']

        delta = 200
        local_pos = np.transpose(
            np.reshape(
                np.meshgrid(np.array((-delta, 0, delta)),
                            np.array((-delta, 0, delta))), (2, -1)))
        local_pos_name = [str(i) for i in range(9)]

        self.cam = CamActuator()
        self.cam.initializeCamera()

        # Offset variables are used to generate replicates for good statistics
        offset_y = offset_x = delta

        cnt = 0
        for i in range(global_pos.shape[0]):
            for j in range(local_pos.shape[0]):
                x = global_pos[i, 0] + local_pos[j, 0] + offset_x
                y = global_pos[i, 1] + local_pos[j, 1] + offset_y

                self.ludlStage.moveAbs(x=x, y=y)
                # stage_movement_thread = StagemovementAbsoluteThread(x, y)
                # stage_movement_thread.start()
                # time.sleep(2)
                # stage_movement_thread.quit()
                # stage_movement_thread.wait()

                image = self.cam.SnapImage(0.04)
                filename = global_pos_name[i] + local_pos_name[j]

                self.save_image(filename, image)

                cnt += 1
                print(
                    str(cnt) + '/' +
                    str(len(local_pos_name) * len(global_pos_name)))

        self.cam.Exit()

    def save_image(self, filename, image):
        plt.imsave(self.image_file_path + filename + '.png', image)
示例#10
0
class MainGUI(QWidget):

    # signal_DMDmask is cictionary with laser specification as key and binary mask as content.
    signal_DMDmask = pyqtSignal(dict)
    signal_DMDcontour = pyqtSignal(list)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        os.chdir('./')  # Set directory to current folder.
        self.setFont(QFont("Arial"))

        #        self.setMinimumSize(900, 1020)
        self.setWindowTitle("Cell Selection")
        self.layout = QGridLayout(self)

        self.roi_list_freehandl_added = []
        self.selected_ML_Index = []
        self.selected_cells_infor_dict = {}

        self.mask_color_multiplier = [1, 1, 0]
        # =============================================================================
        #         Container for image display
        # =============================================================================
        graphContainer = StylishQT.roundQGroupBox()
        graphContainerLayout = QGridLayout()

        self.Imgviewtabs = QTabWidget()

        MLmaskviewBox = QWidget()
        MLmaskviewBoxLayout = QGridLayout()

        self.Matdisplay_Figure = Figure()
        self.Matdisplay_Canvas = FigureCanvas(self.Matdisplay_Figure)
        self.Matdisplay_Canvas.setFixedWidth(500)
        self.Matdisplay_Canvas.setFixedHeight(500)
        self.Matdisplay_Canvas.mpl_connect('button_press_event', self._onclick)

        self.Matdisplay_toolbar = NavigationToolbar(self.Matdisplay_Canvas,
                                                    self)

        MLmaskviewBoxLayout.addWidget(self.Matdisplay_toolbar, 0, 0)
        MLmaskviewBoxLayout.addWidget(self.Matdisplay_Canvas, 1, 0)

        MLmaskviewBox.setLayout(MLmaskviewBoxLayout)

        self.Imgviewtabs.addTab(MLmaskviewBox, "MaskRCNN")

        # =============================================================================
        #         Mask editing tab
        # =============================================================================
        MLmaskEditBox = QWidget()
        MLmaskEditBoxLayout = QGridLayout()

        self.Mask_edit_view = DrawingWidget(self)
        self.Mask_edit_view.enable_drawing(False)  # Disable drawing first
        #        self.Mask_edit_view = pg.ImageView()
        #        self.Mask_edit_view.getView().setLimits(xMin = 0, xMax = 2048, yMin = 0, yMax = 2048, minXRange = 2048, minYRange = 2048, maxXRange = 2048, maxYRange = 2048)
        self.Mask_edit_viewItem = self.Mask_edit_view.getImageItem()

        #        self.ROIitem = pg.PolyLineROI([[0,0], [80,0], [80,80], [0,80]], closed=True)
        self.Mask_edit_view_getView = self.Mask_edit_view.getView()
        #        self.Mask_edit_view_getView.addItem(self.ROIitem)

        self.Mask_edit_view.ui.roiBtn.hide()
        self.Mask_edit_view.ui.menuBtn.hide()
        self.Mask_edit_view.ui.normGroup.hide()
        self.Mask_edit_view.ui.roiPlot.hide()

        MLmaskEditBoxLayout.addWidget(self.Mask_edit_view, 0, 0)

        MLmaskEditBox.setLayout(MLmaskEditBoxLayout)

        self.Imgviewtabs.addTab(MLmaskEditBox, "Mask edit")

        graphContainerLayout.addWidget(self.Imgviewtabs, 0, 0)
        graphContainer.setLayout(graphContainerLayout)

        # =============================================================================
        #         Operation container
        # =============================================================================
        operationContainer = StylishQT.roundQGroupBox()
        operationContainerLayout = QGridLayout()

        self.init_ML_button = QPushButton('Initialize ML', self)
        operationContainerLayout.addWidget(self.init_ML_button, 0, 0)
        self.init_ML_button.clicked.connect(self.init_ML)

        #---------------------Load image from file-----------------------------
        self.textbox_loadimg = QLineEdit(self)
        operationContainerLayout.addWidget(self.textbox_loadimg, 1, 0)

        self.button_import_img_browse = QPushButton('Browse', self)
        operationContainerLayout.addWidget(self.button_import_img_browse, 1, 1)
        self.button_import_img_browse.clicked.connect(self.get_img_file_tif)

        self.run_ML_button = QPushButton('Analysis', self)
        operationContainerLayout.addWidget(self.run_ML_button, 2, 0)
        self.run_ML_button.clicked.connect(self.run_ML_onImg_and_display)

        self.generate_MLmask_button = QPushButton('Mask', self)
        operationContainerLayout.addWidget(self.generate_MLmask_button, 2, 1)
        self.generate_MLmask_button.clicked.connect(self.generate_MLmask)

        self.update_MLmask_button = QPushButton('Update mask', self)
        operationContainerLayout.addWidget(self.update_MLmask_button, 3, 0)
        self.update_MLmask_button.clicked.connect(self.update_mask)

        self.enable_modify_MLmask_button = QPushButton('Enable free-hand',
                                                       self)
        self.enable_modify_MLmask_button.setCheckable(True)
        operationContainerLayout.addWidget(self.enable_modify_MLmask_button, 4,
                                           0)
        self.enable_modify_MLmask_button.clicked.connect(self.enable_free_hand)

        #        self.modify_MLmask_button = QPushButton('Add patch', self)
        #        operationContainerLayout.addWidget(self.modify_MLmask_button, 4, 1)
        #        self.modify_MLmask_button.clicked.connect(self.addedROIitem_to_Mask)

        self.clear_roi_button = QPushButton('Clear ROIs', self)
        operationContainerLayout.addWidget(self.clear_roi_button, 5, 0)
        self.clear_roi_button.clicked.connect(self.clear_edit_roi)

        #        self.maskLaserComboBox = QComboBox()
        #        self.maskLaserComboBox.addItems(['640', '532', '488'])
        #        operationContainerLayout.addWidget(self.maskLaserComboBox, 6, 0)
        #
        #        self.generate_transformed_mask_button = QPushButton('Transform mask', self)
        #        operationContainerLayout.addWidget(self.generate_transformed_mask_button, 6, 1)
        #        self.generate_transformed_mask_button.clicked.connect(self.generate_transformed_mask)

        self.emit_transformed_mask_button = QPushButton('Emit mask', self)
        operationContainerLayout.addWidget(self.emit_transformed_mask_button,
                                           7, 1)
        self.emit_transformed_mask_button.clicked.connect(
            self.emit_mask_contour)

        operationContainer.setLayout(operationContainerLayout)

        # =============================================================================
        #         Mask para container
        # =============================================================================
        MaskparaContainer = StylishQT.roundQGroupBox()
        MaskparaContainerContainerLayout = QGridLayout()

        #----------------------------------------------------------------------
        self.fillContourButton = QCheckBox()
        self.invertMaskButton = QCheckBox()
        self.thicknessSpinBox = QSpinBox()
        self.thicknessSpinBox.setRange(1, 25)
        MaskparaContainerContainerLayout.addWidget(QLabel('Fill contour:'), 0,
                                                   0)
        MaskparaContainerContainerLayout.addWidget(self.fillContourButton, 0,
                                                   1)
        MaskparaContainerContainerLayout.addWidget(QLabel('Invert mask:'), 1,
                                                   0)
        MaskparaContainerContainerLayout.addWidget(self.invertMaskButton, 1, 1)
        MaskparaContainerContainerLayout.addWidget(QLabel('Thickness:'), 2, 0)
        MaskparaContainerContainerLayout.addWidget(self.thicknessSpinBox, 2, 1)

        MaskparaContainer.setLayout(MaskparaContainerContainerLayout)
        # =============================================================================
        #         Device operation container
        # =============================================================================
        deviceOperationContainer = StylishQT.roundQGroupBox()
        deviceOperationContainerLayout = QGridLayout()

        #----------------------------------------------------------------------
        self.CamExposureBox = QDoubleSpinBox(self)
        self.CamExposureBox.setDecimals(6)
        self.CamExposureBox.setMinimum(0)
        self.CamExposureBox.setMaximum(100)
        self.CamExposureBox.setValue(0.001501)
        self.CamExposureBox.setSingleStep(0.001)
        deviceOperationContainerLayout.addWidget(self.CamExposureBox, 0, 1)
        deviceOperationContainerLayout.addWidget(QLabel("Exposure time:"), 0,
                                                 0)

        cam_snap_button = QPushButton('Cam snap', self)
        deviceOperationContainerLayout.addWidget(cam_snap_button, 0, 2)
        cam_snap_button.clicked.connect(self.cam_snap)

        cam_snap_button = QPushButton('Cam snap', self)
        deviceOperationContainerLayout.addWidget(cam_snap_button, 0, 2)
        cam_snap_button.clicked.connect(self.cam_snap)

        deviceOperationContainer.setLayout(deviceOperationContainerLayout)

        self.layout.addWidget(graphContainer, 0, 0, 3, 1)
        self.layout.addWidget(operationContainer, 0, 1)
        self.layout.addWidget(MaskparaContainer, 1, 1)
        self.layout.addWidget(deviceOperationContainer, 2, 1)
        self.setLayout(self.layout)

    #%%
    # =============================================================================
    #     MaskRCNN detection part
    # =============================================================================
#    @run_in_thread

    def init_ML(self):
        # Initialize the detector instance and load the model.
        self.ProcessML = ProcessImageML()

    def get_img_file_tif(self):
        self.img_tif_filePath, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, 'Single File',
            'M:/tnw/ist/do/projects/Neurophotonics/Brinkslab/Data', "(*.tif)")
        self.textbox_loadimg.setText(self.img_tif_filePath)

        if self.img_tif_filePath != None:
            self.Rawimage = imread(self.img_tif_filePath)

            self.MLtargetedImg_raw = self.Rawimage.copy()

            self.MLtargetedImg = self.convert_for_MaskRCNN(
                self.MLtargetedImg_raw)

            self.show_raw_image(self.MLtargetedImg)

            self.addedROIitemMask = np.zeros(
                (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))
            self.MLmask = np.zeros(
                (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))

    def show_raw_image(self, image):
        # display a single image
        try:
            self.Matdisplay_Figure.clear()
        except:
            pass
        ax1 = self.Matdisplay_Figure.add_subplot(111)
        ax1.set_xticks([])
        ax1.set_yticks([])
        ax1.imshow(image)

        self.Matdisplay_Figure.tight_layout()
        self.Matdisplay_Canvas.draw()

        RGB_image = gray2rgb(image)
        self.Mask_edit_viewItem.setImage(RGB_image)

    def convert_for_MaskRCNN(self, input_img):
        """Convert the image size and bit-depth to make it suitable for MaskRCNN detection."""
        if input_img.shape[0] > 1024 or input_img.shape[1] > 1024:
            resized_img = resize(input_img, [1024, 1024],
                                 preserve_range=True).astype(input_img.dtype)

        minval = np.min(resized_img)
        maxval = np.max(resized_img)

        return ((resized_img - minval) / (maxval - minval) * 255).astype(
            np.uint8)

    def run_ML_onImg_and_display(self):
        """Run MaskRCNN on input image"""
        self.Matdisplay_Figure.clear()
        ax1 = self.Matdisplay_Figure.add_subplot(111)

        # Depends on show_mask or not, the returned figure will be input raw image with mask or not.
        self.MLresults, self.Matdisplay_Figure_axis, self.unmasked_fig = self.ProcessML.DetectionOnImage(
            self.MLtargetedImg, axis=ax1, show_mask=False, show_bbox=False)
        self.Mask = self.MLresults['masks']
        self.Label = self.MLresults['class_ids']
        self.Score = self.MLresults['scores']
        self.Bbox = self.MLresults['rois']

        self.SelectedCellIndex = 0
        self.NumCells = int(len(self.Label))
        self.selected_ML_Index = []
        self.selected_cells_infor_dict = {}

        self.Matdisplay_Figure_axis.imshow(self.unmasked_fig.astype(np.uint8))

        self.Matdisplay_Figure.tight_layout()
        self.Matdisplay_Canvas.draw()

    #%%
    # =============================================================================
    #     Configure click event to add clicked cell mask
    # =============================================================================

    def _onclick(self, event):
        """Highlights the cell selected in the figure by the user when clicked on"""
        if self.NumCells > 0:
            ShapeMask = np.shape(self.Mask)
            # get coorinates at selected location in image coordinates
            if event.xdata == None or event.ydata == None:
                return
            xcoor = min(max(int(event.xdata), 0), ShapeMask[1])
            ycoor = min(max(int(event.ydata), 0), ShapeMask[0])

            # search for the mask coresponding to the selected cell
            for EachCell in range(self.NumCells):
                if self.Mask[ycoor, xcoor, EachCell]:
                    self.SelectedCellIndex = EachCell
                    break

            # highlight selected cell
            if self.SelectedCellIndex not in self.selected_ML_Index:
                # Get the selected cell's contour coordinates and mask patch
                self.contour_verts, self.Cell_patch = self.get_cell_polygon(
                    self.Mask[:, :, self.SelectedCellIndex])

                self.Matdisplay_Figure_axis.add_patch(self.Cell_patch)
                self.Matdisplay_Canvas.draw()

                self.selected_ML_Index.append(self.SelectedCellIndex)
                self.selected_cells_infor_dict['cell{}_verts'.format(
                    str(self.SelectedCellIndex))] = self.contour_verts
            else:
                # If click on the same cell
                self.Cell_patch.remove()
                self.Matdisplay_Canvas.draw()
                self.selected_ML_Index.remove(self.SelectedCellIndex)
                self.selected_cells_infor_dict.pop('cell{}_verts'.format(
                    str(self.SelectedCellIndex)))

    def get_cell_polygon(self, mask):
        # Mask Polygon
        # Pad to ensure proper polygons for masks that touch image edges.
        padded_mask = np.zeros((mask.shape[0] + 2, mask.shape[1] + 2),
                               dtype=np.uint8)
        padded_mask[1:-1, 1:-1] = mask
        contours = find_contours(padded_mask, 0.5)
        for verts in contours:
            # Subtract the padding and flip (y, x) to (x, y)
            verts = np.fliplr(verts) - 1
            contour_polygon = mpatches.Polygon(
                verts, facecolor=self.random_colors(1)[0])

        return contours, contour_polygon

    def random_colors(self, N, bright=True):
        """
        Generate random colors.
        To get visually distinct colors, generate them in HSV space then
        convert to RGB.
        """
        brightness = 1.0 if bright else 0.7
        hsv = [(i / N, 1, brightness) for i in range(N)]
        colors = list(map(lambda c: colorsys.hsv_to_rgb(*c), hsv))
        random.shuffle(colors)
        return colors

    #%%
    # =============================================================================
    #     For mask generation
    # =============================================================================

    def generate_MLmask(self):
        """ Generate binary mask with all selected cells"""
        self.MLmask = np.zeros(
            (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))

        if len(self.selected_ML_Index) > 0:
            for selected_index in self.selected_ML_Index:
                self.MLmask = np.add(self.MLmask, self.Mask[:, :,
                                                            selected_index])

            self.intergrate_into_final_mask()

            self.add_rois_of_selected()

        else:
            self.intergrate_into_final_mask()
#            self.Mask_edit_viewItem.setImage(gray2rgb(self.MLtargetedImg))

    def add_rois_of_selected(self):
        """
        Using find_contours to get list of contour coordinates in the binary mask, and then generate polygon rois based on these coordinates.
        """

        for selected_index in self.selected_ML_Index:

            contours = self.selected_cells_infor_dict['cell{}_verts'.format(
                str(selected_index))]
            #            contours = find_contours(self.Mask[:,:,selected_index], 0.5) # Find iso-valued contours in a 2D array for a given level value.

            for n, contour in enumerate(contours):
                contour_coord_array = contours[n]
                #Swap columns
                contour_coord_array[:,
                                    0], contour_coord_array[:,
                                                            1] = contour_coord_array[:,
                                                                                     1], contour_coord_array[:, 0].copy(
                                                                                     )

                #Down sample the coordinates otherwise it will be too dense.
                contour_coord_array_del = np.delete(
                    contour_coord_array,
                    np.arange(2, contour_coord_array.shape[0] - 3, 2), 0)

                self.selected_cells_infor_dict['cell{}_ROIitem'.format(str(selected_index))] = \
                pg.PolyLineROI(positions=contour_coord_array_del, closed=True)

                self.Mask_edit_view.getView().addItem(
                    self.selected_cells_infor_dict['cell{}_ROIitem'.format(
                        str(selected_index))])

    def update_mask(self):
        """
        Regenerate the masks for MaskRCNN and free-hand added (in case they are changed), and show in imageview.
        
        !!!ISSUE: getLocalHandlePositions: moving handles changes the position read out, dragging roi as a whole doesn't.
        """

        # Binary mask from ML detection
        if len(self.selected_ML_Index) > 0:
            # Delete items in dictionary that are not roi items
            roi_dict = self.selected_cells_infor_dict.copy()
            del_key_list = []
            for key in roi_dict:
                print(key)
                if 'ROIitem' not in key:
                    del_key_list.append(key)
            for key in del_key_list:
                del roi_dict[key]

            self.MLmask = ProcessImage.ROIitem2Mask(
                roi_dict,
                mask_resolution=(self.MLtargetedImg.shape[0],
                                 self.MLtargetedImg.shape[1]))
        # Binary mask of added rois
        self.addedROIitemMask = ProcessImage.ROIitem2Mask(
            self.roi_list_freehandl_added,
            mask_resolution=(self.MLtargetedImg.shape[0],
                             self.MLtargetedImg.shape[1]))

        self.intergrate_into_final_mask()

#        if type(self.roi_list_freehandl_added) is list:
#            for ROIitem in self.roi_list_freehandl_added:
#
#                ROIitem.sigHoverEvent.connect(lambda: self.show_roi_detail(ROIitem))
#
#        plt.figure()
#        plt.imshow(self.addedROIitemMask)
#        plt.show()
# =============================================================================
#     For free-hand rois
# =============================================================================

    def enable_free_hand(self):
        if self.enable_modify_MLmask_button.isChecked():
            self.Mask_edit_view.enable_drawing(True)
        else:
            self.Mask_edit_view.enable_drawing(False)

    def add_freehand_roi(self, roi):
        # For drawwidget
        self.roi_list_freehandl_added.append(roi)

    def clear_edit_roi(self):
        """
        Clean up all the free-hand rois.
        """

        for roi in self.roi_list_freehandl_added:
            self.Mask_edit_view.getView().removeItem(roi)

        self.roi_list_freehandl_added = []

        if len(self.selected_cells_infor_dict) > 0:
            # Remove all selected masks
            for roiItemkey in self.selected_cells_infor_dict:
                if 'ROIitem' in roiItemkey:
                    self.Mask_edit_view.getView().removeItem(
                        self.selected_cells_infor_dict[roiItemkey])

        self.selected_cells_infor_dict = {}
        self.MLmask = np.zeros(
            (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))
        self.intergrate_into_final_mask()

    def intergrate_into_final_mask(self):
        # Binary mask of added rois
        self.addedROIitemMask = ProcessImage.ROIitem2Mask(
            self.roi_list_freehandl_added,
            mask_resolution=(self.MLtargetedImg.shape[0],
                             self.MLtargetedImg.shape[1]))
        #Display the RGB mask, ML mask plus free-hand added.
        self.Mask_edit_viewItem.setImage(gray2rgb(self.addedROIitemMask) * self.mask_color_multiplier + \
                                         gray2rgb(self.MLmask) * self.mask_color_multiplier + gray2rgb(self.MLtargetedImg))

        self.final_mask = self.MLmask + self.addedROIitemMask

        # In case the input image is 2048*2048, and it is resized to fit in MaskRCNN, need to convert back to original size for DMD tranformation.
        if self.final_mask.shape[0] != self.Rawimage.shape[
                0] or self.final_mask.shape[1] != self.Rawimage.shape[1]:
            self.final_mask = resize(
                self.final_mask,
                [self.Rawimage.shape[0], self.Rawimage.shape[1]],
                preserve_range=True).astype(self.final_mask.dtype)
#        self.final_mask = np.where(self.final_mask <= 1, self.final_mask, int(1))

        plt.figure()
        plt.imshow(self.final_mask)
        plt.show()

    # =============================================================================
    # For DMD transformation and mask generation
    # =============================================================================
    def generate_transformed_mask(self):
        self.read_transformations_from_file()
        #        self.transform_to_DMD_mask(laser = self.maskLaserComboBox.currentText(), dict_transformations = self.dict_transformations)
        target_laser = self.maskLaserComboBox.currentText()
        self.final_DMD_mask = self.finalmask_to_DMD_mask(
            laser=target_laser, dict_transformations=self.dict_transformations)

        plt.figure()
        plt.imshow(self.final_DMD_mask)
        plt.show()

    def emit_mask_contour(self):
        """Use find_contours to get a list of (n,2)-ndarrays consisting of n (row, column) coordinates along the contour,
           and then feed the list of signal:[list_of_rois, flag_fill_contour, contour_thickness, flag_invert_mode] to the 
           receive_mask_coordinates function in DMDWidget.
        """
        contours = find_contours(self.final_mask, 0.5)

        sig = [
            contours,
            self.fillContourButton.isChecked(),
            self.thicknessSpinBox.value(),
            self.invertMaskButton.isChecked()
        ]

        self.signal_DMDcontour.emit(sig)

    def emit_mask(self):
        target_laser = self.maskLaserComboBox.currentText()
        final_DMD_mask_dict = {}
        final_DMD_mask_dict['camera-dmd-' + target_laser] = self.final_DMD_mask

        self.signal_DMDmask.emit(final_DMD_mask_dict)

    def read_transformations_from_file(self):
        try:
            with open(
                    r'M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\People\Xin Meng\Code\Python_test\DMDManager\Registration\transformation.txt',
                    'r') as json_file:
                self.dict_transformations = json.load(json_file)
        except:
            print(
                'No transformation could be loaded from previous registration run.'
            )
            return

#    def transform_to_DMD_mask(self, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1, flag_invert_mode = False, mask_resolution = (1024, 768)):
#        """
#        Get roi vertices from all roi items and perform the transformation, and then create the mask for DMD.
#        """
#
#        #list of roi vertices each being (n,2) numpy array for added rois
#        if len(self.roi_list_freehandl_added) > 0:
#            self.addedROIitem_vertices = ProcessImage.ROIitem2Vertices(self.roi_list_freehandl_added)
#            #addedROIitem_vertices needs to be seperated to be inidividual (n,2) np.array
#                self.ROIitems_mask_transformed = ProcessImage.vertices_to_DMD_mask(self.addedROIitem_vertices, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1,\
#                                                                          flag_invert_mode = False, mask_resolution = (1024, 768))
#
#        #Dictionary with (n,2) numpy array for clicked cells
#        if len(self.selected_cells_infor_dict) > 0:
#            #Convert dictionary to np.array
#            for roiItemkey in self.selected_cells_infor_dict:
#                #Each one is 'contours' from find_contour
#                if '_verts' in roiItemkey:
#                    self.selected_cells_infor_dict[roiItemkey]
#
#            self.MLitems_mask_transformed = ProcessImage.vertices_to_DMD_mask(self.selected_cells_infor_dict, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1,\
#                                                                      flag_invert_mode = False, mask_resolution = (1024, 768))
#
#        if len(self.roi_list_freehandl_added) > 0:
#            self.final_DMD_mask = self.ROIitems_mask_transformed + self.MLitems_mask_transformed
#            self.final_DMD_mask[self.final_DMD_mask>1] = 1
#        else:
#            self.final_DMD_mask = self.MLitems_mask_transformed
#
#        return self.final_DMD_mask

    def finalmask_to_DMD_mask(self,
                              laser,
                              dict_transformations,
                              flag_fill_contour=True,
                              contour_thickness=1,
                              flag_invert_mode=False,
                              mask_resolution=(1024, 768)):
        """
        Same goal as transform_to_DMD_mask, with input being the final binary mask and using find_contour to get all vertices and perform transformation,
        and then coordinates to mask.
        """

        self.final_DMD_mask = ProcessImage.binarymask_to_DMD_mask(self.final_mask, laser, dict_transformations, flag_fill_contour = True, \
                                                                  contour_thickness = 1, flag_invert_mode = False, mask_resolution = (1024, 768))

        return self.final_DMD_mask

    def closeEvent(self, event):
        QtWidgets.QApplication.quit()
        event.accept()


#    def apply_mask(self, image, mask, color, alpha=0.5):
#        """Apply the given mask to the image.
#        """
#        for c in range(3):
#            image[:, :, c] = np.where(mask == 1,
#                                      image[:, :, c] *
#                                      (1 - alpha) + alpha * color[c] * 255,
#                                      image[:, :, c])
#        return image
#%%
#    @run_in_thread

    def cam_snap(self):
        """Get a image from camera"""
        self.cam = CamActuator()
        self.cam.initializeCamera()

        exposure_time = self.CamExposureBox.value()
        self.Rawimage = self.cam.SnapImage(exposure_time)
        self.cam.Exit()
        print('Snap finished')

        self.MLtargetedImg_raw = self.Rawimage.copy()

        self.MLtargetedImg = self.convert_for_MaskRCNN(self.MLtargetedImg_raw)

        self.show_raw_image(self.MLtargetedImg)

        self.addedROIitemMask = np.zeros(
            (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))
        self.MLmask = np.zeros(
            (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))
    def run(self):
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                               Connect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        """
        # =============================================================================
        #         connect the Objective motor
        # =============================================================================
        """
        print('----------------------Starting to connect the Objective motor-------------------------')
        self.pi_device_instance = PIMotor()
        print('Objective motor connected.')
        self.errornum = 0
        self.init_focus_position = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)['1']
        print("init_focus_position : {}".format(self.init_focus_position))

        """
        # =============================================================================
        #         connect the Hmamatsu camera
        # =============================================================================
        """
        self._use_camera = False
        # Check if camera is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for waveform_and_cam_key in self.RoundQueueDict[key][1]:
                    if "CameraPackage_" in waveform_and_cam_key:
                        if len(self.RoundQueueDict[key][1][waveform_and_cam_key]) != 0:
                            self._use_camera = True
        
        if self._use_camera:
            print('Connecting camera...')
            self.HamamatsuCam = CamActuator()
            self.HamamatsuCam.initializeCamera()
        else:
            print('No camera involved.')
        
        """
        # =============================================================================
        #         connect the Insight X3
        # =============================================================================
        """        
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.Laserinstance = InsightX3('COM11')
            try:
                # querygap = 1.1
                # self.Laserinstance.SetWatchdogTimer(0) # Disable the laser watchdog!
#                Status_watchdog_thread = threading.Thread(target = self.Status_watchdog, args=[querygap], daemon=True)
#                Status_watchdog_thread.start() 
                # time.sleep(1)
                #-------------Initialize laser--------------
                self.watchdog_flag = False
                time.sleep(0.5)
        
                warmupstatus = 0
                while int(warmupstatus) != 100:
                    warmupstatus = self.Laserinstance.QueryWarmupTime()
                    time.sleep(0.6)
                        
                self.watchdog_flag = True
                time.sleep(0.5)
            except:
                print('Laser not connected.')
                
            # If turn on the laser shutter in the beginning
            if 'Shutter_Open' in self.GeneralSettingDict['StartUpEvents']:
                time.sleep(0.5)

                self.Laserinstance.Open_TunableBeamShutter()

                time.sleep(0.5)     
        """
        # =============================================================================
        #         Initialize ML
        # =============================================================================
        """        
        self._use_ML = False
        # Check if machine learning segmentation is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for photocycle_key in self.RoundQueueDict[key][2]:
                    if "PhotocyclePackage_" in photocycle_key:
                        if len(self.RoundQueueDict[key][2][photocycle_key]) != 0:
                            self._use_ML = True
        if self._use_ML:
            from ImageAnalysis.ImageProcessing_MaskRCNN import ProcessImageML
            self.Predictor = ProcessImageML()
            print("ML loaded.")
            
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                                  Execution
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        GridSequence = 0
        TotalGridNumber = self.meshgridnumber**2
        
        for EachGrid in range(TotalGridNumber):
            """
            #------------------------------------------------------------------------------
            #:::::::::::::::::::::::::::::::: AT EACH GRID ::::::::::::::::::::::::::::::::
            #------------------------------------------------------------------------------
            """
            self.Grid_index = EachGrid
            """
            # =============================================================================
            #         For each small repeat unit in the scanning meshgrid
            # =============================================================================
            """

            time.sleep(0.5)
            
            for EachRound in range(int(len(self.RoundQueueDict)/2-1)): # EachRound is the round sequence number starting from 0, while the actual number used in dictionary is 1.
                """
                #-------------------------------------------------------------------------------
                #:::::::::::::::::::::::::::::::: AT EACH ROUND ::::::::::::::::::::::::::::::::
                #-------------------------------------------------------------------------------
                """
                print ('----------------------------------------------------------------------------')            
                print('Below is Grid {}, Round {}.'.format(EachGrid, EachRound+1)) # EachRound+1 is the corresponding round number when setting the dictionary starting from round 1.
                """
                # =============================================================================
                #         Execute Insight event at the beginning of each round
                # =============================================================================
                """            
                self.laser_init(EachRound)
                    
                """
                # =============================================================================
                #         Execute filter event at the beginning of each round
                # =============================================================================
                """
                self.filters_init(EachRound)

                """
                # =============================================================================
                #         Generate focus position list at the beginning of each round
                # =============================================================================
                """
                if EachRound == 0:
                    ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]        
                    ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
                    ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
            
                    # Generate position list.
                    ZStacklinspaceStart = self.init_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.init_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
    
                    self.ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = ZStackNum)
                
                self.currentCoordsSeq = 0
                #%%
                #-------------Unpack infor for stage move.
                CoordsNum = len(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)])              
                for EachCoord in range(CoordsNum):
                    """
                    #------------------------------------------------------------------------------------
                    #:::::::::::::::::::::::::::::::: AT EACH COORDINATE ::::::::::::::::::::::::::::::::
                    #------------------------------------------------------------------------------------
                    """
                    self.error_massage = None
                    
                    self.currentCoordsSeq += 1
                    
                    """
                    # =============================================================================
                    #         Stage movement
                    # =============================================================================
                    """
                    self.coord_array = self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord]
                    
                    # Offset coordinate row value for each well.
                    ScanningGridOffset_Row = int(EachGrid % self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 
                    # Offset coordinate colunm value for each well.
                    ScanningGridOffset_Col = int(EachGrid/self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 

                    RowIndex = self.coord_array['row'] + ScanningGridOffset_Row
                    ColumnIndex = self.coord_array['col'] + ScanningGridOffset_Col
                                      
                    try:
                        # for _ in range(2): # Repeat twice
                        #     self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        #     time.sleep(1)
                        self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        time.sleep(1.2)
                    except:
                        self.error_massage = 'Fail_MoveStage'
                        self.errornum += 1
                        print('Stage move failed! Error number: {}'.format(int(self.errornum)))

                    time.sleep(0.2)
                    
                    """
                    # =============================================================================
                    #                               Focus position
                    #         Unpack the focus stack information, conduct auto-focusing if set.
                    # =============================================================================
                    """
                    # Here also generate the ZStackPosList.
                    self.ZStackNum = self.unpack_focus_stack(EachGrid, EachRound, EachCoord)
                    
                    self.stack_focus_degree_list = []
                    
                    print('*******************************************Round {}. Current index: {}.**************************************************'.format\
                          (EachRound+1, [RowIndex,ColumnIndex]))
                    
                    #------------------Move to Z stack focus-------------------
                    for EachZStackPos in range(self.ZStackNum): 
                        """
                        #------------------------------------------------------------------------------------
                        #:::::::::::::::::::::::::::::::: AT EACH ZSTACK ::::::::::::::::::::::::::::::::::::
                        #------------------------------------------------------------------------------------
                        """
                        print('--------------------------------------------Stack {}--------------------------------------------------'.format(EachZStackPos+1))
                        if self.ZStackNum > 1:
                            self.ZStackOrder = int(EachZStackPos +1) # Here the first one is 1, not starting from 0.
                            
                            self.FocusPos = self.ZStackPosList[EachZStackPos]# + self.wavelength_offset    
                            
                            # Add the focus degree of previous image to the list.
                            # For stack of 3 only.
                            if EachZStackPos > 0:
                                try:
                                    self.stack_focus_degree_list.append(self.FocusDegree_img_reconstructed)
                                    
                                except:
                                    # FocusDegree_img_reconstructed is not generated with camera imaging.
                                    pass
                            print('stack_focus_degree_list is {}'.format(self.stack_focus_degree_list))
                            #-----Suppose now it's the 3rd stack position-----
                            if len(self.stack_focus_degree_list) == 2:
                                if self.stack_focus_degree_list[-1] < self.stack_focus_degree_list[-2]:
                                    self.FocusPos = self.ZStackPosList[0] - (self.ZStackPosList[1] - self.ZStackPosList[0])/2
                                    print('Focus degree decreasing, run the other direction.')
                            #-------------------------------------------------
                            
                            print('Target focus pos: {}'.format(self.FocusPos))
    
                            self.pi_device_instance.move(self.FocusPos)
                            # self.auto_focus_positionInStack = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)
                            # print("Current position: {:.4f}".format(self.auto_focus_positionInStack['1']))
                            
                            time.sleep(0.3)
                        else:
                            self.ZStackOrder = 1
                            self.FocusPos = self.init_focus_position
                        """
                        # =============================================================================
                        #         Execute waveform packages
                        # =============================================================================
                        """
                        self.Waveform_sequence_Num = int(len(self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]))                        
                        #------------For waveforms in each coordinate----------
                        for EachWaveform in range(self.Waveform_sequence_Num):
                            
                            """
                            # =============================================================================
                            #         For photo-cycle 
                            # =============================================================================
                            """
                            # Get the photo cycle information
                            PhotocyclePackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][2] \
                                                            ["PhotocyclePackage_{}".format(EachWaveform+1)]
                            
                            # See if in this waveform sequence photo cycle is involved.
                            # PhotocyclePackageToBeExecute is {} if not configured.
                            if len(PhotocyclePackageToBeExecute) > 0:
                                
                                
                                
                                # Load the previous acquired camera image
                                self.cam_tif_name = r"M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Octoscope\2020-8-13 Screening Archon1 library V5 and 6\V6\Round2_Coords181_R19800C0_PMT_0Zmax.tif"
                                previous_cam_img = imread(self.cam_tif_name)
                                img_width = previous_cam_img.shape[1]

                                img_height = previous_cam_img.shape[0]

                                # Get the segmentation of full image.
                                fig, ax = plt.subplots()
                                MLresults = self.Predictor.DetectionOnImage(previous_cam_img, axis = ax)
                                
                                ROI_number = len(MLresults["scores"])
                                print('roi number: {}'.format(ROI_number))
                                for each_ROI in range(ROI_number):
                                    # if MLresults['class_ids'][each_ROI] == 3:
                                    ROIlist = MLresults['rois'][each_ROI]
                                    print(ROIlist)
                                    # np array's column([1]) is the width of image, and is the row in stage coordinates.
                                    ROI_center_width = int(ROIlist[1] + (ROIlist[3] - ROIlist[1])/2)
                                    print('ROI_center_width '.format(ROIlist))
                                    ROI_center_height = int(ROIlist[0] + (ROIlist[2] + ROIlist[0])/2)
                                    print('ROI_center_height '.format(ROIlist))
                                    cam_stage_transform_factor = 1.135
                                    
                                    stage_move_col = (int((img_width)/2) - ROI_center_width) * cam_stage_transform_factor
                                    print(stage_move_col)
                                    stage_move_row = (int((img_height)/2) - ROI_center_height) * cam_stage_transform_factor
                                    print(stage_move_row)
                                    # Move cell of interest to the center of field of view
                                    self.ludlStage.moveRel(xRel = stage_move_row, yRel= stage_move_col)
                                    
                                    time.sleep(1)
                                    
                                    # Move the cell back
                                    self.ludlStage.moveRel(xRel = -1*stage_move_row, yRel= -1*stage_move_col)
                                    time.sleep(1)
                            #--------------------------------------------------
                            
                            """
                            # =============================================================================
                            #         Execute pre-set operations at EACH COORDINATE.
                            # =============================================================================
                            """
                            self.inidividual_coordinate_operation(EachRound, EachWaveform, RowIndex, ColumnIndex)
                                
                            
                        time.sleep(0.6) # Wait for receiving data to be done.
                    time.sleep(0.5)
                    print('*************************************************************************************************************************')
                    
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                            Disconnect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""             
        
        # Switch off laser
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.watchdog_flag = False
            time.sleep(0.5)

            self.Laserinstance.Close_TunableBeamShutter()

            time.sleep(0.5)
            
            self.Laserinstance.SaveVariables()
                     
            self.Laserinstance.Turn_Off_PumpLaser()
                    
        # Disconnect camera
        if self._use_camera == True:
            self.HamamatsuCam.Exit()
        
        # Disconnect focus motor
        try:
            self.pi_device_instance.CloseMotorConnection()
            print('Objective motor disconnected.')
        except:
            pass
class ScanningExecutionThread(QThread):
    
    ScanningResult = pyqtSignal(np.ndarray, np.ndarray, object, object) #The signal for the measurement, we can connect to this signal
    #%%
    def __init__(self, RoundQueueDict, RoundCoordsDict, GeneralSettingDict, *args, **kwargs):        
        super().__init__(*args, **kwargs)
        self.RoundQueueDict = RoundQueueDict
        self.RoundCoordsDict = RoundCoordsDict
        self.GeneralSettingDict = GeneralSettingDict
        self.Status_list = None
        self.ludlStage = LudlStage("COM12")
        self.watchdog_flag = True
        
        self.clock_source = 'DAQ' # Should be set by GUI.
        
        self.scansavedirectory = self.GeneralSettingDict['savedirectory']
        self.meshgridnumber = int(self.GeneralSettingDict['Meshgrid'])
        
        self.wavelength_offset = 0 # An offset of 0.002 mm is observed between 900 and 1280 nm.
    #%%
    def run(self):
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                               Connect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        """
        # =============================================================================
        #         connect the Objective motor
        # =============================================================================
        """
        print('----------------------Starting to connect the Objective motor-------------------------')
        self.pi_device_instance = PIMotor()
        print('Objective motor connected.')
        self.errornum = 0
        self.init_focus_position = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)['1']
        print("init_focus_position : {}".format(self.init_focus_position))

        """
        # =============================================================================
        #         connect the Hmamatsu camera
        # =============================================================================
        """
        self._use_camera = False
        # Check if camera is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for waveform_and_cam_key in self.RoundQueueDict[key][1]:
                    if "CameraPackage_" in waveform_and_cam_key:
                        if len(self.RoundQueueDict[key][1][waveform_and_cam_key]) != 0:
                            self._use_camera = True
        
        if self._use_camera:
            print('Connecting camera...')
            self.HamamatsuCam = CamActuator()
            self.HamamatsuCam.initializeCamera()
        else:
            print('No camera involved.')
        
        """
        # =============================================================================
        #         connect the Insight X3
        # =============================================================================
        """        
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.Laserinstance = InsightX3('COM11')
            try:
                # querygap = 1.1
                # self.Laserinstance.SetWatchdogTimer(0) # Disable the laser watchdog!
#                Status_watchdog_thread = threading.Thread(target = self.Status_watchdog, args=[querygap], daemon=True)
#                Status_watchdog_thread.start() 
                # time.sleep(1)
                #-------------Initialize laser--------------
                self.watchdog_flag = False
                time.sleep(0.5)
        
                warmupstatus = 0
                while int(warmupstatus) != 100:
                    warmupstatus = self.Laserinstance.QueryWarmupTime()
                    time.sleep(0.6)
                        
                self.watchdog_flag = True
                time.sleep(0.5)
            except:
                print('Laser not connected.')
                
            # If turn on the laser shutter in the beginning
            if 'Shutter_Open' in self.GeneralSettingDict['StartUpEvents']:
                time.sleep(0.5)

                self.Laserinstance.Open_TunableBeamShutter()

                time.sleep(0.5)     
        """
        # =============================================================================
        #         Initialize ML
        # =============================================================================
        """        
        self._use_ML = False
        # Check if machine learning segmentation is used.
        for key in self.RoundQueueDict:
            if "RoundPackage_" in key:
                for photocycle_key in self.RoundQueueDict[key][2]:
                    if "PhotocyclePackage_" in photocycle_key:
                        if len(self.RoundQueueDict[key][2][photocycle_key]) != 0:
                            self._use_ML = True
        if self._use_ML:
            from ImageAnalysis.ImageProcessing_MaskRCNN import ProcessImageML
            self.Predictor = ProcessImageML()
            print("ML loaded.")
            
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                                  Execution
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
        GridSequence = 0
        TotalGridNumber = self.meshgridnumber**2
        
        for EachGrid in range(TotalGridNumber):
            """
            #------------------------------------------------------------------------------
            #:::::::::::::::::::::::::::::::: AT EACH GRID ::::::::::::::::::::::::::::::::
            #------------------------------------------------------------------------------
            """
            self.Grid_index = EachGrid
            """
            # =============================================================================
            #         For each small repeat unit in the scanning meshgrid
            # =============================================================================
            """

            time.sleep(0.5)
            
            for EachRound in range(int(len(self.RoundQueueDict)/2-1)): # EachRound is the round sequence number starting from 0, while the actual number used in dictionary is 1.
                """
                #-------------------------------------------------------------------------------
                #:::::::::::::::::::::::::::::::: AT EACH ROUND ::::::::::::::::::::::::::::::::
                #-------------------------------------------------------------------------------
                """
                print ('----------------------------------------------------------------------------')            
                print('Below is Grid {}, Round {}.'.format(EachGrid, EachRound+1)) # EachRound+1 is the corresponding round number when setting the dictionary starting from round 1.
                """
                # =============================================================================
                #         Execute Insight event at the beginning of each round
                # =============================================================================
                """            
                self.laser_init(EachRound)
                    
                """
                # =============================================================================
                #         Execute filter event at the beginning of each round
                # =============================================================================
                """
                self.filters_init(EachRound)

                """
                # =============================================================================
                #         Generate focus position list at the beginning of each round
                # =============================================================================
                """
                if EachRound == 0:
                    ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]        
                    ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
                    ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
            
                    # Generate position list.
                    ZStacklinspaceStart = self.init_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.init_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
    
                    self.ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = ZStackNum)
                
                self.currentCoordsSeq = 0
                #%%
                #-------------Unpack infor for stage move.
                CoordsNum = len(self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)])              
                for EachCoord in range(CoordsNum):
                    """
                    #------------------------------------------------------------------------------------
                    #:::::::::::::::::::::::::::::::: AT EACH COORDINATE ::::::::::::::::::::::::::::::::
                    #------------------------------------------------------------------------------------
                    """
                    self.error_massage = None
                    
                    self.currentCoordsSeq += 1
                    
                    """
                    # =============================================================================
                    #         Stage movement
                    # =============================================================================
                    """
                    self.coord_array = self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord]
                    
                    # Offset coordinate row value for each well.
                    ScanningGridOffset_Row = int(EachGrid % self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 
                    # Offset coordinate colunm value for each well.
                    ScanningGridOffset_Col = int(EachGrid/self.meshgridnumber) * (self.GeneralSettingDict['StageGridOffset']) 

                    RowIndex = self.coord_array['row'] + ScanningGridOffset_Row
                    ColumnIndex = self.coord_array['col'] + ScanningGridOffset_Col
                                      
                    try:
                        # for _ in range(2): # Repeat twice
                        #     self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        #     time.sleep(1)
                        self.ludlStage.moveAbs(RowIndex,ColumnIndex) # Row/Column indexs of np.array are opposite of stage row-col indexs.
                        time.sleep(1.2)
                    except:
                        self.error_massage = 'Fail_MoveStage'
                        self.errornum += 1
                        print('Stage move failed! Error number: {}'.format(int(self.errornum)))

                    time.sleep(0.2)
                    
                    """
                    # =============================================================================
                    #                               Focus position
                    #         Unpack the focus stack information, conduct auto-focusing if set.
                    # =============================================================================
                    """
                    # Here also generate the ZStackPosList.
                    self.ZStackNum = self.unpack_focus_stack(EachGrid, EachRound, EachCoord)
                    
                    self.stack_focus_degree_list = []
                    
                    print('*******************************************Round {}. Current index: {}.**************************************************'.format\
                          (EachRound+1, [RowIndex,ColumnIndex]))
                    
                    #------------------Move to Z stack focus-------------------
                    for EachZStackPos in range(self.ZStackNum): 
                        """
                        #------------------------------------------------------------------------------------
                        #:::::::::::::::::::::::::::::::: AT EACH ZSTACK ::::::::::::::::::::::::::::::::::::
                        #------------------------------------------------------------------------------------
                        """
                        print('--------------------------------------------Stack {}--------------------------------------------------'.format(EachZStackPos+1))
                        if self.ZStackNum > 1:
                            self.ZStackOrder = int(EachZStackPos +1) # Here the first one is 1, not starting from 0.
                            
                            self.FocusPos = self.ZStackPosList[EachZStackPos]# + self.wavelength_offset    
                            
                            # Add the focus degree of previous image to the list.
                            # For stack of 3 only.
                            if EachZStackPos > 0:
                                try:
                                    self.stack_focus_degree_list.append(self.FocusDegree_img_reconstructed)
                                    
                                except:
                                    # FocusDegree_img_reconstructed is not generated with camera imaging.
                                    pass
                            print('stack_focus_degree_list is {}'.format(self.stack_focus_degree_list))
                            #-----Suppose now it's the 3rd stack position-----
                            if len(self.stack_focus_degree_list) == 2:
                                if self.stack_focus_degree_list[-1] < self.stack_focus_degree_list[-2]:
                                    self.FocusPos = self.ZStackPosList[0] - (self.ZStackPosList[1] - self.ZStackPosList[0])/2
                                    print('Focus degree decreasing, run the other direction.')
                            #-------------------------------------------------
                            
                            print('Target focus pos: {}'.format(self.FocusPos))
    
                            self.pi_device_instance.move(self.FocusPos)
                            # self.auto_focus_positionInStack = self.pi_device_instance.pidevice.qPOS(self.pi_device_instance.pidevice.axes)
                            # print("Current position: {:.4f}".format(self.auto_focus_positionInStack['1']))
                            
                            time.sleep(0.3)
                        else:
                            self.ZStackOrder = 1
                            self.FocusPos = self.init_focus_position
                        """
                        # =============================================================================
                        #         Execute waveform packages
                        # =============================================================================
                        """
                        self.Waveform_sequence_Num = int(len(self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]))                        
                        #------------For waveforms in each coordinate----------
                        for EachWaveform in range(self.Waveform_sequence_Num):
                            
                            """
                            # =============================================================================
                            #         For photo-cycle 
                            # =============================================================================
                            """
                            # Get the photo cycle information
                            PhotocyclePackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][2] \
                                                            ["PhotocyclePackage_{}".format(EachWaveform+1)]
                            
                            # See if in this waveform sequence photo cycle is involved.
                            # PhotocyclePackageToBeExecute is {} if not configured.
                            if len(PhotocyclePackageToBeExecute) > 0:
                                
                                
                                
                                # Load the previous acquired camera image
                                self.cam_tif_name = r"M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Octoscope\2020-8-13 Screening Archon1 library V5 and 6\V6\Round2_Coords181_R19800C0_PMT_0Zmax.tif"
                                previous_cam_img = imread(self.cam_tif_name)
                                img_width = previous_cam_img.shape[1]

                                img_height = previous_cam_img.shape[0]

                                # Get the segmentation of full image.
                                fig, ax = plt.subplots()
                                MLresults = self.Predictor.DetectionOnImage(previous_cam_img, axis = ax)
                                
                                ROI_number = len(MLresults["scores"])
                                print('roi number: {}'.format(ROI_number))
                                for each_ROI in range(ROI_number):
                                    # if MLresults['class_ids'][each_ROI] == 3:
                                    ROIlist = MLresults['rois'][each_ROI]
                                    print(ROIlist)
                                    # np array's column([1]) is the width of image, and is the row in stage coordinates.
                                    ROI_center_width = int(ROIlist[1] + (ROIlist[3] - ROIlist[1])/2)
                                    print('ROI_center_width '.format(ROIlist))
                                    ROI_center_height = int(ROIlist[0] + (ROIlist[2] + ROIlist[0])/2)
                                    print('ROI_center_height '.format(ROIlist))
                                    cam_stage_transform_factor = 1.135
                                    
                                    stage_move_col = (int((img_width)/2) - ROI_center_width) * cam_stage_transform_factor
                                    print(stage_move_col)
                                    stage_move_row = (int((img_height)/2) - ROI_center_height) * cam_stage_transform_factor
                                    print(stage_move_row)
                                    # Move cell of interest to the center of field of view
                                    self.ludlStage.moveRel(xRel = stage_move_row, yRel= stage_move_col)
                                    
                                    time.sleep(1)
                                    
                                    # Move the cell back
                                    self.ludlStage.moveRel(xRel = -1*stage_move_row, yRel= -1*stage_move_col)
                                    time.sleep(1)
                            #--------------------------------------------------
                            
                            """
                            # =============================================================================
                            #         Execute pre-set operations at EACH COORDINATE.
                            # =============================================================================
                            """
                            self.inidividual_coordinate_operation(EachRound, EachWaveform, RowIndex, ColumnIndex)
                                
                            
                        time.sleep(0.6) # Wait for receiving data to be done.
                    time.sleep(0.5)
                    print('*************************************************************************************************************************')
                    
        #%%
        """~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        #                                                            Disconnect devices.
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""             
        
        # Switch off laser
        if len(self.RoundQueueDict['InsightEvents']) != 0:
            self.watchdog_flag = False
            time.sleep(0.5)

            self.Laserinstance.Close_TunableBeamShutter()

            time.sleep(0.5)
            
            self.Laserinstance.SaveVariables()
                     
            self.Laserinstance.Turn_Off_PumpLaser()
                    
        # Disconnect camera
        if self._use_camera == True:
            self.HamamatsuCam.Exit()
        
        # Disconnect focus motor
        try:
            self.pi_device_instance.CloseMotorConnection()
            print('Objective motor disconnected.')
        except:
            pass
        
    #%%
    def laser_init(self, EachRound):
        """
        Execute Insight event at the beginning of each round

        Parameters
        ----------
        EachRound : int
            Round index.

        Returns
        -------
        None.

        """
        #-Unpack infor for Insight X3. In the list, the first one is for shutter event and the second one is for wavelength event. 
        InsightX3EventIndexList = [i for i,x in enumerate(self.RoundQueueDict['InsightEvents']) if 'Round_{}'.format(EachRound+1) in x]
          
        if len(InsightX3EventIndexList) == 1:
            print(InsightX3EventIndexList)
            InsightText = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
            if 'Shutter_Open' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)

                self.Laserinstance.Open_TunableBeamShutter()

                time.sleep(0.5)
                print('Laser shutter open.')
                self.watchdog_flag = True
                time.sleep(0.5)

            elif 'Shutter_Close' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)

                self.Laserinstance.Close_TunableBeamShutter()

                time.sleep(0.5)
                print('Laser shutter closed.')
                self.watchdog_flag = True
                time.sleep(0.5)
            elif 'WavelengthTo' in InsightText:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText[InsightText.index('To_')+3:len(InsightText)])
                
                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                self.Laserinstance.SetWavelength(TargetWavelen)

                time.sleep(5)
                self.watchdog_flag = True
                time.sleep(0.5)
                
        elif len(InsightX3EventIndexList) == 2:
            
            InsightText_wl = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[1]]
            InsightText_st = self.RoundQueueDict['InsightEvents'][InsightX3EventIndexList[0]]
            
            if 'WavelengthTo' in InsightText_wl and 'Shutter_Open' in InsightText_st:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])

                self.Laserinstance.SetWavelength(TargetWavelen)

                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                time.sleep(5)

                self.Laserinstance.Open_TunableBeamShutter()

                print('Laser shutter open.')
                self.watchdog_flag = True
                time.sleep(0.5)
                
            elif 'WavelengthTo' in InsightText_wl and 'Shutter_Close' in InsightText_st:
                self.watchdog_flag = False
                time.sleep(0.5)
                TargetWavelen = int(InsightText_wl[InsightText_wl.index('To_')+3:len(InsightText_wl)])
               
                self.Laserinstance.SetWavelength(TargetWavelen)

                if TargetWavelen == 1280:
                    self.wavelength_offset = -0.002 # give an offset if wavelength goes to 1280.
                elif TargetWavelen == 900:
                    self.wavelength_offset = 0
                    
                time.sleep(5)

                self.Laserinstance.Close_TunableBeamShutter()

                time.sleep(1)
                print('Laser shutter closed.')
                self.watchdog_flag = True
                time.sleep(0.5)
                
            time.sleep(2)
            
    def filters_init(self, EachRound):
        """
        Execute filter event at the beginning of each round.

        Parameters
        ----------
        EachRound : int
            Round index.

        Returns
        -------
        None.

        """
        
        #-Unpack infor for filter event. In the list, the first one is for ND filter and the second one is for emission filter.
        FilterEventIndexList = [i for i,x in enumerate(self.RoundQueueDict['FilterEvents']) if 'Round_{}'.format(EachRound+1) in x]
        
        if len(FilterEventIndexList) > 0:
            NDposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[0]]
            NDnumber = NDposText[NDposText.index('ToPos_')+6:len(NDposText)]
            
            EMposText = self.RoundQueueDict['FilterEvents'][FilterEventIndexList[1]]
            EMprotein = EMposText[EMposText.index('ToPos_')+6:len(EMposText)]
            
            # "COM9" for filter 1 port, which has ND values from 0 to 3.
            # "COM7" for filter 2 port, which has ND values from 0 to 0.5.
            if NDnumber == '0':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 0
            elif NDnumber == '1':
                ND_filter1_Pos = 1
                ND_filter2_Pos = 0
            elif NDnumber == '2':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 0
            elif NDnumber == '2.3':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 2
            elif NDnumber == '2.5':
                ND_filter1_Pos = 2
                ND_filter2_Pos = 3
            elif NDnumber == '0.5':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 3        
            elif NDnumber == '0.3':
                ND_filter1_Pos = 0
                ND_filter2_Pos = 2
            
            if EMprotein == 'Arch':
                EM_filter_Pos = 0
            elif EMprotein == 'eGFP' or EMprotein == 'Citrine':
                EM_filter_Pos = 1
            
            # Execution
            if ND_filter1_Pos != None and ND_filter2_Pos != None:
                #Move filter 1
                self.filter1 = ELL9Filter("COM9")
                self.filter1.moveToPosition(ND_filter1_Pos)
                time.sleep(1)
                #Move filter 2
                self.filter2 = ELL9Filter("COM7")
                self.filter2.moveToPosition(ND_filter2_Pos)
                time.sleep(1)
            if EM_filter_Pos != None:
                self.filter3 = ELL9Filter("COM15")
                self.filter3.moveToPosition(EM_filter_Pos)
                time.sleep(1)
    
    def unpack_focus_stack(self, EachGrid, EachRound, EachCoord):
        """
        Unpack the focus stack information.
        Determine focus position either from pre-set numbers of by auto-focusing.
        
        Parameters
        ----------
        EachGrid : int
            Current grid index.
        EachRound : int
            Current round index.
        EachCoord : int
            Current coordinate index.

        Returns
        -------
        ZStackNum : int
            Number of focus positions in stack.
        ZStackPosList : list
            List of focus stack positions for objective to go to.

        """
        ZStackinfor = self.GeneralSettingDict['FocusStackInfoDict']['RoundPackage_{}'.format(EachRound+1)]        
        ZStackNum = int(ZStackinfor[ZStackinfor.index('Focus')+5])
        ZStackStep = float(ZStackinfor[ZStackinfor.index('Being')+5:len(ZStackinfor)])
        
        # If manual focus correction applies, unpact the target focus infor.
        if len(self.GeneralSettingDict['FocusCorrectionMatrixDict']) > 0:
            FocusPosArray = self.GeneralSettingDict['FocusCorrectionMatrixDict']['RoundPackage_{}_Grid_{}'.format(EachRound+1, EachGrid)]
            FocusPosArray = FocusPosArray.flatten('F')
            FocusPos_fromCorrection = FocusPosArray[EachCoord]
            
            ZStacklinspaceStart = FocusPos_fromCorrection - (math.floor(ZStackNum/2))* ZStackStep
            ZStacklinspaceEnd = FocusPos_fromCorrection + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep         
        
        # With auto-focus correction
        else:
            # If go for auto-focus at this coordinate
            auto_focus_flag = self.coord_array['auto_focus_flag']
            # auto_focus_flag = False
            print('focus_position {}'.format(self.coord_array['focus_position']))
            
            #-----------------------Auto focus---------------------------------
            if auto_focus_flag == "yes":
                
                if self.coord_array['focus_position'] == -1.:
                    instance_FocusFinder = FocusFinder(motor_handle = self.pi_device_instance)
                    print("--------------Start auto-focusing-----------------")
                    # instance_FocusFinder.total_step_number = 7
                    # instance_FocusFinder.init_step_size = 0.013
                    # self.auto_focus_position = instance_FocusFinder.gaussian_fit()
                    self.auto_focus_position = instance_FocusFinder.bisection()
                    
                    relative_move_coords = [[1550,0],[0,1550],[1550,1550]]
                    trial_num = 0
                    while self.auto_focus_position == False: # If there's no cell in FOV
                        if trial_num <= 2:
                            print('No cells found. move to next pos.')
                            # Move to next position in real scanning coordinates.
                            self.ludlStage.moveRel(relative_move_coords[trial_num][0],relative_move_coords[trial_num][1])
                            time.sleep(1)
                            print('Now stage pos is {}'.format(self.ludlStage.getPos()))
                            
                            self.auto_focus_position = instance_FocusFinder.bisection()
                            # Move back 
                            self.ludlStage.moveRel(-1*relative_move_coords[trial_num][0],-1*relative_move_coords[trial_num][1])
                            
                            trial_num += 1
                        else:
                            print('No cells in neighbouring area.')
                            self.auto_focus_position = self.ZStackPosList[int(len(self.ZStackPosList)/2)]
                            break
                        
                    print("--------------End of auto-focusing----------------")
                    time.sleep(1)
      
                    # Record the position, try to write it in the NEXT round dict.
                    try:
                        self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.auto_focus_position
                    except:# If it's already the last round, skip.
                        pass
                    
                    # Generate position list.
                    ZStacklinspaceStart = self.auto_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.auto_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
                
                else: # If there's already position from last round, move to it.
                    self.previous_auto_focus_position = self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound+1)][EachCoord]['focus_position']
                    
                    try:
                        # Record the position, try to write it in the NEXT round dict. 
                        self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.previous_auto_focus_position
                    except:
                        pass
                    
                    # Generate position list.
                    ZStacklinspaceStart = self.previous_auto_focus_position - (math.floor(ZStackNum/2)) * ZStackStep
                    ZStacklinspaceEnd = self.previous_auto_focus_position + (ZStackNum - math.floor(ZStackNum/2)-1) * ZStackStep
                    
                    # self.previous_auto_focus_position = self.auto_focus_position
                    # print("=====================Move to last recorded position=================")
                    # self.pi_device_instance.move(self.previous_auto_focus_position)
                    # time.sleep(0.2)
                    
                    # instance_FocusFinder = FocusFinder(motor_handle = self.pi_device_instance)
                    # print("--------------Start auto-focusing-----------------")
                    # self.auto_focus_position = instance_FocusFinder.bisection()
                    
                    # try:
                    #     if self.auto_focus_position[0] == False: # If there's no cell in FOV
                    #     # Skip?  mid_position = [False, self.current_pos]
                    #         self.auto_focus_position = self.auto_focus_position[1]
                    # except:
                    #     pass
                        
                    # print("--------------End of auto-focusing----------------")
                    # time.sleep(1)
      
                    # # Record the position, try to write it in the NEXT round dict.
                    # try:
                    #     self.RoundCoordsDict['CoordsPackage_{}'.format(EachRound + 2)][EachCoord]['focus_position'] = self.auto_focus_position
                    # except:# If it's already the last round, skip.
                    #     pass                    
                

                # Generate the position list, for none-auto-focus coordinates they will use the same list variable.
                self.ZStackPosList = np.linspace(ZStacklinspaceStart, ZStacklinspaceEnd, num = ZStackNum)
                print('ZStackPos is : {}'.format(self.ZStackPosList))   
            #------------------------------------------------------------------
            # If not auto focus, use the same list variable self.ZStackPosList.
            else:
                pass
                
        return ZStackNum
        
    def inidividual_coordinate_operation(self, EachRound, EachWaveform, RowIndex, ColumnIndex):
        """
        Execute pre-set operations at each coordinate.

        Parameters
        ----------
        EachRound : int
            Current round index.
        EachWaveform : int
            Current waveform package index.
        RowIndex : int
            Current sample stage row index.
        ColumnIndex : int
            Current sample stage row index.

        Returns
        -------
        None.

        """
        # Extract information
        WaveformPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][0]['WaveformPackage_{}'.format(EachWaveform+1)]
        CameraPackageToBeExecute = self.RoundQueueDict['RoundPackage_{}'.format(EachRound+1)][1]["CameraPackage_{}".format(EachWaveform+1)]                  
        WaveformPackageGalvoInfor = self.RoundQueueDict['GalvoInforPackage_{}'.format(EachRound+1)]['GalvoInfor_{}'.format(EachWaveform+1)]

        self.readinchan = WaveformPackageToBeExecute[3]
        self.RoundWaveformIndex = [EachRound+1, EachWaveform+1] # first is current round number, second is current waveform package number.
        self.CurrentPosIndex = [RowIndex, ColumnIndex]
        
        #----------------Camera operations-----------------
        _camera_isUsed = False
        if CameraPackageToBeExecute != {}: # if camera operations are configured
            _camera_isUsed = True
            CamSettigList = CameraPackageToBeExecute["Settings"]
            self.HamamatsuCam.StartStreaming(BufferNumber = CameraPackageToBeExecute["Buffer_number"],
                                             trigger_source = CamSettigList[CamSettigList.index("trigger_source")+1],
                                             exposure_time = CamSettigList[CamSettigList.index("exposure_time")+1],
                                             trigger_active = CamSettigList[CamSettigList.index("trigger_active")+1],
                                             subarray_hsize = CamSettigList[CamSettigList.index("subarray_hsize")+1],
                                             subarray_vsize = CamSettigList[CamSettigList.index("subarray_vsize")+1],
                                             subarray_hpos = CamSettigList[CamSettigList.index("subarray_hpos")+1],
                                             subarray_vpos = CamSettigList[CamSettigList.index("subarray_vpos")+1])
            # HamamatsuCam starts another thread to pull out frames from buffer.
            # Make sure that the camera is prepared before waveform execution.
#                                while self.HamamatsuCam.isStreaming == False:
#                                    print('Waiting for camera...')
#                                    time.sleep(0.5)
            time.sleep(1)
        print('Now start waveforms')
        #----------------Waveforms operations--------------
        if WaveformPackageGalvoInfor != 'NoGalvo': # Unpack the information of galvo scanning.
            self.readinchan = WaveformPackageGalvoInfor[0]
            self.repeatnum = WaveformPackageGalvoInfor[1]
            self.PMT_data_index_array = WaveformPackageGalvoInfor[2]
            self.averagenum = WaveformPackageGalvoInfor[3]
            self.lenSample_1 = WaveformPackageGalvoInfor[4]
            self.ScanArrayXnum = WaveformPackageGalvoInfor[5]
        
        self.adcollector = DAQmission()
        # self.adcollector.collected_data.connect(self.ProcessData)
        self.adcollector.runWaveforms(clock_source = self.clock_source, sampling_rate = WaveformPackageToBeExecute[0],
                                      analog_signals = WaveformPackageToBeExecute[1], digital_signals = WaveformPackageToBeExecute[2], 
                                      readin_channels = WaveformPackageToBeExecute[3])
        self.adcollector.save_as_binary(self.scansavedirectory)
        self.recorded_raw_data = self.adcollector.get_raw_data()
        self.Process_raw_data()
        
        #------------------Camera saving-------------------
        if _camera_isUsed == True:
            self.HamamatsuCam.isSaving = True
            img_text =  "_Cam_"+str(self.RoundWaveformIndex[1])+"_Zpos" + str(self.ZStackOrder)
            self.cam_tif_name = self.generate_tif_name(extra_text = img_text)
            self.HamamatsuCam.StopStreaming(saving_dir = self.cam_tif_name)
            # Make sure that the saving process is finished.
            while self.HamamatsuCam.isSaving == True:
                print('Camera saving...')
                time.sleep(0.5)
            time.sleep(1) 

        time.sleep(0.5)      
        
    def Process_raw_data(self):

        self.channel_number = len(self.recorded_raw_data)
        
        self.data_collected_0 = self.recorded_raw_data[0][0:len(self.recorded_raw_data[0])-1]*-1
        print(len(self.data_collected_0))         
        
        if self.channel_number == 1:            
            if 'Vp' in self.readinchan:
                pass
            elif 'Ip' in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:  # repeatnum, PMT_data_index_array, averagenum, ScanArrayXnum
                # Reconstruct the image from np array and save it.
                self.PMT_image_processing()
                
        elif self.channel_number == 2: 
            if 'PMT' not in self.readinchan:
                pass
            elif 'PMT' in self.readinchan:

                self.PMT_image_processing()

        print('ProcessData executed.')        
        

    # def ProcessData(self, data_waveformreceived):
    #     print('ZStackOrder is:'+str(self.ZStackOrder)+'numis_'+str(self.ZStackNum))
    #     self.adcollector.save_as_binary(self.scansavedirectory)
        
    #     self.channel_number = len(data_waveformreceived)
        
    #     self.data_collected_0 = data_waveformreceived[0]*-1
    #     self.data_collected_0 = self.data_collected_0[0:len(self.data_collected_0)-1]
    #     print(len(self.data_collected_0))
        
    #     if self.channel_number == 1:            
    #         if 'Vp' in self.readinchan:
    #             pass
    #         elif 'Ip' in self.readinchan:
    #             pass
    #         elif 'PMT' in self.readinchan:  # repeatnum, PMT_data_index_array, averagenum, ScanArrayXnum

    #             # Reconstruct the image from np array and save it.
    #             self.PMT_image_processing()
                
    #     elif self.channel_number == 2: 
    #         if 'PMT' not in self.readinchan:
    #             pass
    #         elif 'PMT' in self.readinchan:
                
    #             self.PMT_image_processing()

    #     print('ProcessData executed.')

    def PMT_image_processing(self):
        """
        Reconstruct the image from np array and save it.

        Returns
        -------
        None.

        """
        for imageSequence in range(self.repeatnum):
            
            try:
                self.PMT_image_reconstructed_array = self.data_collected_0[np.where(self.PMT_data_index_array == imageSequence+1)]

                Dataholder_average = np.mean(self.PMT_image_reconstructed_array.reshape(self.averagenum, -1), axis=0)

                Value_yPixels = int(self.lenSample_1/self.ScanArrayXnum)
                self.PMT_image_reconstructed = np.reshape(Dataholder_average, (Value_yPixels, self.ScanArrayXnum))
                
                self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 50:550] # Crop size based on: M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Xin\2019-12-30 2p beads area test 4um
                # self.PMT_image_reconstructed = self.PMT_image_reconstructed[:, 70:326] # for 256*256 images
                
                # Evaluate the focus degree of re-constructed image.
                self.FocusDegree_img_reconstructed = ProcessImage.local_entropy(self.PMT_image_reconstructed.astype('float32'))
                print('FocusDegree_img_reconstructed is {}'.format(self.FocusDegree_img_reconstructed))
                
                # Save the individual file.
                with skimtiff.TiffWriter(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0]) + '_Grid' + str(self.Grid_index) +'_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zpos'+str(self.ZStackOrder)+'.tif'), imagej = True) as tif:                
                    tif.save(self.PMT_image_reconstructed.astype('float32'), compress=0, metadata = {"FocusPos: " : str(self.FocusPos)})
               
                plt.figure()
                plt.imshow(self.PMT_image_reconstructed, cmap = plt.cm.gray) # For reconstructed image we pull out the first layer, getting 2d img.
                plt.show()                

                #---------------------------------------------For multiple images in one z pos, Stack the arrays into a 3d array--------------------------------------------------------------------------
                # if imageSequence == 0:
                #     self.PMT_image_reconstructed_stack = self.PMT_image_reconstructed[np.newaxis, :, :] # Turns into 3d array
                # else:
                #     self.PMT_image_reconstructed_stack = np.concatenate((self.PMT_image_reconstructed_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)
                #     print(self.PMT_image_reconstructed_stack.shape)
                    
                #---------------------------------------------Calculate the z max projection-----------------------------------------------------------------------
                if self.repeatnum == 1: # Consider one repeat image situlation 
                    if self.ZStackNum > 1:
                        if self.ZStackOrder == 1:
                            self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]

                        else:

                            self.PMT_image_maxprojection_stack = np.concatenate((self.PMT_image_maxprojection_stack, self.PMT_image_reconstructed[np.newaxis, :, :]), axis=0)

                    else:
                        self.PMT_image_maxprojection_stack = self.PMT_image_reconstructed[np.newaxis, :, :]
                        
                # Save the max projection image
                if self.ZStackOrder == self.ZStackNum:
                    self.PMT_image_maxprojection = np.max(self.PMT_image_maxprojection_stack, axis=0)
                    
                    # Save the zmax file.
                    with skimtiff.TiffWriter(os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+ '_Grid' + str(self.Grid_index) + '_Coords'+str(self.currentCoordsSeq)+'_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+'_PMT_'+str(imageSequence)+'Zmax'+'.tif'), imagej = True) as tif:                
                        tif.save(self.PMT_image_maxprojection.astype('float32'), compress=0, metadata = {"FocusPos: " : str(self.FocusPos)})

                
            except:
                print('No.{} image failed to generate.'.format(imageSequence))
                        
        
    def generate_tif_name(self, extra_text = "_"):
        
        tif_name = os.path.join(self.scansavedirectory, 'Round'+str(self.RoundWaveformIndex[0])+'_Coords'+str(self.currentCoordsSeq)+ \
                                '_R'+str(self.CurrentPosIndex[0])+'C'+str(self.CurrentPosIndex[1])+ extra_text +'.tif')            
        return tif_name
    
    #----------------------------------------------------------------WatchDog for laser----------------------------------------------------------------------------------
    def Status_watchdog(self, querygap):
        
        while True:
            if self.watchdog_flag == True:
                self.Status_list = self.Laserinstance.QueryStatus()
                time.sleep(querygap)
            else:
                print('Watchdog stopped')
                time.sleep(querygap)
示例#13
0
class CoordinatesWidgetUI(QWidget):

    sig_cast_mask_coordinates_to_dmd = pyqtSignal(list)
    sig_cast_mask_coordinates_to_galvo = pyqtSignal(list)
    sig_start_registration = pyqtSignal()
    sig_finished_registration = pyqtSignal()
    sig_cast_camera_image = pyqtSignal(np.ndarray)

    def __init__(self, parent=None, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.main_application = parent
        self.init_gui()

    def closeEvent(self, event):
        try:
            self.DMD
        except:
            pass
        else:
            self.DMD.disconnect_DMD()

        QtWidgets.QApplication.quit()
        event.accept()

    def init_gui(self):
        self.setWindowTitle("Coordinate control")

        self.layout = QGridLayout()
        self.setMinimumSize(1250, 1000)
        self.setLayout(self.layout)

        self.image_mask_stack = QTabWidget()

        self.selection_view = DrawingWidget(self)
        self.selection_view.enable_drawing(True)
        self.selection_view.getView().setLimits(xMin=0,
                                                xMax=2048,
                                                yMin=0,
                                                yMax=2048,
                                                minXRange=2048,
                                                minYRange=2048,
                                                maxXRange=2048,
                                                maxYRange=2048)
        self.selection_view.ui.roiBtn.hide()
        self.selection_view.ui.menuBtn.hide()
        self.selection_view.ui.normGroup.hide()
        self.selection_view.ui.roiPlot.hide()
        # self.selection_view.setImage(plt.imread('CoordinatesManager/Registration_Images/StageRegistration/Distance200_Offset0/A1.png'))

        self.mask_view = SquareImageView()
        self.mask_view.getView().setLimits(xMin=0,
                                           xMax=2048,
                                           yMin=0,
                                           yMax=2048,
                                           minXRange=2048,
                                           minYRange=2048,
                                           maxXRange=2048,
                                           maxYRange=2048)
        self.mask_view.ui.roiBtn.hide()
        self.mask_view.ui.menuBtn.hide()
        self.mask_view.ui.normGroup.hide()
        self.mask_view.ui.roiPlot.hide()
        self.mask_view.ui.histogram.hide()

        self.image_mask_stack.addTab(self.selection_view, 'Select')
        self.image_mask_stack.addTab(self.mask_view, 'Mask')

        self.layout.addWidget(self.image_mask_stack, 0, 0, 5, 1)

        # ---------------------- Mask generation Container  --------------

        self.maskGeneratorContainer = roundQGroupBox()
        self.maskGeneratorContainer.setFixedSize(320, 220)
        self.maskGeneratorContainer.setTitle("Mask generator")
        self.maskGeneratorContainerLayout = QGridLayout()

        self.maskGeneratorLayout = QGridLayout()
        self.maskGeneratorContainer.setLayout(
            self.maskGeneratorContainerLayout)

        self.loadMaskFromFileButton = QPushButton('Open mask')
        self.loadMaskFromFileButton.clicked.connect(self.load_mask_from_file)

        self.addRoiButton = QPushButton("Add ROI")
        self.createMaskButton = QPushButton("Create mask")
        self.removeSelectionButton = QPushButton("Remove ROIs")
        self.addRoiButton.clicked.connect(self.add_polygon_roi)

        self.createMaskButton.clicked.connect(self.create_mask)
        self.removeSelectionButton.clicked.connect(self.remove_selection)

        self.maskGeneratorContainerLayout.addWidget(self.addRoiButton, 1, 0)
        self.maskGeneratorContainerLayout.addWidget(self.createMaskButton, 2,
                                                    0)
        self.maskGeneratorContainerLayout.addWidget(self.removeSelectionButton,
                                                    1, 1)
        self.selectionOptionsContainer = roundQGroupBox()
        self.selectionOptionsContainer.setTitle('Options')
        self.selectionOptionsLayout = QGridLayout()
        self.fillContourButton = QCheckBox()
        self.invertMaskButton = QCheckBox()
        self.thicknessSpinBox = QSpinBox()
        self.thicknessSpinBox.setRange(1, 25)
        self.selectionOptionsLayout.addWidget(QLabel('Fill contour:'), 0, 0)
        self.selectionOptionsLayout.addWidget(self.fillContourButton, 0, 1)
        self.selectionOptionsLayout.addWidget(QLabel('Invert mask:'), 1, 0)
        self.selectionOptionsLayout.addWidget(self.invertMaskButton, 1, 1)
        self.selectionOptionsLayout.addWidget(QLabel('Thickness:'), 2, 0)
        self.selectionOptionsLayout.addWidget(self.thicknessSpinBox, 2, 1)
        self.selectionOptionsContainer.setLayout(self.selectionOptionsLayout)

        self.snapFovButton = QPushButton('Image FOV')
        self.snapFovButton.clicked.connect(self.snap_fov)

        self.maskGeneratorContainerLayout.addWidget(self.snapFovButton, 0, 0,
                                                    1, 1)
        self.maskGeneratorContainerLayout.addWidget(
            self.loadMaskFromFileButton, 0, 1, 1, 1)
        self.maskGeneratorContainerLayout.addWidget(
            self.selectionOptionsContainer, 2, 1, 2, 1)

        self.layout.addWidget(self.maskGeneratorContainer, 0, 1)

        self.DMDWidget = DMDWidget.DMDWidget()
        self.layout.addWidget(self.DMDWidget, 1, 1)

        self.DMDWidget.sig_request_mask_coordinates.connect(
            lambda: self.cast_mask_coordinates('dmd'))
        self.sig_cast_mask_coordinates_to_dmd.connect(
            self.DMDWidget.receive_mask_coordinates)
        self.DMDWidget.sig_start_registration.connect(
            lambda: self.sig_start_registration.emit())
        self.DMDWidget.sig_finished_registration.connect(
            lambda: self.sig_finished_registration.emit())

        self.GalvoWidget = GalvoWidget.GalvoWidget()
        self.layout.addWidget(self.GalvoWidget, 2, 1)

        self.GalvoWidget.sig_request_mask_coordinates.connect(
            lambda: self.cast_mask_coordinates('galvo'))
        self.sig_cast_mask_coordinates_to_galvo.connect(
            self.GalvoWidget.receive_mask_coordinates)
        self.GalvoWidget.sig_start_registration.connect(
            lambda: self.sig_start_registration.emit())
        self.GalvoWidget.sig_finished_registration.connect(
            lambda: self.sig_finished_registration.emit())

        self.ManualRegistrationWidget = ManualRegistration.ManualRegistrationWidget(
            self)
        self.ManualRegistrationWidget.sig_request_camera_image.connect(
            self.cast_camera_image)
        self.sig_cast_camera_image.connect(
            self.ManualRegistrationWidget.receive_camera_image)

        self.layout.addWidget(self.ManualRegistrationWidget, 3, 1)

        self.StageRegistrationWidget = StageRegistrationWidget.StageWidget(
            self)
        self.layout.addWidget(self.StageRegistrationWidget, 4, 1)

    def cast_transformation_to_DMD(self, transformation, laser):
        self.DMDWidget.transform[laser] = transformation
        self.DMDWidget.save_transformation()

    def cast_transformation_to_galvos(self, sig):
        transformation = sig
        self.GalvoWidget.transform = transformation
        self.GalvoWidget.save_transformation()

    def cast_camera_image(self):
        image = self.selection_view.image
        if type(image) == np.ndarray:
            self.sig_cast_camera_image.emit(image)

    def snap_fov(self):
        self.DMDWidget.interupt_projection()

        self.DMDWidget.project_full_white()

        self.cam = CamActuator()
        self.cam.initializeCamera()
        image = self.cam.SnapImage(0.04)
        self.cam.Exit()
        self.selection_view.setImage(image)

    def cast_mask_coordinates(self, receiver):
        list_of_rois = self.get_list_of_rois()

        sig = [
            list_of_rois,
            self.fillContourButton.isChecked(),
            self.thicknessSpinBox.value(),
            self.invertMaskButton.isChecked()
        ]

        if receiver == 'dmd':
            self.sig_cast_mask_coordinates_to_dmd.emit(sig)
        else:
            self.sig_cast_mask_coordinates_to_galvo.emit(sig)

    def get_list_of_rois(self):
        view = self.selection_view
        list_of_rois = []

        for roi in view.roilist:
            roi_handle_positions = roi.getLocalHandlePositions()
            roi_origin = roi.pos()

            for idx, pos in enumerate(roi_handle_positions):
                roi_handle_positions[idx] = pos[1]

            num_vertices = len(roi_handle_positions)
            vertices = np.zeros([num_vertices, 2])

            for idx, vertex in enumerate(roi_handle_positions):
                vertices[idx, :] = np.array(
                    [vertex.x() + roi_origin.x(),
                     vertex.y() + roi_origin.y()])

            list_of_rois.append(vertices)

        return list_of_rois

    def create_mask(self):
        flag_fill_contour = self.fillContourButton.isChecked()
        flag_invert_mode = self.invertMaskButton.isChecked()
        contour_thickness = self.thicknessSpinBox.value()

        list_of_rois = self.get_list_of_rois()

        self.mask = ProcessImage.CreateBinaryMaskFromRoiCoordinates(list_of_rois, \
                                                       fill_contour = flag_fill_contour, \
                                                       contour_thickness = contour_thickness, \
                                                       invert_mask = flag_invert_mode)

        self.mask_view.setImage(self.mask)

    def remove_selection(self):
        self.selection_view.clear_rois()

    def set_camera_image(self, sig):
        self.selection_view.setImage(sig)

    def add_polygon_roi(self):
        view = self.selection_view

        x = (view.getView().viewRect().x()) * 0.3
        y = (view.getView().viewRect().y()) * 0.3
        a = (view.getView().viewRect().width() + x) * 0.3
        b = (view.getView().viewRect().height() + y) * 0.3
        c = (view.getView().viewRect().width() + x) * 0.7
        d = (view.getView().viewRect().height() + y) * 0.7
        polygon_roi = pg.PolyLineROI([[a, b], [c, b], [c, d], [a, d]],
                                     pen=view.pen,
                                     closed=True,
                                     movable=True,
                                     removable=True)

        view.getView().addItem(polygon_roi)
        view.append_to_roilist(polygon_roi)

    def load_mask_from_file(self):
        """
        Open a file manager to browse through files, load image file
        """
        self.loadFileName, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, 'Select file', './CoordinateManager/Images/',
            "(*.png, *.tiff, *.jpg)")
        try:
            image = plt.imread(self.loadFileName)

            self.mask = image
            self.mask_view.setImage(self.mask)
        except:
            print('fail to load file.')
示例#14
0
    def __init__(self, source_of_image = "PMT", init_search_range = 0.010, total_step_number = 5, imaging_conditions = {'edge_volt':5}, \
                 motor_handle = None, camera_handle = None, twophoton_handle = None, *args, **kwargs):
        """
        

        Parameters
        ----------
        source_of_image : string, optional
            The input source of image. The default is PMT.
        init_search_range : int, optional
            The step size when first doing coarse searching. The default is 0.010.
        total_step_number : int, optional
            Number of steps in total to find optimal focus. The default is 5.
        imaging_conditions : list
            Parameters for imaging.
            For PMT, it specifies the scanning voltage.
            For camera, it specifies the AOTF voltage and exposure time.
        motor_handle : TYPE, optional
            Handle to control PI motor. The default is None.
        twophoton_handle : TYPE, optional
            Handle to control Insight X3. The default is None.

        Returns
        -------
        None.

        """
        super().__init__(*args, **kwargs)
        
        # The step size when first doing coarse searching.
        self.init_search_range = init_search_range
        
        # Number of steps in total to find optimal focus.
        self.total_step_number = total_step_number
        
        # Parameters for imaging.
        self.imaging_conditions = imaging_conditions
        
        if motor_handle == None:
            # Connect the objective if the handle is not provided.
            self.pi_device_instance = PIMotor()
        else:
            self.pi_device_instance = motor_handle
        
        # Current position of the focus.
        self.current_pos = self.pi_device_instance.GetCurrentPos()

        # Number of steps already tried.
        self.steps_taken = 0
        # The focus degree of previous position.
        self.previous_degree_of_focus = 0
        # Number of going backwards.
        self.turning_point = 0
        # The input source of image.
        self.source_of_image = source_of_image
        if source_of_image == "PMT":
            self.galvo = RasterScan(Daq_sample_rate = 500000, edge_volt = self.imaging_conditions['edge_volt'])
        elif source_of_image == "Camera":
            if camera_handle == None:
                # If no camera instance fed in, initialize camera.
                self.HamamatsuCam_ins = CamActuator()
                self.HamamatsuCam_ins.initializeCamera()
            else:
                self.HamamatsuCam_ins = camera_handle
示例#15
0
class FocusFinder():
    
    def __init__(self, source_of_image = "PMT", init_search_range = 0.010, total_step_number = 5, imaging_conditions = {'edge_volt':5}, \
                 motor_handle = None, camera_handle = None, twophoton_handle = None, *args, **kwargs):
        """
        

        Parameters
        ----------
        source_of_image : string, optional
            The input source of image. The default is PMT.
        init_search_range : int, optional
            The step size when first doing coarse searching. The default is 0.010.
        total_step_number : int, optional
            Number of steps in total to find optimal focus. The default is 5.
        imaging_conditions : list
            Parameters for imaging.
            For PMT, it specifies the scanning voltage.
            For camera, it specifies the AOTF voltage and exposure time.
        motor_handle : TYPE, optional
            Handle to control PI motor. The default is None.
        twophoton_handle : TYPE, optional
            Handle to control Insight X3. The default is None.

        Returns
        -------
        None.

        """
        super().__init__(*args, **kwargs)
        
        # The step size when first doing coarse searching.
        self.init_search_range = init_search_range
        
        # Number of steps in total to find optimal focus.
        self.total_step_number = total_step_number
        
        # Parameters for imaging.
        self.imaging_conditions = imaging_conditions
        
        if motor_handle == None:
            # Connect the objective if the handle is not provided.
            self.pi_device_instance = PIMotor()
        else:
            self.pi_device_instance = motor_handle
        
        # Current position of the focus.
        self.current_pos = self.pi_device_instance.GetCurrentPos()

        # Number of steps already tried.
        self.steps_taken = 0
        # The focus degree of previous position.
        self.previous_degree_of_focus = 0
        # Number of going backwards.
        self.turning_point = 0
        # The input source of image.
        self.source_of_image = source_of_image
        if source_of_image == "PMT":
            self.galvo = RasterScan(Daq_sample_rate = 500000, edge_volt = self.imaging_conditions['edge_volt'])
        elif source_of_image == "Camera":
            if camera_handle == None:
                # If no camera instance fed in, initialize camera.
                self.HamamatsuCam_ins = CamActuator()
                self.HamamatsuCam_ins.initializeCamera()
            else:
                self.HamamatsuCam_ins = camera_handle
    
    def gaussian_fit(self, move_to_focus = True):
        
        # The upper edge.
        upper_position = self.current_pos + self.init_search_range
        # The lower edge.
        lower_position = self.current_pos - self.init_search_range
        
        # Generate the sampling positions.
        sample_positions = np.linspace(lower_position, upper_position, self.total_step_number)
        
        degree_of_focus_list = []
        for each_pos in sample_positions:
            # Go through each position and write down the focus degree.
            degree_of_focus = self.evaluate_focus(round(each_pos, 6))
            degree_of_focus_list.append(degree_of_focus)
        print(degree_of_focus_list)
        
        try:
            interpolated_fitted_curve = ProcessImage.gaussian_fit(degree_of_focus_list)

            # Generate the inpterpolated new focus position axis.
            x_axis_new = np.linspace(lower_position, upper_position, len(interpolated_fitted_curve))
            
            # Generate a dictionary and find the position where has the highest focus degree.
            max_focus_pos = dict(zip(interpolated_fitted_curve, x_axis_new))[np.amax(interpolated_fitted_curve)]
            
            if True: # Plot the fitting.
                plt.plot(sample_positions, np.asarray(degree_of_focus_list),'b+:',label='data')
                plt.plot(x_axis_new, interpolated_fitted_curve,'ro:',label='fit')
                plt.legend()
                plt.title('Fig. Fit for focus degree')
                plt.xlabel('Position')
                plt.ylabel('Focus degree')
                plt.show()
            
            max_focus_pos = round(max_focus_pos, 6)
            print(max_focus_pos)
            self.pi_device_instance.move(max_focus_pos)
            # max_focus_pos_focus_degree = self.evaluate_focus(round(max_focus_pos, 6))
        except:
            print("Fitting failed. Find max in the list.")
            
            max_focus_pos = sample_positions[degree_of_focus_list.index(max(degree_of_focus_list))]
            print(max_focus_pos)
            
        if move_to_focus == True:
            self.pi_device_instance.move(max_focus_pos)
            
        return max_focus_pos
        
    def bisection(self):
        """
        Bisection way of finding focus.

        Returns
        -------
        mid_position : float
            DESCRIPTION.

        """
        # The upper edge in which we run bisection.
        upper_position = self.current_pos + self.init_search_range
        # The lower edge in which we run bisection.
        lower_position = self.current_pos - self.init_search_range

        for step_index in range(1, self.total_step_number + 1):   
            # In each step of bisection finding.
            
            # In the first round, get degree of focus at three positions.
            if step_index == 1:
                # Get degree of focus in the mid.
                mid_position = (upper_position + lower_position)/2
                degree_of_focus_mid = self.evaluate_focus(mid_position)
                print("mid focus degree: {}".format(round(degree_of_focus_mid, 5)))
                
                # Break the loop if focus degree is below threshold which means
                # that there's no cell in image.
                if not ProcessImage.if_theres_cell(self.galvo_image.astype('float32')):
                    print('no cell')
                    mid_position = False
                    break

                # Move to top and evaluate.
                degree_of_focus_up = self.evaluate_focus(obj_position = upper_position)
                print("top focus degree: {}".format(round(degree_of_focus_up, 5)))
                # Move to bottom and evaluate.
                degree_of_focus_low = self.evaluate_focus(obj_position = lower_position)
                print("bot focus degree: {}".format(round(degree_of_focus_low, 5)))
                # Sorting dicitonary of degrees in ascending.
                biesection_range_dic = {"top":[upper_position, degree_of_focus_up], 
                                        "bot":[lower_position, degree_of_focus_low]}
                
            # In the next rounds, only need to go to center and update boundaries.
            elif step_index > 1:
                # The upper edge in which we run bisection.
                upper_position = biesection_range_dic["top"][0]
                # The lower edge in which we run bisection.
                lower_position = biesection_range_dic["bot"][0]
                
                # Get degree of focus in the mid.
                mid_position = (upper_position + lower_position)/2
                degree_of_focus_mid = self.evaluate_focus(mid_position)
                
                print("Current focus degree: {}".format(round(degree_of_focus_mid, 5)))
                
            # If sits in upper half, make the middle values new bottom.
            if biesection_range_dic["top"][1] > biesection_range_dic["bot"][1]:
                biesection_range_dic["bot"] = [mid_position, degree_of_focus_mid]
            else:
                biesection_range_dic["top"] = [mid_position, degree_of_focus_mid]
            
            print("The upper pos: {}; The lower: {}".format(biesection_range_dic["top"][0], biesection_range_dic["bot"][0]))
            
        return mid_position
                
                
    
    def evaluate_focus(self, obj_position = None):
        """
        Evaluate the focus degree of certain objective position.

        Parameters
        ----------
        obj_position : float, optional
            The target objective position. The default is None.

        Returns
        -------
        degree_of_focus : float
            Degree of focus.

        """
        
        if obj_position != None:
            self.pi_device_instance.move(obj_position)
            
        # Get the image.
        if self.source_of_image == "PMT":
            self.galvo_image = self.galvo.run()
            plt.figure()
            plt.imshow(self.galvo_image)
            plt.show()
            
            if False:
                with skimtiff.TiffWriter(os.path.join(r'M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Xin\2020-11-17 gaussian fit auto-focus cells\trial_11', str(obj_position).replace(".", "_")+ '.tif')) as tif:                
                    tif.save(self.galvo_image.astype('float32'), compress=0)
                            
            degree_of_focus = ProcessImage.local_entropy(self.galvo_image.astype('float32'))
            
        elif self.source_of_image == "Camera":
            # First configure the AOTF.
            self.AOTF_runner = DAQmission()
            # Find the AOTF channel key
            for key in self.imaging_conditions:
                if 'AO' in key:
                    # like '488AO'
                    AOTF_channel_key = key
            
            # Set the AOTF first.
            self.AOTF_runner.sendSingleDigital('blankingall', True)
            self.AOTF_runner.sendSingleAnalog(AOTF_channel_key, self.imaging_conditions[AOTF_channel_key])
            
            # Snap an image from camera
            self.camera_image = self.HamamatsuCam_ins.SnapImage(self.imaging_conditions['exposure_time'])
            time.sleep(0.5)
            
            # Set back AOTF
            self.AOTF_runner.sendSingleDigital('blankingall', False)
            self.AOTF_runner.sendSingleAnalog(AOTF_channel_key, 0)
            
            plt.figure()
            plt.imshow(self.camera_image)
            plt.show()
            
            if False:
                with skimtiff.TiffWriter(os.path.join(r'M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Xin\2021-03-06 Camera AF\beads', str(obj_position).replace(".", "_")+ '.tif')) as tif:                
                    tif.save(self.camera_image.astype('float32'), compress=0)
                            
            degree_of_focus = ProcessImage.variance_of_laplacian(self.camera_image.astype('float32'))
                
        time.sleep(0.2)
        
        return degree_of_focus
示例#16
0
class DMDRegistator:
    def __init__(self, DMD, *args, **kwargs):
        self.DMD = DMD
        self.cam = CamActuator()
        self.cam.initializeCamera()

    def registration(self,
                     laser='640',
                     grid_points_x=2,
                     grid_points_y=3,
                     registration_pattern='circles'):
        x_coords = np.linspace(0, 768, grid_points_x + 2)[1:-1]
        y_coords = np.linspace(0, 1024, grid_points_y + 2)[1:-1]

        x_mesh, y_mesh = np.meshgrid(x_coords, y_coords)

        x_coords = np.ravel(x_mesh)
        y_coords = np.ravel(y_mesh)

        dmd_coordinates = np.stack((x_coords, y_coords), axis=1)

        camera_coordinates = np.zeros(dmd_coordinates.shape)

        for i in range(dmd_coordinates.shape[0]):
            x = int(dmd_coordinates[i, 0])
            y = int(dmd_coordinates[i, 1])

            if registration_pattern == 'squares':
                mask = DMDRegistator.create_registration_image_touching_squares(
                    x, y)
            else:
                mask = DMDRegistator.create_registration_image_circle(x, y)

            self.DMD.send_data_to_DMD(mask)
            self.DMD.start_projection()

            image = self.cam.SnapImage(0.01)
            plt.imsave(
                os.getcwd() +
                '/CoordinatesManager/Registration_Images/TouchingSquares/image_'
                + str(i) + '.png', image)
            camera_coordinates[
                i, :] = readRegistrationImages.touchingCoordinateFinder(
                    image, method='curvefit')

            self.DMD.stop_projection()

        print('DMD coordinates:')
        print(dmd_coordinates)
        print('Found camera coordinates:')
        print(camera_coordinates)

        self.DMD.free_memory()
        self.cam.Exit()

        transformation = CoordinateTransformations.polynomial2DFit(
            camera_coordinates, dmd_coordinates, order=1)
        print('Transformation found for x:')
        print(transformation[:, :, 0])
        print('Transformation found for y:')
        print(transformation[:, :, 1])
        return transformation

    def create_registration_image_touching_squares(x, y, sigma=75):
        array = np.zeros((768, 1024))
        array[skimage.draw.rectangle((x - sigma, y - sigma), (x, y))] = 255
        array[skimage.draw.rectangle((x + sigma, y + sigma), (x, y))] = 255
        return array

    def create_registration_image_circle(x, y, sigma=75):
        array = np.zeros((768, 1024))
        array[skimage.draw.circle(x, y, sigma)] = 255
        return array
示例#17
0
 def __init__(self, DMD, *args, **kwargs):
     self.DMD = DMD
     self.cam = CamActuator()
     self.cam.initializeCamera()
示例#18
0
class GalvoRegistrator:
    def __init__(self, *args, **kwargs):
        self.cam = CamActuator()
        self.cam.initializeCamera()

    def registration(self, grid_points_x=3, grid_points_y=3):
        """
        By default, generate 9 galvo voltage coordinates from (-5,-5) to (5,5),
        take the camera images of these points, return a function matrix that 
        transforms camera_coordinates into galvo_coordinates using polynomial transform. 

        Parameters
        ----------
        grid_points_x : TYPE, optional
            DESCRIPTION. The default is 3.
        grid_points_y : TYPE, optional
            DESCRIPTION. The default is 3.

        Returns
        -------
        transformation : TYPE
            DESCRIPTION.

        """
        galvothread = DAQmission()
        readinchan = []

        x_coords = np.linspace(-10, 10, grid_points_x + 2)[1:-1]
        y_coords = np.linspace(-10, 10, grid_points_y + 2)[1:-1]

        xy_mesh = np.reshape(np.meshgrid(x_coords, y_coords), (2, -1),
                             order='F').transpose()

        galvo_coordinates = xy_mesh
        camera_coordinates = np.zeros((galvo_coordinates.shape))

        for i in range(galvo_coordinates.shape[0]):

            galvothread.sendSingleAnalog('galvosx', galvo_coordinates[i, 0])
            galvothread.sendSingleAnalog('galvosy', galvo_coordinates[i, 1])
            time.sleep(1)

            image = self.cam.SnapImage(0.06)
            plt.imsave(
                os.getcwd() +
                '/CoordinatesManager/Registration_Images/2P/image_' + str(i) +
                '.png', image)

            camera_coordinates[i, :] = readRegistrationImages.gaussian_fitting(
                image)

        print('Galvo Coordinate')
        print(galvo_coordinates)
        print('Camera coordinates')
        print(camera_coordinates)
        del galvothread
        self.cam.Exit()

        transformation_cam2galvo = CoordinateTransformations.polynomial2DFit(
            camera_coordinates, galvo_coordinates, order=1)

        transformation_galvo2cam = CoordinateTransformations.polynomial2DFit(
            galvo_coordinates, camera_coordinates, order=1)

        print('Transformation found for x:')
        print(transformation_cam2galvo[:, :, 0])
        print('Transformation found for y:')
        print(transformation_cam2galvo[:, :, 1])

        print('galvo2cam found for x:')
        print(transformation_galvo2cam[:, :, 0])
        print('galvo2cam found for y:')
        print(transformation_galvo2cam[:, :, 1])

        return transformation_cam2galvo