Пример #1
0
def addLabel(info=None):
    global labelInfo
    create = False
    if info is None:
        create = True
        l = ui.labelSpin.value()
        if l in labelInfo:
            return
        info = {
            'visible': True,
            'name': 'label',
            'color': pg.intColor(len(labelInfo), 16),
            'id': l
        }
    else:
        info = info.copy()
        info['color'] = pg.mkColor(info['color'])

    l = info['id']
    item = Qt.QTreeWidgetItem([str(l), info['name'], ''])
    item.setFlags(item.flags() | Qt.Qt.ItemIsEditable
                  | Qt.Qt.ItemIsUserCheckable)
    if info['visible']:
        item.setCheckState(0, Qt.Qt.Checked)
    else:
        item.setCheckState(0, Qt.Qt.Unchecked)
    btn = pg.ColorButton(color=info['color'])
    ui.labelTree.addTopLevelItem(item)
    ui.labelTree.setItemWidget(item, 2, btn)
    labelInfo[l] = {'item': item, 'btn': btn}
    btn.sigColorChanged.connect(itemChanged)
    btn.sigColorChanging.connect(imageChanged)

    if create:
        writeMeta()
Пример #2
0
    def addROI(self, roiType):
        pen = pg.mkPen(pg.intColor(len(self.ROIs)))
        center = self.view.viewRect().center()
        #print 'camerawindow.py: addROI:: ', self.view.viewPixelSize()
        size = [x * 50 for x in self.view.viewPixelSize()]
        if roiType == 'rect':
            roi = PlotROI(center, size)
        elif roiType == 'ellipse':
            roi = pg.EllipseROI(center, size, removable=True)
        elif roiType == 'polygon':
            pts = [
                center, center + pg.Point(0, size[1]),
                center + pg.Point(size[0], 0)
            ]
            roi = pg.PolyLineROI(pts, closed=True, removable=True)
        elif roiType == 'ruler':
            pts = [center, center + pg.Point(size[0], size[1])]
            roi = RulerROI(pts, removable=True)
        else:
            raise ValueError("Invalid ROI type %s" % roiType)

        roi.setZValue(40000)
        roi.setPen(pen)
        self.view.addItem(roi)
        plot = self.roiPlot.plot(pen=pen)
        self.ROIs.append({'roi': roi, 'plot': plot, 'vals': [], 'times': []})
        roi.sigRemoveRequested.connect(self.removeROI)
Пример #3
0
def addLabel(info=None):
    global labelInfo
    create = False
    if info is None:
        create = True
        l = ui.labelSpin.value()
        if l in labelInfo:
            return
        info = {
            'visible': True,
            'name': 'label',
            'color': pg.intColor(len(labelInfo), 16),
            'id': l
        }
    else:
        info = info.copy()
        info['color'] = pg.mkColor(info['color'])
    
    l = info['id']
    item = QtGui.QTreeWidgetItem([str(l), info['name'], ''])
    item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable)
    if info['visible']:
        item.setCheckState(0, QtCore.Qt.Checked)
    else:
        item.setCheckState(0, QtCore.Qt.Unchecked)
    btn = pg.ColorButton(color=info['color'])
    ui.labelTree.addTopLevelItem(item)
    ui.labelTree.setItemWidget(item, 2, btn)
    labelInfo[l] = {'item': item, 'btn': btn}
    btn.sigColorChanged.connect(itemChanged)
    btn.sigColorChanging.connect(imageChanged)
    
    if create:
        writeMeta()
Пример #4
0
    def redisplayData(self, points):  ## data must be [(scan, fh, <event time>), ...]  
        #raise Exception('blah')
        #print points
        try:
            QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
            plot = self.getElement("Data Plot")
            plot.clear()
            eTable = self.getElement("Event Table")
            sTable = self.getElement("Stats")
            
            #num = len(point.data)
            num = len(points)
            statList = []
            evList = []
            for i in range(num):
                color = pg.intColor(i, num)
                #scan, fh = point.data[i]
                try:
                    scan, fh = points[i][:2]
                except:
                    print points[i]
                    raise
                
                if len(points[i]) == 3:
                    evTime = points[i][2]
                else:
                    evTime = None
                
                scan.displayData(fh, plot, color, evTime)
                
                ## show stats
                stats = scan.getStats(fh.parent())
                statList.append(stats)
                events = scan.getEvents(fh)['events']
                if len(events) > 0:
                    evList.append(events)

            
            sTable.setData(statList)
            if len(evList) > 0:
                try:
                    eTable.setData(np.concatenate(evList))
                except:
                    for i in range(1,len(evList)):
                        if len(evList[i].dtype) != len(evList[i-1].dtype):
                            print "Cannot concatenate; event lists have different dtypes:"
                            print evList[i].dtype
                            print evList[i-1].dtype
                        else:
                            for j in range(len(evList[i].dtype)):
                                if evList[i-1].dtype[j] != evList[i].dtype[j]:
                                    for l in evList:
                                        print l
                                    print "Warning: can not concatenate--field '%s' has inconsistent types %s, %s  (data printed above)" % (evList[i].dtype.names[j], str(evList[i-1].dtype[j]), str(evList[i].dtype[j]))
                    raise
        finally:
            QtGui.QApplication.restoreOverrideCursor()
Пример #5
0
 def addROI(self):
     pen = pg.mkPen(pg.intColor(len(self.ROIs)))
     center = self.view.viewRect().center()
     #print 'camerawindow.py: addROI:: ', self.view.viewPixelSize()
     size = [x*50 for x in self.view.viewPixelSize()]
     roi = PlotROI(center, size)
     roi.setZValue(40000)
     roi.setPen(pen)
     self.view.addItem(roi)
     plot = self.roiPlot.plot(pen=pen)
     self.ROIs.append({'roi': roi, 'plot': plot, 'vals': [], 'times': []})
     roi.sigRemoveRequested.connect(self.removeROI)
Пример #6
0
    def updateTracesPlot(self):
        """Update the Trace display plot to show the traces corresponding to
         the timestamps selected by the region in the experiment plot."""

        rgn = self.traceSelectRgn.getRegion()
        self.tracesPlot.clear()

        ### plot all the traces with timestamps within the selected region (according to self.traceSelectRgn)
        data = self.traces[(self.traces['timestamp'] > rgn[0] + self.expStart)
                           *
                           (self.traces['timestamp'] < rgn[1] + self.expStart)]

        for i, d in enumerate(data['data']):
            self.tracesPlot.plot(d['primary'], pen=pg.intColor(i, len(data)))

        if len(data) > 0:
            self.flowchart.setInput(dataIn=data[0]['fileHandle'])
Пример #7
0
def plotData():
    rate = 1e3
    nPts = 100
    plot.clear()

    params = {}
    paramSpace = sg.listSequences()
    for k in paramSpace:
        params[k] = range(len(paramSpace[k]))

    global seqPlots
    seqPlots = []
    SequenceRunner.runSequence(lambda p: seqPlots.append(sg.getSingle(rate, nPts, p)), params, params.keys())
    
    for i, w in enumerate(seqPlots):
        if w is not None:
            plot.plot(w, pen=pg.intColor(i, len(seqPlots)*1.5))

    data = sg.getSingle(rate, nPts)
    plot.plot(data, pen=pg.mkPen('w', width=2))
Пример #8
0
def plotData():
    rate = 1e3
    nPts = 100
    plot.clear()

    params = {}
    paramSpace = sg.listSequences()
    for k in paramSpace:
        params[k] = range(len(paramSpace[k]))

    global seqPlots
    seqPlots = []
    SequenceRunner.runSequence(
        lambda p: seqPlots.append(sg.getSingle(rate, nPts, p)), params,
        list(params.keys()))

    for i, w in enumerate(seqPlots):
        if w is not None:
            plot.plot(w, pen=pg.intColor(i, len(seqPlots) * 1.5))

    data = sg.getSingle(rate, nPts)
    plot.plot(data, pen=pg.mkPen('w', width=2))
Пример #9
0
    def _load_data(self, seqDir):
        man = getManager()
        model = man.dataModel

        # read all image data
        self.img_data = model.buildSequenceArray(
            seqDir, lambda dh: dh['Camera']['frames.ma'].read(),
            join=False).asarray()
        seqParams = list(model.listSequenceParams(seqDir).items())
        if self.img_data.ndim == 1:
            self.img_data = self.img_data[np.newaxis, :]
            seqParams.insert(0, (None, [0]))

        transpose = seqParams[0][0] == ('protocol', 'repetitions')
        if transpose:
            self.img_data = np.swapaxes(self.img_data, 0, 1)
            seqParams = seqParams[::-1]
        self.seqParams = seqParams

        if seqParams[0][0] is None:
            self.seqColors = [pg.mkColor('w')]
        else:
            nSeq = len(seqParams[0][1])
            if nSeq == 2:
                # Special case: add in difference between two sequence trials
                seqParams[0] = (seqParams[0][0], list(seqParams[0][1]) +
                                [np.mean(seqParams[0][1])])
                nSeq = 3
                img_data = np.empty((3, ) + self.img_data.shape[1:],
                                    dtype=self.img_data.dtype)
                img_data[:2] = self.img_data
                for i in range(img_data.shape[1]):
                    minlen = min(self.img_data[0, i].shape[0],
                                 self.img_data[1, i].shape[0])
                    img_data[2, i] = self.img_data[0, i][:minlen].copy()
                    img_data[2, i]._data = self.img_data[
                        0, i][:minlen].asarray().astype(
                            'float') - self.img_data[1, i][:minlen].asarray()
                self.img_data = img_data

            self.seqColors = [pg.intColor(i, nSeq * 1.6) for i in range(nSeq)]

        # cull out truncated recordings :(
        self.img_data = [[
            d['Time':0:200e-3] for d in row if d.xvals('Time')[-1] > 150e-3
        ] for row in self.img_data]

        # crop / concatenate
        img_len = min(
            [min([d.shape[0] for d in row]) for row in self.img_data])
        self.img_data = [[d[:img_len] for d in row] for row in self.img_data]
        self.img_arrays = [
            np.concatenate([d.asarray()[np.newaxis, ...] for d in row], axis=0)
            for row in self.img_data
        ]

        # average
        self.img_mean = [img_arr.mean(axis=0) for img_arr in self.img_arrays]

        for p in self.clamp_plots:
            self.plt2.removeItem(p)

        # read all clamp data
        first_subdir = seqDir[seqDir.ls()[0]]
        clamp_name = 'Clamp1'
        if not first_subdir[clamp_name + '.ma'].exists():
            clamp_name = 'Clamp2'

        clamp_file = first_subdir[clamp_name + '.ma']
        if clamp_file.exists():
            self.clamp_mode = model.getClampMode(clamp_file)
            chan = 'command' if self.clamp_mode == 'VC' else 'primary'
            self.clamp_data = model.buildSequenceArray(
                seqDir,
                lambda dh: dh[clamp_name + '.ma'].read()['Channel':chan],
                join=False).asarray()
            if self.clamp_data.ndim == 1:
                self.clamp_data = self.clamp_data[np.newaxis, :]
            if transpose:
                self.clamp_data = np.swapaxes(self.clamp_data, 0, 1)

            self.plt2.setLabels(left=('Vm', 'V'))

            for i in range(self.clamp_data.shape[0]):
                for j in range(self.clamp_data.shape[1]):
                    trace = self.clamp_data[i, j]
                    pen = self.seqColors[i]
                    p = self.plt2.plot(trace.xvals('Time'),
                                       trace.asarray(),
                                       antialias=True,
                                       pen=pen)
                    self.clamp_plots.append(p)
            self.plt2.show()
        else:
            self.clamp_mode = None
            self.plt2.hide()

        self.img_t = self.img_data[0][0].xvals('Time')

        self.img1.setImage(self.img_mean[-1].mean(axis=0))

        self.roi_changed(None)
        self.time_rgn_changed(None)
        self.update_sequence_analysis()
Пример #10
0
    def _load_data(self, seqDir):
        man = getManager()
        model = man.dataModel

        # read all image data
        self.img_data = model.buildSequenceArray(
            seqDir, 
            lambda dh: dh['Camera']['frames.ma'].read(),
            join=False).asarray()
        seqParams = list(model.listSequenceParams(seqDir).items())
        if self.img_data.ndim == 1:
            self.img_data = self.img_data[np.newaxis, :]
            seqParams.insert(0, (None, [0]))

        transpose = seqParams[0][0] == ('protocol', 'repetitions')
        if transpose:
            self.img_data = np.swapaxes(self.img_data, 0, 1)
            seqParams = seqParams[::-1]
        self.seqParams = seqParams

        if seqParams[0][0] is None:
            self.seqColors = [pg.mkColor('w')]
        else:
            nSeq = len(seqParams[0][1])
            if nSeq == 2:
                # Special case: add in difference between two sequence trials
                seqParams[0] = (seqParams[0][0], list(seqParams[0][1]) + [np.mean(seqParams[0][1])])
                nSeq = 3
                img_data = np.empty((3,) + self.img_data.shape[1:], dtype=self.img_data.dtype)
                img_data[:2] = self.img_data
                for i in range(img_data.shape[1]):
                    minlen = min(self.img_data[0,i].shape[0], self.img_data[1,i].shape[0])
                    img_data[2,i] = self.img_data[0,i][:minlen].copy()
                    img_data[2,i]._data = self.img_data[0,i][:minlen].asarray().astype('float') - self.img_data[1,i][:minlen].asarray()
                self.img_data = img_data
                
                
            self.seqColors = [pg.intColor(i, nSeq*1.6) for i in range(nSeq)]

        # cull out truncated recordings :(
        self.img_data = [[d['Time':0:200e-3] for d in row if d.xvals('Time')[-1] > 150e-3] for row in self.img_data]

        # crop / concatenate
        img_len = min([min([d.shape[0] for d in row]) for row in self.img_data])
        self.img_data = [[d[:img_len] for d in row] for row in self.img_data]
        self.img_arrays = [np.concatenate([d.asarray()[np.newaxis, ...] for d in row], axis=0) for row in self.img_data]

        # average
        self.img_mean = [img_arr.mean(axis=0) for img_arr in self.img_arrays]

        for p in self.clamp_plots:
            self.plt2.removeItem(p)
            
        # read all clamp data
        first_subdir = seqDir[seqDir.ls()[0]]
        clamp_name = 'Clamp1'
        if not first_subdir[clamp_name + '.ma'].exists():
            clamp_name = 'Clamp2'
        
        clamp_file = first_subdir[clamp_name + '.ma']
        if clamp_file.exists():
            self.clamp_mode = model.getClampMode(clamp_file)
            chan = 'command' if self.clamp_mode == 'VC' else 'primary'
            self.clamp_data = model.buildSequenceArray(
                seqDir, 
                lambda dh: dh[clamp_name + '.ma'].read()['Channel': chan],
                join=False).asarray()
            if self.clamp_data.ndim == 1:
                self.clamp_data = self.clamp_data[np.newaxis, :]
            if transpose:
                self.clamp_data = np.swapaxes(self.clamp_data, 0, 1)

            self.plt2.setLabels(left=('Vm', 'V'))
            
            for i in range(self.clamp_data.shape[0]):
                for j in range(self.clamp_data.shape[1]):
                    trace = self.clamp_data[i,j]
                    pen = self.seqColors[i]
                    p = self.plt2.plot(trace.xvals('Time'), trace.asarray(), antialias=True, pen=pen)
                    self.clamp_plots.append(p)
            self.plt2.show()
        else:
            self.clamp_mode = None
            self.plt2.hide()

        self.img_t = self.img_data[0][0].xvals('Time')

        self.img1.setImage(self.img_mean[-1].mean(axis=0))

        self.roi_changed(None)
        self.time_rgn_changed(None)
        self.update_sequence_analysis()
Пример #11
0
    def updateProfiles(self):
        #if not self.analyzeBtn.isChecked():
        #return
        plots = self.getElement('profiles'), self.getElement('profile fits')
        for plot in plots:
            plot.clear()
            plot.setLabel('bottom', 'distance', units='m')
        width, height = self.normData.shape
        xVals = np.linspace(0, self.px[0] * width, width)
        fits = []

        def slopeGaussian(v, x):  ## gaussian + slope
            return fn.gaussian(v[:4], x) + v[4] * x

        def gaussError(
                v, x, y):  ## center-weighted error functionfor sloped gaussian
            err = abs(y - slopeGaussian(v, x))
            v2 = [2.0, v[1], v[2] * 0.3, 1.0, 0.0]
            return err * slopeGaussian(v2, x)

        with pg.ProgressDialog("Processing..", 0, height - 1,
                               cancelText=None) as dlg:
            for i in range(height):
                row = self.normData[:, i]
                guess = [
                    row.max() - row.min(), xVals[int(width / 2)],
                    self.px[0] * 3,
                    row.max(), 0.0
                ]
                #fit = fn.fitGaussian(xVals=xVals, yVals=row, guess=guess)[0]
                #fit = fn.fit(slopeGaussian, xVals=xVals, yVals=row, guess=guess)[0]
                fit = scipy.optimize.leastsq(gaussError,
                                             guess,
                                             args=(xVals, row))[0]
                fit[2] = abs(fit[2])
                dist = fit[1] / (self.px[0] * width / 2.)
                #print fit, dist
                ## sanity check on fit
                if abs(dist - 1) > 0.5 or (0.5 < fit[3] / np.median(row) >
                                           2.0):
                    #print "rejected:", fit, fit[3]/np.median(row), self.px[0]*width/2.
                    #fit = guess[:]
                    #fit[0] = 0
                    fit = [0, 0, 0, 0, 0]
                else:
                    # round 2: eliminate anomalous points and re-fit
                    fitCurve = slopeGaussian(fit, xVals)
                    diff = row - fitCurve
                    std = diff.std()
                    mask = abs(diff) < std * 1.5
                    x2 = xVals[mask]
                    y2 = row[mask]
                    fit = fn.fit(slopeGaussian, xVals=x2, yVals=y2,
                                 guess=fit)[0]
                fits.append(fit)
                dlg += 1
                if dlg.wasCanceled():
                    raise Exception("Processing canceled by user")

        for i in range(len(fits)):  ## plot in reverse order
            pen = pg.intColor(height - i, height * 1.4)
            plots[0].plot(y=self.normData[:, -1 - i], x=xVals, pen=pen)
            plots[1].plot(y=slopeGaussian(fits[-1 - i], xVals),
                          x=xVals,
                          pen=pen)

        yVals = np.linspace(0, self.px[0] * height, height)
        arr = np.array(fits)
        info = [{
            'name': 'depth',
            'units': 'm',
            'values': yVals
        }, {
            'name':
            'fitParams',
            'cols': [
                {
                    'name': 'Amplitude'
                },
                {
                    'name': 'X Offset'
                },
                {
                    'name': 'Sigma',
                    'units': 'm'
                },
                {
                    'name': 'Y Offset'
                },
                {
                    'name': 'Slope'
                },
            ]
        }, {
            'sourceImage':
            self.fileHandle.name(),
            'dataRegion':
            self.dataRgn.saveState(),
            'backgroundRegion':
            self.bgRgn.saveState(),
            'description':
            """
                    The source image was normalized for background fluorescence, then each row was fit to a sloped gaussian function:
                        v[0] * np.exp(-((x-v[1])**2) / (2 * v[2]**2)) + v[3] + v[4] * x
                    The fit parameters v[0..4] for each image row are stored in the columns of this data set.
                    """
        }]
        #print info
        self.data = MetaArray(arr, info=info)
        self.showResults(self.data)
Пример #12
0
    def redisplayData(self,
                      points):  ## data must be [(scan, fh, <event time>), ...]
        #raise Exception('blah')
        #print points
        try:
            QtGui.QApplication.setOverrideCursor(
                QtGui.QCursor(QtCore.Qt.WaitCursor))
            plot = self.getElement("Data Plot")
            plot.clear()
            eTable = self.getElement("Event Table")
            sTable = self.getElement("Stats")

            #num = len(point.data)
            num = len(points)
            statList = []
            evList = []
            for i in range(num):
                color = pg.intColor(i, num)
                #scan, fh = point.data[i]
                try:
                    scan, fh = points[i][:2]
                except:
                    print points[i]
                    raise

                if len(points[i]) == 3:
                    evTime = points[i][2]
                else:
                    evTime = None

                scan.displayData(fh, plot, color, evTime)

                ## show stats
                stats = scan.getStats(fh.parent())
                statList.append(stats)
                events = scan.getEvents(fh)['events']
                if len(events) > 0:
                    evList.append(events)

            sTable.setData(statList)
            if len(evList) > 0:
                try:
                    eTable.setData(np.concatenate(evList))
                except:
                    for i in range(1, len(evList)):
                        if len(evList[i].dtype) != len(evList[i - 1].dtype):
                            print "Cannot concatenate; event lists have different dtypes:"
                            print evList[i].dtype
                            print evList[i - 1].dtype
                        else:
                            for j in range(len(evList[i].dtype)):
                                if evList[i -
                                          1].dtype[j] != evList[i].dtype[j]:
                                    for l in evList:
                                        print l
                                    print "Warning: can not concatenate--field '%s' has inconsistent types %s, %s  (data printed above)" % (
                                        evList[i].dtype.names[j],
                                        str(evList[i - 1].dtype[j]),
                                        str(evList[i].dtype[j]))
                    raise
        finally:
            QtGui.QApplication.restoreOverrideCursor()
Пример #13
0
    def updateProfiles(self):
        #if not self.analyzeBtn.isChecked():
            #return
        plots = self.getElement('profiles'), self.getElement('profile fits')
        for plot in plots:
            plot.clear()
            plot.setLabel('bottom', 'distance', units='m')
        width, height = self.normData.shape
        xVals = np.linspace(0, self.px[0]*width, width)
        fits = []
        
        def slopeGaussian(v, x):  ## gaussian + slope
            return fn.gaussian(v[:4], x) + v[4] * x
        def gaussError(v, x, y):  ## center-weighted error functionfor sloped gaussian
            err = abs(y-slopeGaussian(v, x))
            v2 = [2.0, v[1], v[2]*0.3, 1.0, 0.0]
            return err * slopeGaussian(v2, x)
        
        with pg.ProgressDialog("Processing..", 0, height-1, cancelText=None) as dlg:
            for i in range(height):
                row = self.normData[:, i]
                guess = [row.max()-row.min(), xVals[int(width/2)], self.px[0]*3, row.max(), 0.0]
                #fit = fn.fitGaussian(xVals=xVals, yVals=row, guess=guess)[0]
                #fit = fn.fit(slopeGaussian, xVals=xVals, yVals=row, guess=guess)[0]
                fit = scipy.optimize.leastsq(gaussError, guess, args=(xVals, row))[0] 
                fit[2] = abs(fit[2])
                dist = fit[1] / (self.px[0] * width / 2.)
                #print fit, dist
                ## sanity check on fit
                if abs(dist-1) > 0.5 or (0.5 < fit[3]/np.median(row) > 2.0):
                    #print "rejected:", fit, fit[3]/np.median(row), self.px[0]*width/2.
                    #fit = guess[:]
                    #fit[0] = 0
                    fit = [0,0,0,0,0]
                else:
                    # round 2: eliminate anomalous points and re-fit
                    fitCurve = slopeGaussian(fit, xVals)
                    diff = row - fitCurve
                    std = diff.std()
                    mask = abs(diff) < std * 1.5
                    x2 = xVals[mask]
                    y2 = row[mask]
                    fit = fn.fit(slopeGaussian, xVals=x2, yVals=y2, guess=fit)[0]
                fits.append(fit)
                dlg += 1
                if dlg.wasCanceled():
                    raise Exception("Processing canceled by user")
        
        for i in range(len(fits)):  ## plot in reverse order
            pen = pg.intColor(height-i, height*1.4)
            plots[0].plot(y=self.normData[:, -1-i], x=xVals, pen=pen)
            plots[1].plot(y=slopeGaussian(fits[-1-i], xVals), x=xVals, pen=pen)

        

        yVals = np.linspace(0, self.px[0]*height, height)
        arr = np.array(fits)
        info = [
                {'name': 'depth', 'units': 'm', 'values': yVals},
                {'name': 'fitParams', 'cols': [
                    {'name': 'Amplitude'},
                    {'name': 'X Offset'},
                    {'name': 'Sigma', 'units': 'm'},
                    {'name': 'Y Offset'},
                    {'name': 'Slope'},                    
                    ]},
                {
                    'sourceImage': self.fileHandle.name(),
                    'dataRegion': self.dataRgn.saveState(),
                    'backgroundRegion': self.bgRgn.saveState(),
                    'description': """
                    The source image was normalized for background fluorescence, then each row was fit to a sloped gaussian function:
                        v[0] * np.exp(-((x-v[1])**2) / (2 * v[2]**2)) + v[3] + v[4] * x
                    The fit parameters v[0..4] for each image row are stored in the columns of this data set.
                    """
                }
            ]
        #print info
        self.data = MetaArray(arr, info=info)
        self.showResults(self.data)