def registration(self, grid_points_x=3, grid_points_y=3):
        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 = CoordinateTransformations.polynomial2DFit(
            camera_coordinates, galvo_coordinates, order=1)

        print('Transformation found for x:')
        print(transformation[:, :, 0])
        print('Transformation found for y:')
        print(transformation[:, :, 1])
        return transformation
Beispiel #2
0
    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])
Beispiel #3
0
    def control_for_registration(self, wavelength, value):
        value = int(value)
        daq = DAQmission()

        if value == 0:
            switch = False
        else:
            switch = True

        if wavelength == "640":
            print(wavelength + ":" + str(value))
            print(str(switch))
            daq.sendSingleAnalog("640AO", value)

            daq.sendSingleDigital("640blanking", switch)

        elif wavelength == "532":
            print(wavelength + ":" + str(value))
            print(str(switch))
            daq.sendSingleAnalog("532AO", value)

            daq.sendSingleDigital("640blanking", switch)

        else:
            print(wavelength + ":" + str(value))
            print(str(switch))
            daq.sendSingleAnalog("488AO", value)

            daq.sendSingleDigital("640blanking", switch)
    def control_for_registration(self, wavelength, value):
        value = int(value)
        daq = DAQmission()

        if value == 0:
            switch = False
        else:
            switch = True

        if wavelength == '640':
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('640AO', value)

            daq.sendSingleDigital('640blanking', switch)

        elif wavelength == '532':
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('532AO', value)

            daq.sendSingleDigital('640blanking', switch)

        else:
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('488AO', value)

            daq.sendSingleDigital('640blanking', switch)
    def execute_tread_single_sample_analog(self, channel):
        daq = DAQmission()
        if channel == '640AO':
            self.lasers_status['640'][1] = self.slider640.value()

            daq.sendSingleAnalog(channel, self.slider640.value() / 100)

        elif channel == '532AO':
            self.lasers_status['532'][1] = self.slider532.value()
            daq.sendSingleAnalog(channel, self.slider532.value() / 100)

        elif channel == '488AO':
            self.lasers_status['488'][1] = self.slider488.value()
            daq.sendSingleAnalog(channel, self.slider488.value() / 100)

        self.sig_lasers_status_changed.emit(self.lasers_status)
    def control_for_registration(self, wavelength, value):
        value = int(value)
        daq = DAQmission()

        if value == 0:
            switch = False
        else:
            switch = True

        if wavelength == '640':
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('640AO', value)
            # execute_tread_singlesample_AOTF_analog = execute_tread_singlesample_analog()
            # execute_tread_singlesample_AOTF_analog.set_waves('640AO', value)
            # execute_tread_singlesample_AOTF_analog.start()

            daq.sendSingleDigital('640blanking', switch)
            # execute_tread_singlesample_AOTF_digital = execute_tread_singlesample_digital()
            # execute_tread_singlesample_AOTF_digital.set_waves('640blanking', switch)
            # execute_tread_singlesample_AOTF_digital.start()

        elif wavelength == '532':
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('532AO', value)

            daq.sendSingleDigital('640blanking', switch)
            # execute_tread_singlesample_AOTF_analog = execute_tread_singlesample_analog()
            # execute_tread_singlesample_AOTF_analog.set_waves('532AO', value)
            # execute_tread_singlesample_AOTF_analog.start()

            # execute_tread_singlesample_AOTF_digital = execute_tread_singlesample_digital()
            # execute_tread_singlesample_AOTF_digital.set_waves('640blanking', switch)
            # execute_tread_singlesample_AOTF_digital.start()

        else:
            print(wavelength + ':' + str(value))
            print(str(switch))
            daq.sendSingleAnalog('488AO', value)

            daq.sendSingleDigital('640blanking', switch)
class PatchclampSealTestUI(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.saving_dir = r'M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Patch clamp\seal_test'

        #------------------------Initiating patchclamp class-------------------
        self.sealTest = PatchclampSealTest()
        self.sealTest.measurementThread.measurement.connect(
            self.handleMeasurement)  #Connecting to the measurement signal

        self.holdTest = PatchclampSealTest_hold()
        self.holdTest.measurementThread_hold.measurement.connect(
            self.handleMeasurement)  #Connecting to the measurement signal

        self.currentclampTest = PatchclampSealTest_currentclamp()
        self.currentclampTest.measurementThread_currentclamp.measurement.connect(
            self.handleMeasurement)  #Connecting to the measurement signal

        self.zapfunction = PatchclampSealTest_zap()
        #----------------------------------------------------------------------
        #----------------------------------GUI---------------------------------
        #----------------------------------------------------------------------
        self.setFixedHeight(700)
        self.setWindowTitle("Patchclamp Seal Test")

        self.ICON_RED_LED = "./Icons/off.png"
        self.ICON_GREEN_LED = '/Icons/on.png'
        self.is_sealtesting = False

        #------------------------------Gains-----------------------------------
        gainContainer = StylishQT.roundQGroupBox(title="Gains")
        # gainContainer.setFixedWidth(320)
        gainLayout = QGridLayout()

        gainLayout.addWidget(QLabel("Input Voltage"), 0, 0)
        self.inVolGainList = QComboBox()
        self.inVolGainList.addItems(['1/10', '1', '1/50'])
        gainLayout.addWidget(self.inVolGainList, 1, 0)

        gainLayout.addWidget(QLabel("Output Voltage"), 0, 1)
        self.outVolGainList = QComboBox()
        self.outVolGainList.addItems(['10', '2', '5', '1', '20', '50', '100'])
        gainLayout.addWidget(self.outVolGainList, 1, 1)

        gainLayout.addWidget(QLabel("Output Current"), 0, 2)
        self.outCurGainList = QComboBox()
        self.outCurGainList.addItems(['1', '2', '5', '10', '20', '50', '100'])
        gainLayout.addWidget(self.outCurGainList, 1, 2)

        gainLayout.addWidget(QLabel("Probe"), 0, 3)
        self.probeGainList = QComboBox()
        self.probeGainList.addItems(['100M\u03A9', '10G\u03A9'])
        gainLayout.addWidget(self.probeGainList, 1, 3)

        gainContainer.setLayout(gainLayout)
        #------------------------------Wavesettings-----------------------------------
        WavesettingsContainer = StylishQT.roundQGroupBox(title="Wave settings")
        # WavesettingsContainer.setFixedWidth(320)
        WavesettingsContainerLayout = QGridLayout()

        WavesettingsContainerLayout.addWidget(QLabel("Voltage step(mV)"), 0, 0)
        self.DiffVoltagebox = QSpinBox(self)
        self.DiffVoltagebox.setMaximum(2000)
        self.DiffVoltagebox.setMinimum(-2000)
        self.DiffVoltagebox.setValue(10)
        self.DiffVoltagebox.setSingleStep(10)
        WavesettingsContainerLayout.addWidget(self.DiffVoltagebox, 0, 1)

        WavesettingsContainerLayout.addWidget(QLabel("Voltage baseline(mV)"),
                                              0, 2)
        self.LowerVoltagebox = QSpinBox(self)
        self.LowerVoltagebox.setMaximum(2000)
        self.LowerVoltagebox.setMinimum(-2000)
        self.LowerVoltagebox.setValue(0)
        self.LowerVoltagebox.setSingleStep(30)
        WavesettingsContainerLayout.addWidget(self.LowerVoltagebox, 0, 3)

        WavesettingsContainer.setLayout(WavesettingsContainerLayout)
        #------------------------------Membrane potential-----------------------------------
        Vm_measureContainer = StylishQT.roundQGroupBox(title="Vm measurement")
        # Vm_measureContainer.setFixedWidth(320)
        Vm_measureContainerLayout = QGridLayout()

        Vm_measureContainerLayout.addWidget(QLabel("Clamping current(pA)"), 0,
                                            0)
        self.clampingcurrentbox = QSpinBox(self)
        self.clampingcurrentbox.setMaximum(2000)
        self.clampingcurrentbox.setMinimum(-2000)
        self.clampingcurrentbox.setValue(0)
        self.clampingcurrentbox.setSingleStep(100)
        Vm_measureContainerLayout.addWidget(self.clampingcurrentbox, 0, 1)

        self.membraneVoltLabel = QLabel("Vm: ")
        Vm_measureContainerLayout.addWidget(self.membraneVoltLabel, 0, 2)

        self.VmstartButton = StylishQT.runButton()
        self.VmstartButton.clicked.connect(lambda: self.measure_currentclamp())
        Vm_measureContainerLayout.addWidget(self.VmstartButton, 0, 3)

        self.VmstopButton = StylishQT.stop_deleteButton()
        self.VmstopButton.setEnabled(False)
        self.VmstopButton.clicked.connect(
            lambda: self.stopMeasurement_currentclamp())
        Vm_measureContainerLayout.addWidget(self.VmstopButton, 0, 4)

        Vm_measureContainer.setLayout(Vm_measureContainerLayout)
        #------------------------------zap-----------------------------------
        zapContainer = StylishQT.roundQGroupBox(title="ZAP")
        # zapContainer.setFixedWidth(320)
        zapContainerLayout = QGridLayout()

        self.ICON_zap = './Icons/zap.jpg'
        self.zapiconlabel = QLabel()
        self.zapiconlabel.setPixmap(QPixmap(self.ICON_zap))
        zapContainerLayout.addWidget(self.zapiconlabel, 0, 0)

        self.zapButton = QPushButton("ZAP!")
        self.zapButton.setStyleSheet(
            "QPushButton {color:white;background-color: blue; border-style: outset;border-radius: 10px;border-width: 2px;font: bold 14px;padding: 6px}"
            "QPushButton:pressed {color:black;background-color: red; border-style: outset;border-radius: 10px;border-width: 2px;font: bold 14px;padding: 6px}"
            "QPushButton:hover:!pressed {color:blue;background-color: white; border-style: outset;border-radius: 10px;border-width: 2px;font: bold 14px;padding: 6px}"
        )
        self.zapButton.setShortcut('z')
        zapContainerLayout.addWidget(self.zapButton, 0, 1)
        self.zapButton.clicked.connect(self.zap)

        zapContainerLayout.addWidget(QLabel("ZAP voltage(V)"), 0, 2)
        self.zapVbox = QDoubleSpinBox(self)
        self.zapVbox.setMaximum(1)
        self.zapVbox.setMinimum(-2000)
        self.zapVbox.setValue(1)
        self.zapVbox.setSingleStep(0.1)
        zapContainerLayout.addWidget(self.zapVbox, 0, 3)

        zapContainerLayout.addWidget(QLabel("ZAP duration(us)"), 0, 4)
        self.zaptimebox = QSpinBox(self)
        self.zaptimebox.setMaximum(200000000)
        self.zaptimebox.setMinimum(-200000000)
        self.zaptimebox.setValue(200)
        self.zaptimebox.setSingleStep(200)
        zapContainerLayout.addWidget(self.zaptimebox, 0, 5)

        zapContainer.setLayout(zapContainerLayout)
        #----------------------------Control-----------------------------------
        controlContainer = StylishQT.roundQGroupBox(title="Control")
        controlContainer.setFixedWidth(350)
        controlLayout = QGridLayout()

        #        controlLayout.addWidget(QLabel("Holding Vm:"), 1, 0)
        #        self.HoldingList = QComboBox()
        #        self.HoldingList.addItems(['000 mV', '-30 mV', '-50 mV', '-40 mV', '-60 mV'])
        #        controlLayout.addWidget(self.HoldingList, 2, 0)
        #
        #        self.iconlabel = QLabel(self)
        #        self.iconlabel.setPixmap(QPixmap(self.ICON_RED_LED))
        #        controlLayout.addWidget(self.iconlabel, 1, 1)
        #
        #        self.holdingbutton = QPushButton("Hold")
        #        self.holdingbutton.setCheckable(True)
        #        #self.holdingbutton.toggle()
        #
        #        self.holdingbutton.clicked.connect(lambda: self.btnstate())
        #        #self.holdingbutton.clicked.connect(lambda: self.hold())
        #        #self.holdingbutton.clicked.connect(self.setGreenlight)
        #        controlLayout.addWidget(self.holdingbutton, 2, 1)

        #        self.stopandholdbutton = QPushButton("Stop and Hold")
        #        self.stopandholdbutton.clicked.connect(lambda: self.stopMeasurement())
        #        self.stopandholdbutton.clicked.connect(lambda: self.measure_hold())
        #        self.stopandholdbutton.clicked.connect(self.setGreenlight)
        #        controlLayout.addWidget(self.stopandholdbutton, 0, 2)
        '''
        self.stopholdingbutton = QPushButton("Stop holding")
        self.stopholdingbutton.clicked.connect(lambda: self.stophold())
        self.stopholdingbutton.clicked.connect(self.setRedlight)        
        controlLayout.addWidget(self.stopholdingbutton, 2, 2)        
        '''
        controlLayout.addWidget(gainContainer, 0, 0, 1, 2)
        controlLayout.addWidget(WavesettingsContainer, 1, 0, 1, 2)
        controlLayout.addWidget(Vm_measureContainer, 2, 0, 1, 2)
        controlLayout.addWidget(zapContainer, 3, 0, 1, 2)

        self.startButton = StylishQT.runButton("Start seal-test")
        self.startButton.clicked.connect(lambda: self.measure())
        # self.startButton.setFixedWidth(120)
        # self.startButton.clicked.connect(self.setRedlight)
        # self.startButton.clicked.connect(self.startUpdatingGUIThread)
        controlLayout.addWidget(self.startButton, 4, 0, 1, 1)

        self.stopButton = StylishQT.stop_deleteButton()
        self.stopButton.setEnabled(False)
        # self.stopButton.setFixedWidth(120)
        self.stopButton.clicked.connect(lambda: self.stopMeasurement())
        controlLayout.addWidget(self.stopButton, 4, 1, 1, 1)

        controlContainer.setLayout(controlLayout)

        #-----------------------------Plots------------------------------------
        plotContainer = StylishQT.roundQGroupBox(title="Output")
        plotContainer.setFixedWidth(350)
        self.plotLayout = QGridLayout(
        )  #We set the plotLayout as an attribute of the object (i.e. self.plotLayout instead of plotLayout)
        #This is to prevent the garbage collector of the C++ wrapper from deleting the layout and thus triggering errors.
        #Derived from: https://stackoverflow.com/questions/17914960/pyqt-runtimeerror-wrapped-c-c-object-has-been-deleted
        # and http://enki-editor.org/2014/08/23/Pyqt_mem_mgmt.html

        # self.outVolPlotWidget = SlidingWindow(200, title = "Voltage", unit = "V") #Should be bigger than the readvalue
        self.outCurPlotWidget = SlidingWindow(
            200, title="Current",
            unit="A")  #Should be bigger than the readvalue

        self.display_tab_widget = QTabWidget()

        # self.plotLayout.addWidget(QLabel('Voltage (mV):'), 0, 0)
        self.display_tab_widget.addTab(self.outCurPlotWidget, "Current")
        # self.plotLayout.addWidget(self.outVolPlotWidget, 1, 0)
        # self.plotLayout.addWidget(QLabel('Current (pA):'), 0, 1)
        # self.display_tab_widget.addTab(self.outVolPlotWidget, "Voltage")
        # self.plotLayout.addWidget(self.outCurPlotWidget, 1, 1)

        valueContainer = QGroupBox("Resistance/Capacitance")
        self.valueLayout = QGridLayout()
        self.resistanceLabel = QLabel("Resistance: ")
        self.capacitanceLabel = QLabel("Capacitance: ")
        self.ratioLabel = QLabel("Ratio: ")
        self.valueLayout.addWidget(self.resistanceLabel, 0, 0)
        self.valueLayout.addWidget(self.capacitanceLabel, 0, 1)
        self.valueLayout.addWidget(self.ratioLabel, 0, 2)

        self.pipette_resistance = QLineEdit(self)
        self.pipette_resistance.setPlaceholderText('Pipette resistance')
        self.pipette_resistance.setFixedWidth(100)
        self.valueLayout.addWidget(self.pipette_resistance, 1, 0)

        self.savedataButton = QPushButton("Save figure")
        self.savedataButton.clicked.connect(lambda: self.savePatchfigure())
        self.valueLayout.addWidget(self.savedataButton, 1, 1)

        self.resetButton = QPushButton("Reset Iplot")
        self.resetButton.clicked.connect(lambda: self.ResetCurrentImgView())
        self.valueLayout.addWidget(self.resetButton, 1, 2)

        valueContainer.setLayout(self.valueLayout)
        self.plotLayout.addWidget(self.display_tab_widget, 0, 0, 1, 1)
        self.plotLayout.addWidget(valueContainer, 2, 0, 1, 1)

        plotContainer.setLayout(self.plotLayout)
        #---------------------------Adding to master---------------------------
        master = QVBoxLayout()
        master.addWidget(controlContainer)
        master.addWidget(plotContainer)

        self.setLayout(master)

        #--------------------------Setting variables---------------------------
        self.changeVolInGain(self.inVolGainList.currentText())
        self.changeVolOutGain(self.outVolGainList.currentText())
        self.changeCurOutGain(self.outCurGainList.currentText())
        self.changeProbeGain(self.probeGainList.currentText())

        self.inVolGainList.currentIndexChanged.connect(
            lambda: self.changeVolInGain(self.inVolGainList.currentText()))
        self.outVolGainList.currentIndexChanged.connect(
            lambda: self.changeVolOutGain(self.outVolGainList.currentText()))
        self.outCurGainList.currentIndexChanged.connect(
            lambda: self.changeCurOutGain(self.outCurGainList.currentText()))
        self.probeGainList.currentIndexChanged.connect(
            lambda: self.changeProbeGain(self.probeGainList.currentText()))

    def ResetCurrentImgView(self):
        """Closes the widget nicely, making sure to clear the graphics scene and release memory."""
        self.outCurPlotWidget.close()
        # self.outVolPlotWidget.close()

        # Replot the imageview
        self.outCurPlotWidget = SlidingWindow(200, title="Current", unit="A")

        self.display_tab_widget.addTab(self.outCurPlotWidget, "Current")
        # self.display_tab_widget.addTab(self.outVolPlotWidget, "Voltage")

    def measure(self):
        """Pop up window asking to check the gains.
        Returns 
        True if the measurement can be done
        and 
        False if not.
        """
        #check = QMessageBox.question(self, 'GAINS!', "Are all the gains corresponding?",
        #QMessageBox.Yes | QMessageBox.No)

        #if check == QMessageBox.Yes:
        """Start the patchclamp measurement"""
        self.stopButton.setEnabled(True)
        self.startButton.setEnabled(False)

        self.diffvoltage = self.DiffVoltagebox.value() / 1000
        self.lowervoltage = self.LowerVoltagebox.value() / 1000
        self.sealTest.setWave(self.inVolGain, self.diffvoltage,
                              self.lowervoltage)
        self.sealTest.start()
        self.is_sealtesting = True
        self.startUpdatingGUIThread()

    def measure_hold(self):
        """Pop up window asking to check the gains.
        Returns 
        True if the measurement can be done
        and 
        False if not.
        """
        self.holdTest.setWave(self.inVolGain,
                              float(self.HoldingList.currentText()[0:3]))
        self.holdTest.start()
        self.is_sealtesting = True

    def measure_currentclamp(self):
        """Pop up window asking to check the gains.
        Returns 
        True if the measurement can be done
        and 
        False if not.
        """
        #check = QMessageBox.question(self, 'GAINS!', "Are all the gains corresponding?",
        #QMessageBox.Yes | QMessageBox.No)

        #if check == QMessageBox.Yes:
        """Start the patchclamp measurement"""
        self.VmstartButton.setEnabled(False)
        self.VmstopButton.setEnabled(True)

        self.currentclamp_value = self.clampingcurrentbox.value()
        self.currentclampTest.setWave(self.inVolGain, self.probeGain,
                                      self.currentclamp_value)
        self.currentclampTest.start()
        self.is_sealtesting = True

    def hold(self):
        constant = float(self.HoldingList.currentText()[0:3])
        self.executer = DAQmission()
        self.executer.sendSingleAnalog('patchAO', constant / 1000 * 10)
        print("Holding vm at " + str(constant) + ' mV')

    def stophold(self):
        self.executer.sendSingleAnalog('patchAO', 0)
        print('Stop holding')

    def btnstate(self):
        #source = self.sender()
        if self.holdingbutton.isChecked():
            self.measure_hold()
            self.setGreenlight()
        else:
            self.stop_hold_Measurement()
            self.setRedlight()

    def setRedlight(self):
        self.iconlabel.setPixmap(QPixmap(self.ICON_RED_LED))

    def setGreenlight(self):
        self.iconlabel.setPixmap(QPixmap(self.ICON_GREEN_LED))

    def startUpdatingGUIThread(self):
        time.sleep(0.3)
        StartGUIThread = threading.Thread(target=self.startUpdatingGUI)
        StartGUIThread.start()


#        else:
#            .disconnect()

    def handleMeasurement(self, voltOut, curOut):
        """Handle the measurement. Update the graph."""

        #Rescaling using gains
        self.voltOut = voltOut / self.outVolGain
        self.curOut = curOut / self.outCurGain / self.probeGain

    def startUpdatingGUI(self):
        while self.is_sealtesting == True:
            try:
                # self.outVolPlotWidget.append_(self.voltOut)
                self.outCurPlotWidget.append_(self.curOut)
                self.updateGraphs()
                self.updateLabels(self.curOut, self.voltOut)
                time.sleep(0.05)
            except:
                pass

    def updateGraphs(self):
        """Update graphs."""
        self.outCurPlotWidget.updateWindow()
        # self.outVolPlotWidget.updateWindow()

    def updateLabels(self, curOut, voltOut):
        """Update the resistance and capacitance labels.
        http://scipy-lectures.org/intro/scipy/auto_examples/plot_curve_fit.html
        https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html"""
        constants = MeasurementConstants()
        sampPerCyc = int(constants.patchSealSampRate / constants.patchSealFreq)

        try:
            curOutCyc = curOut.reshape(int(curOut.size / sampPerCyc),
                                       sampPerCyc)
            curData = np.mean(curOutCyc, axis=0)
        except:
            curData = curOut

        voltData = voltOut
        try:
            #Computing resistance
            tres = np.mean(voltData)
            dV = np.mean(voltData[voltData > tres]) - np.mean(
                voltData[voltData < tres])  #Computing the voltage difference
            dIss = np.mean(
                curData[math.floor(0.15 *
                                   sampPerCyc):math.floor(sampPerCyc / 2) -
                        2]) - np.mean(
                            curData[math.floor(0.65 * sampPerCyc):sampPerCyc -
                                    2])  #Computing the current distance
            membraneResistance = dV / (dIss * 1000000)  #Ohms law (MegaOhm)
            self.resistanceLabel.setText("Resistance:  %.4f M\u03A9" %
                                         membraneResistance)

            self.estimated_size_resistance = 10000 / (
                membraneResistance * 1000000
            )  #The resistance of a typical patch of membrane, RM is 10000 Omega/{cm}^2
        except:
            self.resistanceLabel.setText("Resistance:  %s" % 'NaN')

        try:
            measured_vlotage = np.mean(voltData) * 1000
            self.membraneVoltLabel.setText("Vm:  %.2f mV" % measured_vlotage)
            self.membraneVoltLabel.setStyleSheet('color: red')
        except:
            self.membraneVoltLabel.setText("Vm:  %s" % 'NaN')
        try:
            #Computing capacitance
            points = 10
            maxCur = np.amax(curData)
            maxCurIndex = np.where(curData == maxCur)[0][0]
            curFit = curData[int(maxCurIndex + 1):int(
                maxCurIndex + 1 + points - 1)] - 0.5 * (np.mean(curData[
                    math.floor(0.15 * sampPerCyc):math.floor(sampPerCyc / 2) -
                    2]) + np.mean(
                        curData[math.floor(0.65 * sampPerCyc):sampPerCyc - 2]))
            timepoints = 1000 * np.arange(
                3, points - 1 + 3) / constants.patchSealSampRate
            #Fitting the data to an exponential of the form y=a*exp(-b*x) where b = 1/tau and tau = RC
            #I(t)=I0*e^−t/τ, y=a*exp(-b*x), get log of both sides:log y = -bx + log a
            fit = np.polyfit(
                timepoints, curFit, 1
            )  #Converting the exponential to a linear function and fitting it
            #Extracting data
            current = fit[0]
            resistance = dV * 1000 / current / 2  #Getting the resistance
            tau = -1 / fit[1]
            capacitance = 1000 * tau / resistance
            self.capacitanceLabel.setText("Capacitance:  %.4f" % capacitance)

            self.estimated_size_capacitance = capacitance * (10**-12) * (10**6)

            if self.estimated_size_capacitance > self.estimated_size_resistance:
                self.estimated_ratio = self.estimated_size_capacitance / self.estimated_size_resistance
            else:
                self.estimated_ratio = self.estimated_size_resistance / self.estimated_size_capacitance

            self.ratioLabel.setText(
                "Ratio:  %.4f" % self.estimated_ratio
            )  #http://www.cnbc.cmu.edu/~bard/passive2/node5.html

        except:
            self.capacitanceLabel.setText("Capacitance:  %s" % 'NaN')
            self.ratioLabel.setText("Ratio:  %s" % 'NaN')

        self.patch_parameters = "R_{}_C_{}".format(
            round(membraneResistance, 3), round(capacitance, 3))

    def stopMeasurement(self):
        """Stop the seal test."""
        self.stopButton.setEnabled(False)
        self.startButton.setEnabled(True)
        self.sealTest.aboutToQuitHandler()
        self.is_sealtesting = False
        #constant = float(self.HoldingList.currentText()[0:3])
        #self.executer = execute_constant_vpatch(constant/1000*10)
        #print("Holding vm at "+str(constant)+' mV')

    '''        
    def closeEvent(self, event):
        """On closing the application we have to make sure that the measuremnt
        stops and the device gets freed."""
        self.stopMeasurement()
    '''

    def stop_hold_Measurement(self):
        """Stop the seal test."""
        self.holdTest.aboutToQuitHandler()
        self.is_sealtesting = False

    def close_hold_Event(self, event):
        """On closing the application we have to make sure that the measuremnt
        stops and the device gets freed."""
        self.stop_hold_Measurement()

    def stopMeasurement_currentclamp(self):
        """Stop the seal test."""
        self.VmstartButton.setEnabled(True)
        self.VmstopButton.setEnabled(False)

        self.currentclampTest.aboutToQuitHandler()
        self.is_sealtesting = False

    #Change gain
    def changeVolInGain(self, gain):
        if gain == '1':
            self.inVolGain = 1
        elif gain == '1/10':
            self.inVolGain = 0.1
        elif gain == '1/50':
            self.inVolGain = 1. / 50

    def changeVolOutGain(self, gain):
        self.outVolGain = float(gain)

    def changeCurOutGain(self, gain):
        self.outCurGain = float(gain)

    def changeProbeGain(self, gain):
        if gain == '100M\u03A9':
            self.probeGain = 100 * 10**6
        elif gain == '10G\u03A9':
            self.probeGain = 10 * 10**9

    def zap(self):
        self.zap_v = self.zapVbox.value()
        self.zap_time = self.zaptimebox.value()
        self.sealTest.aboutToQuitHandler()
        self.zapfunction.setWave(self.inVolGain, self.zap_v, self.zap_time)
        self.zapfunction.start()
        time.sleep(0.06)
        self.zapfunction.aboutToQuitHandler()
        """Start the patchclamp measurement"""
        self.diffvoltage = self.DiffVoltagebox.value() / 1000
        self.lowervoltage = self.LowerVoltagebox.value() / 1000
        self.sealTest.setWave(self.inVolGain, self.diffvoltage,
                              self.lowervoltage)
        self.sealTest.start()

    def savePatchfigure(self):
        # create an exporter instance, as an argument give it
        # the item you wish to export
        exporter = pg.exporters.ImageExporter(
            self.outCurPlotWidget.getPlotItem())

        # set export parameters if needed
        exporter.parameters(
        )['width'] = 500  # (note this also affects height parameter)

        # save to file
        exporter.export(
            os.path.join(
                self.saving_dir,
                'SealTest_' + "Rpip_" + self.pipette_resistance.text() +
                "Mohm_" + self.patch_parameters + '.png'))
Beispiel #8
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
    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