Esempio n. 1
0
def errorSurface(axes=[3, 0],
                 v1=None,
                 v2=None,
                 bounds=None,
                 noise=0.0,
                 n=5000):
    ## compute sum of squares error between two templates over a range of differences in v
    ## the two templates are generated from the parameters in v1 and v2
    ## the error surface is computed by varying v2[axis[n]] from bounds[axis[n]][0] to bounds[axis[n]][1] on
    ## each axis of the surface.

    ## displays and returns the error surface,
    ## also returns an array of all the v2 parameters used for each point in the surface.

    x = np.linspace(0, 0.5, 5000)
    v = [1.0, 0.05, 0.05, 0.1]  ## defaults used if v1 / v2 are not given
    if v1 is None:
        v1 = v[:]
    if v2 is None:
        v2 = v1[:]

    if bounds is None:
        bounds = [(0.0, 2.0), (0.0, 0.1), (0.01, 0.1), (0.01, 0.5)]

    template1 = pspFunc(v1, x) + np.random.normal(size=len(x), scale=noise)

    ## number of iterations per axis
    n = int(n**(1.0 / len(axes)))

    axv = []
    for ax in axes:
        axv.append(np.linspace(bounds[ax][0], bounds[ax][1], n))

    err = np.empty((n, ) * len(axes))
    vals = np.empty(err.shape, dtype=object)

    inds = np.indices(err.shape).reshape((len(axes), err.size))

    for i in xrange(inds.shape[1]):
        ind = tuple(inds[:, i])
        v2a = v2[:]
        for j in range(len(axes)):
            v2a[axes[j]] = axv[j][ind[j]]
        template2 = pspFunc(v2a, x)
        err[ind] = np.sum((template2 - template1)**2)
        vals[ind] = v2a

    if len(axes) == 2:
        p = pg.plot()
        img = pg.ImageItem(err)
        p.addItem(img)
        b1 = bounds[axes[0]]
        b2 = bounds[axes[1]]
        img.setRect(QtCore.QRectF(b1[0], b2[0], b1[1] - b1[0], b2[1] - b2[0]))
    elif len(axes) == 3:
        pg.image(err)

    return err, vals
Esempio n. 2
0
def errorSurface(axes=[3, 0], v1=None, v2=None, bounds=None, noise=0.0, n=5000):
    ## compute sum of squares error between two templates over a range of differences in v
    ## the two templates are generated from the parameters in v1 and v2
    ## the error surface is computed by varying v2[axis[n]] from bounds[axis[n]][0] to bounds[axis[n]][1] on 
    ## each axis of the surface.
    
    ## displays and returns the error surface, 
    ## also returns an array of all the v2 parameters used for each point in the surface.
    
    x = np.linspace(0, 0.5, 5000)
    v = [1.0, 0.05, 0.05, 0.1]  ## defaults used if v1 / v2 are not given
    if v1 is None:
        v1 = v[:]
    if v2 is None:
        v2 = v1[:]
    
    if bounds is None:
        bounds = [(0.0, 2.0), (0.0, 0.1), (0.01, 0.1), (0.01, 0.5)]
        
    template1 = pspFunc(v1, x) + np.random.normal(size=len(x), scale=noise)
    
    ## number of iterations per axis
    n = int(n**(1.0/len(axes)))
    
    axv = []
    for ax in axes:
        axv.append(np.linspace(bounds[ax][0], bounds[ax][1], n))
        
    err = np.empty((n,)*len(axes))
    vals = np.empty(err.shape, dtype=object)
    
    inds = np.indices(err.shape).reshape((len(axes), err.size))
    
    for i in xrange(inds.shape[1]):
        ind = tuple(inds[:,i])
        v2a = v2[:]
        for j in range(len(axes)):
            v2a[axes[j]] = axv[j][ind[j]]
        template2 = pspFunc(v2a, x)
        err[ind] = np.sum((template2-template1)**2)
        vals[ind] = v2a
            
    if len(axes) == 2:
        p = pg.plot()
        img = pg.ImageItem(err)
        p.addItem(img)
        b1 = bounds[axes[0]]
        b2 = bounds[axes[1]]
        img.setRect(QtCore.QRectF(b1[0], b2[0], b1[1]-b1[0], b2[1]-b2[0]))
    elif len(axes) == 3:
        pg.image(err)
        
    return err, vals
Esempio n. 3
0
    def _matchTemplateSingle(self, img, template, show=False, unsharp=3):
        import skimage.feature
        if img.shape[0] < template.shape[0] or img.shape[1] < template.shape[1]:
            raise ValueError("Image must be larger than template.  %s %s" % (img.shape, template.shape))
        cc = skimage.feature.match_template(img, template)
        # high-pass filter; we're looking for a fairly sharp peak.
        if unsharp is not False:
            cc_filt = cc - scipy.ndimage.gaussian_filter(cc, (unsharp, unsharp))
        else:
            cc_filt = cc

        if show:
            pg.image(cc)

        ind = np.argmax(cc_filt)
        pos = np.unravel_index(ind, cc.shape)
        val = cc[pos[0], pos[1]]
        return pos, val
Esempio n. 4
0
    def showErrorAnalysis(self):
        if not hasattr(self, 'errorMap'):
            filename = self.dev.configFileName('error_map.np')
            self.errorMap = np.load(open(filename, 'rb'))[np.newaxis][0]

        err = self.errorMap
        imx = pg.image(err['err'][..., 0].transpose(1, 0, 2), title='X error')
        imy = pg.image(err['err'][..., 1], title='Y error')
        imz = pg.image(err['err'][..., 2], title='Z error')

        # get N,3 array of offset values used to randomize hysteresis
        off = np.vstack(err['offsets'])
        sh = err['err'].shape

        # Get N,3 array of measured position errors
        errf = err['err'].reshape(sh[0] * sh[1] * sh[2], 3)[err['order']]

        # Display histogram of errors
        win = pg.GraphicsWindow(title="%s error" % self.dev.name())
        # subtract out slow drift
        normErr = errf - scipy.ndimage.gaussian_filter(errf, (20, 0))
        # calculate magnitude of error
        absErr = (normErr**2).sum(axis=1)**0.5
        # errPlot.plot(absErr)
        title = "Error Histogram (mean=%s)" % pg.siFormat(absErr.mean(),
                                                          suffix='m')
        errPlot = win.addPlot(row=0,
                              col=0,
                              title=title,
                              labels={'bottom': ('Position error', 'm')})
        hist = np.histogram(absErr, bins=50)
        errPlot.plot(hist[1], hist[0], stepMode=True)

        # display drift and hysteresis plots
        driftPlot = win.addPlot(row=0,
                                col=1,
                                rowspan=1,
                                colspan=2,
                                title="Pipette Drift",
                                labels={
                                    'left': ('Position error', 'm'),
                                    'bottom': ('Time', 's')
                                })
        driftPlot.plot(np.linspace(0, err['time'], errf.shape[0]),
                       errf[:, 0],
                       pen='r')
        driftPlot.plot(np.linspace(0, err['time'], errf.shape[0]),
                       errf[:, 1],
                       pen='g')
        driftPlot.plot(np.linspace(0, err['time'], errf.shape[0]),
                       errf[:, 2],
                       pen='b')

        xhplot = win.addPlot(row=1,
                             col=0,
                             title='X Hysteresis',
                             labels={
                                 'left': ('Position error', 'm'),
                                 'bottom': ('Last pipette movement', 'm')
                             })
        xhplot.plot(-off[:, 0], errf[:, 0], pen=None, symbol='o')

        yhplot = win.addPlot(row=1,
                             col=1,
                             title='Y Hysteresis',
                             labels={
                                 'left': ('Position error', 'm'),
                                 'bottom': ('Last pipette movement', 'm')
                             })
        yhplot.plot(-off[:, 1], errf[:, 1], pen=None, symbol='o')

        zhplot = win.addPlot(row=1,
                             col=2,
                             title='Z Hysteresis',
                             labels={
                                 'left': ('Position error', 'm'),
                                 'bottom': ('Last pipette movement', 'm')
                             })
        zhplot.plot(-off[:, 2], errf[:, 2], pen=None, symbol='o')

        # Print best fit for manipulator axes
        expPos = err['inds'] * err['stepSize']
        measPos = expPos + off
        guess = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]],
                         dtype='float')

        def errFn(v):
            return ((measPos -
                     np.dot(expPos, v.reshape(3, 4))[:, :3])**2).sum()

        fit = scipy.optimize.minimize(errFn, guess)
        print("Pipette position transform:", fit)

        self.errorMapAnalysis = (imx, imy, imz, win)
Esempio n. 5
0
    def mapErrors(self,
                  nSteps=(5, 5, 7),
                  stepSize=(50e-6, 50e-6, 50e-6),
                  padding=60e-6,
                  threshold=0.4,
                  speed='slow',
                  show=False,
                  intermediateDist=60e-6):
        """Move pipette tip randomly to locations in a grid and measure the position error
        at each location.

        All tip locations must be within the field of view.
        """
        startTime = time.time()
        start = np.array(self.dev.globalPosition())
        npts = nSteps[0] * nSteps[1] * nSteps[2]
        inds = np.mgrid[0:nSteps[0], 0:nSteps[1], 0:nSteps[2]].reshape(
            (3, npts)).transpose()
        order = np.arange(npts)
        np.random.shuffle(order)

        err = np.zeros(nSteps + (3, ))

        stepSize = np.array(stepSize)

        if show:
            imv = pg.image()
            mark1 = pg.QtGui.QGraphicsEllipseItem(
                pg.QtCore.QRectF(-5, -5, 10, 10))
            mark1.setBrush(pg.mkBrush(255, 255, 0, 100))
            mark1.setZValue(100)
            imv.addItem(mark1)
            mark2 = pg.QtGui.QGraphicsEllipseItem(
                pg.QtCore.QRectF(-5, -5, 10, 10))
            mark2.setBrush(pg.mkBrush(255, 0, 0, 100))
            mark2.setZValue(100)
            imv.addItem(mark2)

        # loop over all points in random order, and such that we do heavy computation while
        # pipette is moving.
        images = []
        offsets = []
        try:
            with pg.ProgressDialog("Acquiring error map...", 0,
                                   len(order)) as dlg:
                for i in range(len(order) + 1):
                    if i > 0:
                        lastPos = pos
                    if i < len(order):
                        ind = inds[order[i]]
                        pos = start.copy() + (stepSize * ind)

                        # Jump to position + a random 20um offset to avoid hysteresis
                        offset = np.random.normal(size=3)
                        offset *= intermediateDist / (offset**2).sum()**0.5
                        offsets.append(offset)

                        mfut = self.dev._moveToGlobal(pos + offset, speed)
                        ffut = self.dev.scopeDevice().setFocusDepth(
                            pos[2], speed)
                    if i > 0:
                        ind = inds[order[i - 1]]

                        print("Frame: %d %s" % (i - 1, lastPos))
                        err[tuple(ind)] = self.measureError(
                            padding=padding,
                            threshold=threshold,
                            frame=frame,
                            pos=lastPos)
                        print("    error: %s" % err[tuple(ind)])
                        dlg += 1

                        if show:
                            imv.setImage(frame.data()[0])
                            p1 = frame.globalTransform().inverted()[0].map(
                                pg.Vector(lastPos))
                            p2 = frame.globalTransform().inverted()[0].map(
                                pg.Vector(lastPos + err[tuple(ind)]))
                            mark1.setPos(p1.x(), p1.y())
                            mark2.setPos(p2.x(), p2.y())

                    # wait for previous moves to complete
                    mfut.wait(updates=True)
                    ffut.wait(updates=True)

                    # step back to actual target position
                    self.dev._moveToGlobal(pos, speed).wait(updates=True)

                    frame = self.takeFrame()

                    if dlg.wasCanceled():
                        return None
        finally:
            self.dev._moveToGlobal(start, 'fast')
            self.dev.scopeDevice().setFocusDepth(start[2], 'fast')

        self.errorMap = {
            'err': err,
            'nSteps': nSteps,
            'stepSize': stepSize,
            'order': order,
            'inds': inds,
            'offsets': offsets,
            'time': time.time() - startTime,
        }

        filename = self.dev.configFileName('error_map.np')
        np.save(open(filename, 'wb'), self.errorMap)

        return self.errorMap