def calculateShiftsFFT(self, stack, reference, offsets=None, widths=None, crop=False): if DEBUG: print("Offsets = ", offsets) print("Widths = ", widths) data = stack.data if offsets is None: offsets = [0.0, 0.0] if widths is None: widths = [reference.shape[0], reference.shape[1]] fft2Function = numpy.fft.fft2 if 1: DTYPE = numpy.float32 else: DTYPE = numpy.float64 image2 = numpy.zeros((widths[0], widths[1]), dtype=DTYPE) shape = image2.shape USE_APODIZATION_WINDOW = False apo = [10, 10] if USE_APODIZATION_WINDOW: # use apodization window window = numpy.outer(SpecfitFuns.slit([0.5, shape[0] * 0.5, shape[0] - 4 * apo[0], apo[0]], numpy.arange(float(shape[0]))), SpecfitFuns.slit([0.5, shape[1] * 0.5, shape[1] - 4 * apo[1], apo[1]], numpy.arange(float(shape[1])))).astype(DTYPE) else: window = numpy.zeros((shape[0], shape[1]), dtype=DTYPE) window[apo[0]:shape[0] - apo[0], apo[1]:shape[1] - apo[1]] = 1 image2[:,:] = window * reference[offsets[0]:offsets[0]+widths[0], offsets[1]:offsets[1]+widths[1]] image2fft2 = fft2Function(image2) mcaIndex = stack.info.get('McaIndex') shifts = numpy.zeros((data.shape[mcaIndex], 2), numpy.float) image1 = numpy.zeros(image2.shape, dtype=DTYPE) total = float(data.shape[mcaIndex]) if mcaIndex == 0: for i in range(data.shape[mcaIndex]): image1[:,:] = window * data[i][offsets[0]:offsets[0]+widths[0], offsets[1]:offsets[1]+widths[1]] image1fft2 = fft2Function(image1) shifts[i] = ImageRegistration.measure_offset_from_ffts(image2fft2, image1fft2) if DEBUG: print("Index = %d shift = %.4f, %.4f" % (i, shifts[i][0], shifts[i][1])) self._progress = (100 * i) / total elif mcaIndex in [2, -1]: for i in range(data.shape[mcaIndex]): image1[:,:] = window * data[:,:,i][offsets[0]:offsets[0]+widths[0], offsets[1]:offsets[1]+widths[1]] image1fft2 = fft2Function(image1) shifts[i] = ImageRegistration.measure_offset_from_ffts(image2fft2, image1fft2) if DEBUG: print("Index = %d shift = %.4f, %.4f" % (i, shifts[i][0], shifts[i][1])) self._progress = (100 * i) / total else: raise IndexError("Only stacks of images or spectra supported. 1D index should be 0 or 2") return shifts
def guess_fwhm(self, **kw): if 'y' in kw: y=kw['y'] else: return self.config['FwhmPoints'] if 'x' in kw: x=kw['x'] else: x=numpy.arange(len(y))*1.0 #set at least a default value for the fwhm fwhm=4 zz=SpecfitFuns.subac(y,1.000,1000) yfit=y-zz #now I should do some sort of peak search ... maximum = max(yfit) idx = numpy.nonzero(yfit == maximum)[0] pos = numpy.take(x,idx)[-1] posindex = idx[-1] height = yfit[posindex] imin = posindex while ((yfit[imin] > 0.5*height) & (imin >0)): imin=imin - 1 imax=posindex while ((yfit[imax] > 0.5*height) & (imax <(len(yfit)-1))): imax=imax + 1 fwhm=max(imax-imin-1,fwhm) return fwhm
def estimateXANESEdge(spectrum, energy=None, full=False): if energy is None: x = numpy.arange(len(spectrum)).astype(numpy.float) else: x = numpy.array(energy, dtype=numpy.float, copy=True) y = numpy.array(spectrum, dtype=numpy.float, copy=True) # make sure data are sorted idx = energy.argsort(kind='mergesort') x = numpy.take(energy, idx) y = numpy.take(spectrum, idx) # make sure data are strictly increasing delta = x[1:] - x[:-1] dmin = delta.min() dmax = delta.max() if delta.min() <= 1.0e-10: # force data are strictly increasing # although we do not consider last point idx = numpy.nonzero(delta>0)[0] x = numpy.take(x, idx) y = numpy.take(y, idx) delta = None sortedX = x sortedY = y # use a regularly spaced spectrum if dmax != dmin: # choose the number of points or deduce it from # the input data length? nchannels = 2 * len(spectrum) xi = numpy.linspace(x[1], x[-2], nchannels).reshape(-1, 1) x.shape = -1 y.shape = -1 y = SpecfitFuns.interpol([x], y, xi, y.min()) x = xi x.shape = -1 y.shape = -1 # take the first derivative npoints = 7 xPrime = x[npoints:-npoints] yPrime = SGModule.getSavitzkyGolay(y, npoints=npoints, degree=2, order=1) # get the index at maximum value iMax = numpy.argmax(yPrime) # get the center of mass w = 2 * npoints selection = yPrime[iMax-w:iMax+w+1] edge = (selection * xPrime[iMax-w:iMax+w+1]).sum(dtype=numpy.float)/\ selection.sum(dtype=numpy.float) if full: # return intermediate information return edge, sortedX, sortedY, xPrime, yPrime else: # return the corresponding x value return edge
def bkg_internal(self,pars,x): """ Internal Background """ #fast #return self.zz #slow: recalculate the background as function of the parameters #yy=SpecfitFuns.subac(self.ydata*self.fitconfig['Yscaling'], # pars[0],pars[1]) yy=SpecfitFuns.subac(self.ydata*1.0, pars[0],pars[1]) nrx=shape(x)[0] nry=shape(yy)[0] if nrx == nry: return SpecfitFuns.subac(yy,pars[0],pars[1]) else: return SpecfitFuns.subac(numpy.take(yy,numpy.arange(0,nry,2)),pars[0],pars[1])
def estimateXANESEdge(spectrum, energy=None, npoints=5, full=False, sanitize=True): if sanitize: if energy is None: x = numpy.arange(len(spectrum)).astype(numpy.float) else: x = numpy.array(energy, dtype=numpy.float, copy=False) y = numpy.array(spectrum, dtype=numpy.float, copy=False) # make sure data are sorted idx = energy.argsort(kind='mergesort') x = numpy.take(energy, idx) y = numpy.take(spectrum, idx) # make sure data are strictly increasing delta = x[1:] - x[:-1] dmin = delta.min() dmax = delta.max() if delta.min() <= 1.0e-10: # force data are strictly increasing # although we do not consider last point idx = numpy.nonzero(delta>0)[0] x = numpy.take(x, idx) y = numpy.take(y, idx) delta = None # use a regularly spaced spectrum if dmax != dmin: # choose the number of points or deduce it from # the input data length? nchannels = 10 * x.size xi = numpy.linspace(x[1], x[-2], nchannels).reshape(-1, 1) x.shape = -1 y.shape = -1 y = SpecfitFuns.interpol([x], y, xi, y.min()) x = xi else: # take views x = energy[:] y = spectrum[:] x.shape = -1 y.shape = -1 # Sorted and regularly spaced values sortedX = x sortedY = y ddict = getE0SavitzkyGolay(sortedX, sortedY, points=npoints, full=full) if full: # return intermediate information return ddict["edge"], sortedX, sortedY, ddict["xPrime"], ddict["yPrime"] else: # return the corresponding x value return ddict
def interpolate(self, factor=1.): """ Input ----- factor : float factor used to oversample existing data, use with caution. Interpolates all existing curves to an equidistant x-range using the either the active or the first curve do determine the number of data points. Use this method instead of self.getAllCurves() when performin FFT related tasks. Returns ------- interpCurves : ndarray Array containing the interpolated curves shown in the plot window. Format: [(x0, y0, legend0, info0), ...] """ curves = self.getAllCurves() if len(curves) < 1: if DEBUG == 1: print('interpolate -- no curves present') raise ValueError("At least 1 curve needed") return activeCurve = self.getActiveCurve() if not activeCurve: activeCurve = curves[0] else: activeLegend = activeCurve[2] idx = list.index([curve[2] for curve in curves], activeLegend) activeCurve = curves[idx] activeX, activeY, activeLegend, activeInfo = activeCurve[0:4] # Determine average spaceing between Datapoints step = numpy.average(numpy.diff(activeX)) xmin, xmax = self.getXLimits([x for (x,y,leg,info) in curves], overlap=False) num = factor * numpy.ceil((xmax-xmin)/step) # Create equidistant x-range, exclude first and last point xeq = numpy.linspace(xmin, xmax, num, endpoint=False)[:-1] # Interpolate on sections of xeq interpCurves = [] for (x,y,legend,info) in curves: idx = numpy.nonzero((x.min()<xeq) & (xeq<x.max()))[0] xi = numpy.take(xeq, idx) yi = SpecfitFuns.interpol([x], y, xi.reshape(-1,1), y.min()) yi.shape = -1 interpCurves += [(xi, yi, legend, info)] return interpCurves
def periodic_gauss(self, pars, x): """ Fit function periodic_gauss(pars, x) pars = [npeaks, delta, height, position, fwhm] """ newpars = numpy.zeros((pars[0], 3), numpy.float) for i in range(int(pars[0])): newpars[i, 0] = pars[2] newpars[i, 1] = pars[3] + i * pars[1] newpars[:, 2] = pars[4] return SpecfitFuns.gauss(newpars,x)
def _fitBkgSubtract(spectra, config=None, anchorslist=None, fitmodel=None): """Subtract brackground from data and add it to fit model """ for k in range(spectra.shape[1]): # obtain the smoothed spectrum background = SpecfitFuns.SavitskyGolay(spectra[:, k], config['fit']['stripfilterwidth']) lastAnchor = 0 for anchor in anchorslist: if (anchor > lastAnchor) and (anchor < background.size): background[lastAnchor:anchor] =\ SpecfitFuns.snip1d(background[lastAnchor:anchor], config['fit']['snipwidth'], 0) lastAnchor = anchor if lastAnchor < background.size: background[lastAnchor:] =\ SpecfitFuns.snip1d(background[lastAnchor:], config['fit']['snipwidth'], 0) spectra[:, k] -= background if fitmodel is not None: fitmodel[:, k] = background
def estimate_linear(self, xx, yy, zzz, xscaling=1.0, yscaling=None): # compute strip bg and use it to estimate the linear bg parameters zz = SpecfitFuns.subac(yy, 1.000, 10000) n = float(len(zz)) Sy = numpy.sum(zz) Sx = float(numpy.sum(xx)) Sxx = float(numpy.sum(xx * xx)) Sxy = float(numpy.sum(xx * zz)) deno = n * Sxx - (Sx * Sx) if deno != 0: bg = (Sxx * Sy - Sx * Sxy) / deno slope = (n * Sxy - Sx * Sy) / deno else: bg = 0.0 slope = 0.0 estimated_par = [bg, slope] # code = 0: FREE constraints = [[0, 0], [0, 0], [0, 0]] return estimated_par, constraints
def test(): import numpy from PyMca5.PyMcaMath.fitting import SpecfitFuns x = numpy.arange(1000.) data = numpy.zeros((50, 1000), numpy.float) #the peaks to be fitted p0 = [100., 300., 50., 200., 500., 30., 300., 800., 65] #generate the data to be fitted for i in range(data.shape[0]): nPeaks = 3 - i % 3 data[i,:] = SpecfitFuns.gauss(p0[:3*nPeaks],x) oldShape = data.shape data.shape = 1,oldShape[0], oldShape[1] instance = StackSimpleFit() instance.setData(x, data) # TODO: Generate this file "on-the-fly" to be able to test everywhere instance.setConfigurationFile(r"C:\StackSimpleFit.cfg") instance.processStack()
def stepdown(self,pars,x): """ A fit function. """ return SpecfitFuns.downstep(pars,x)
def splitlorentz(self,pars,x): """ Asymmetric lorentz. """ return SpecfitFuns.splitlorentz(pars,x)
def apvoigt(self,pars,x): """ Fit function. """ return SpecfitFuns.apvoigt(pars,x)
def lorentz(self,pars,x): """ Fit function. """ #return pars[0] + pars [1] * x + SpecfitFuns.lorentz(pars[2:len(pars)],x) return SpecfitFuns.lorentz(pars,x)
def agauss(self,pars,x): """ A fit function. """ return SpecfitFuns.agauss(pars,x)
def update(self): if self._y is None: return pars = self.getParameters() #smoothed data y = numpy.ravel(numpy.array(self._y)).astype(numpy.float) ysmooth = SpecfitFuns.SavitskyGolay(y, pars['stripfilterwidth']) f=[0.25,0.5,0.25] ysmooth[1:-1] = numpy.convolve(ysmooth,f,mode=0) ysmooth[0] = 0.5 *(ysmooth[0] + ysmooth[1]) ysmooth[-1] = 0.5 * (ysmooth[-1] + ysmooth[-2]) #loop for anchors x = self._x niter = pars['stripiterations'] anchorslist = [] if pars['stripanchorsflag']: if pars['stripanchorslist'] is not None: ravelled = x for channel in pars['stripanchorslist']: if channel <= ravelled[0]:continue index = numpy.nonzero(ravelled >= channel)[0] if len(index): index = min(index) if index > 0: anchorslist.append(index) if niter > 1000: stripBackground = SpecfitFuns.subac(ysmooth, pars['stripconstant'], niter, pars['stripwidth'], anchorslist) #final smoothing stripBackground = SpecfitFuns.subac(stripBackground, pars['stripconstant'], 500,1, anchorslist) elif niter > 0: stripBackground = SpecfitFuns.subac(ysmooth, pars['stripconstant'], niter, pars['stripwidth'], anchorslist) else: stripBackground = 0.0 * ysmooth + ysmooth.min() if len(anchorslist) == 0: anchorslist = [0, len(ysmooth)-1] anchorslist.sort() snipBackground = 0.0 * ysmooth lastAnchor = 0 width = pars['snipwidth'] for anchor in anchorslist: if (anchor > lastAnchor) and (anchor < len(ysmooth)): snipBackground[lastAnchor:anchor] =\ SpecfitFuns.snip1d(ysmooth[lastAnchor:anchor], width, 0) lastAnchor = anchor if lastAnchor < len(ysmooth): snipBackground[lastAnchor:] =\ SpecfitFuns.snip1d(ysmooth[lastAnchor:], width, 0) self.graphWidget.addCurve(x, y, \ legend='Input Data',\ replace=True, replot=False) self.graphWidget.addCurve(x, stripBackground,\ legend='Strip Background',\ replot=False) self.graphWidget.addCurve(x, snipBackground,\ legend='SNIP Background', replot=True)
def XASNormalize(self): #all curves curves = self.getAllCurves() nCurves = len(curves) if nCurves < 1: raise ValueError("At least one curve needed") return #get active curve activeCurve = self.getActiveCurve() if activeCurve is None: raise ValueError("Please select an active curve") return x, y, legend0, info = activeCurve #sort the values idx = numpy.argsort(x, kind='mergesort') x0 = numpy.take(x, idx) y0 = numpy.take(y, idx) xmin, xmax = self.getGraphXLimits() # get calculation parameters if self.widget is None: self._createWidget(y0, x0) parameters = self.parameters if parameters['auto_edge']: edge = None else: edge = parameters['edge_energy'] energy = x pre_edge_regions = parameters['pre_edge']['regions'] post_edge_regions = parameters['post_edge']['regions'] algorithm ='polynomial' algorithm_parameters = {} algorithm_parameters['pre_edge_order'] = parameters['pre_edge']\ ['polynomial'] algorithm_parameters['post_edge_order'] = parameters['post_edge']\ ['polynomial'] i = 0 lastCurve = None for curve in curves: x, y, legend, info = curve[0:4] #take the portion ox x between limits idx = numpy.nonzero((x>=xmin) & (x<=xmax))[0] if not len(idx): #no overlap continue x = numpy.take(x, idx) y = numpy.take(y, idx) idx = numpy.nonzero((x0>=x.min()) & (x0<=x.max()))[0] if not len(idx): #no overlap continue xi = numpy.take(x0, idx) yi = numpy.take(y0, idx) #perform interpolation xi.shape = -1, 1 yw = SpecfitFuns.interpol([x], y, xi, yi.min()) # try: ... except: here? yw.shape = -1 xi.shape = -1 x, y = XASNormalization.XASNormalization(yw, energy=xi, edge=edge, pre_edge_regions=pre_edge_regions, post_edge_regions=post_edge_regions, algorithm=algorithm, algorithm_parameters=algorithm_parameters)[0:2] # if i == 0: replace = True replot = True i = 1 else: replot = False replace = False newLegend = " ".join(legend.split(" ")[:-1]) if not newLegend.startswith('Norm.'): newLegend = "Norm. " + newLegend self.addCurve(x, y, legend=newLegend, info=info, replot=replot, replace=replace) lastCurve = [x, y, newLegend] self.addCurve(lastCurve[0], lastCurve[1], legend=lastCurve[2], info=info, replot=True, replace=False)
def estimate_gauss(self,xx,yy,zzz,xscaling=1.0,yscaling=None): if yscaling == None: try: yscaling=self.config['Yscaling'] except: yscaling=1.0 if yscaling == 0: yscaling=1.0 fittedpar=[] zz=SpecfitFuns.subac(yy,1.000,10000) npoints = len(zz) if self.config['AutoFwhm']: search_fwhm=self.guess_fwhm(x=xx,y=yy) else: search_fwhm=int(float(self.config['FwhmPoints'])) search_sens=float(self.config['Sensitivity']) search_mca=int(float(self.config['McaMode'])) if search_fwhm < 3: search_fwhm = 3 self.config['FwhmPoints']=3 if search_sens < 1: search_sens = 1 self.config['Sensitivity']=1 if npoints > 1.5*search_fwhm: peaks=self.seek(yy,fwhm=search_fwhm, sensitivity=search_sens, yscaling=yscaling, mca=search_mca) #print "estimate peaks = ",peaks #peaks=self.seek(yy-zz,fwhm=search_fwhm, # sensitivity=search_sens, # yscaling=yscaling, # mca=search_mca) else: peaks = [] if not len(peaks): mca = int(float(self.config.get('McaMode', 0))) forcePeak = int(float(self.config.get('ForcePeakPresence', 0))) if (not mca) and forcePeak: delta = yy - zz peaks = [int(numpy.nonzero(delta == delta.max())[0])] #print "peaks = ",peaks #print "peaks subac = ",self.seek(yy-zz,fwhm=search_fwhm, # sensitivity=search_sens, # yscaling=yscaling, # mca=search_mca) largest_index = 0 if len(peaks) > 0: j = 0 for i in peaks: if j == 0: sig=5*abs(xx[npoints-1]-xx[0])/npoints peakpos = xx[int(i)] if abs(peakpos) < 1.0e-16: peakpos = 0.0 param = numpy.array([yy[int(i)] - zz[int(i)], peakpos ,sig]) largest = param else: param2 = numpy.array([yy[int(i)] - zz [int(i)], xx[int(i)] ,sig]) param = numpy.concatenate((param,param2)) if (param2[0] > largest[0]): largest = param2 largest_index = j j = j + 1 xw = numpy.resize(xx,(npoints,1)) if (0): sy = numpy.sqrt(abs(yy)) yw = numpy.resize(yy-zz,(npoints,1)) sy = numpy.resize(sy,(npoints,1)) datawork = numpy.concatenate((xw,yw,sy),1) else: yw = numpy.resize(yy-zz,(npoints,1)) datawork = numpy.concatenate((xw,yw),1) cons = numpy.zeros((3,len(param)),numpy.float64) cons [0] [0:len(param):3] = CPOSITIVE #force peaks to stay around their position if (1): cons [0] [1:len(param):3] = CQUOTED #This does not work!!!! #FWHM should be given in terms of X not of points! #cons [1] [1:len(param):3] = param[1:len(param):3]-0.5*search_fwhm #cons [2] [1:len(param):3] = param[1:len(param):3]+0.5*search_fwhm if len(xw) > search_fwhm: fwhmx = numpy.fabs(xw[int(search_fwhm)]-xw[0]) cons [1] [1:len(param):3] = param[1:len(param):3]-0.5*fwhmx cons [2] [1:len(param):3] = param[1:len(param):3]+0.5*fwhmx else: cons [1] [1:len(param):3] = numpy.ones(shape(param[1:len(param):3]),numpy.float64)*min(xw) cons [2] [1:len(param):3] = numpy.ones(shape(param[1:len(param):3]),numpy.float64)*max(xw) if 0: cons [0] [2:len(param):3] = CFACTOR cons [1] [2:len(param):3] = 2 cons [2] [2:len(param):3] = 1.0 cons [0] [2] = CPOSITIVE cons [1] [2] = 0 cons [2] [2] = 0 else: cons [0] [2:len(param):3] = CPOSITIVE fittedpar, chisq, sigmapar = LeastSquaresFit(SpecfitFuns.gauss,param, datawork, weightflag=self.config['WeightFlag'], maxiter=4,constrains=cons.tolist()) #I already have the estimation #yw=yy-zz-SpecfitFuns.gauss(fittedpar,xx) #if DEBUG: # SimplePlot.plot([xx,yw]) #print self.seek(yw,\ # fwhm=search_fwhm,\ # sensitivity=search_sens,\ # yscaling=yscaling) #else: # #Use SPEC estimate ... # peakpos,height,myidx = SpecArithmetic.search_peak(xx,yy-zz) # fwhm,cfwhm = SpecArithmetic.search_fwhm(xx,yy-zz, # peak=peakpos,index=myidx) # xx = numpy.array(xx) # if npoints > 2: # #forget SPEC estimation of fwhm # fwhm=5*abs(xx[npoints-1]-xx[0])/npoints # fittedpar=[height,peakpos,fwhm] # largest=numpy.array([height,peakpos,fwhm]) # peaks=[peakpos] cons = numpy.zeros((3,len(fittedpar)),numpy.float64) j=0 for i in range(len(peaks)): #Setup height area constrains if self.config['NoConstrainsFlag'] == 0: if self.config['HeightAreaFlag']: #POSITIVE = 1 cons[0] [j] = 1 cons[1] [j] = 0 cons[2] [j] = 0 j=j+1 #Setup position constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PositionFlag']: #QUOTED = 2 cons[0][j]=2 cons[1][j]=min(xx) cons[2][j]=max(xx) j=j+1 #Setup positive FWHM constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PosFwhmFlag']: #POSITIVE=1 cons[0][j]=1 cons[1][j]=0 cons[2][j]=0 if self.config['SameFwhmFlag']: if (i != largest_index): #FACTOR=4 cons[0][j]=4 cons[1][j]=3*largest_index+2 cons[2][j]=1.0 j=j+1 return fittedpar,cons
def seek(self,y,x=None,yscaling=None, fwhm=None, sensitivity=None, mca=None): """ SpecfitFunctions.seek(self,y, x=None, yscaling=None,fwhm=None,sensitivity=None, mca=None) It searches for peaks in the y array. If x it is given, it gives back the closest x(s) to the position of the peak(s). Otherways it gives back the index of the closest point to the peak. """ if yscaling is None: yscaling=self.config['Yscaling'] if fwhm is None: if self.config['AutoFwhm']: fwhm=self.guess_fwhm(x=x,y=y) else: fwhm=self.config['FwhmPoints'] if sensitivity is None: sensitivity=self.config['Sensitivity'] if mca is None: mca=self.config['McaMode'] search_fwhm=int(max(fwhm,3)) search_sensitivity=max(1.0,sensitivity) mca=1.0 if mca: ysearch = numpy.array(y) * yscaling else: ysearch = numpy.ones([len(y) + 2 * search_fwhm,], numpy.float64) if y[0]: ysearch[0:(search_fwhm+1)]=ysearch[0:(search_fwhm+1)]*y[0]*yscaling else: ysearch[0:(search_fwhm+1)]=ysearch[0:(search_fwhm+1)]*yscaling*sum(y[0:3])/3.0 ysearch[-1:-(search_fwhm+1):-1]=ysearch[-1:-(search_fwhm+1):-1]*y[len(y)-1]*yscaling ysearch[search_fwhm:(search_fwhm+len(y))]=y*yscaling npoints=len(ysearch) if npoints > (1.5)*search_fwhm: peaks=SpecfitFuns.seek(ysearch,0,npoints, search_fwhm, search_sensitivity) else: peaks=[] if len(peaks) > 0: if mca == 0: for i in range(len(peaks)): peaks[i]=int(peaks[i]-search_fwhm) for i in peaks: if (i < 1) | (i > (npoints-1)): peaks.remove(i) if x is not None: if len(x) == len(y): for i in range(len(peaks)): peaks[i]=x[int(max(peaks[i],0))] #print "peaks found = ",peaks,"mca =",mca,"fwhm=",search_fwhm,\ # "sensi=",search_sensitivity,"scaling=",yscaling return peaks
def slit(self,pars,x): """ A fit function. """ return 0.5*SpecfitFuns.slit(pars,x)
def stepup(self,pars,x): """ A fit function. """ return SpecfitFuns.upstep(pars,x)
def hypermet(self,pars,x): """ A fit function. """ return SpecfitFuns.ahypermet(pars,x,self.config['HypermetTails'],0)
def divideByActiveCurve(self): #all curves curves = self.getAllCurves() nCurves = len(curves) if nCurves < 2: raise ValueError("At least two curves needed") return #get active curve activeCurve = self.getActiveCurve() if activeCurve is None: raise ValueError("Please select an active curve") return x, y, legend0, info = activeCurve xmin, xmax = self.getGraphXLimits() y = y.astype(numpy.float) #get the nonzero values idx = numpy.nonzero(abs(y) != 0.0)[0] if not len(idx): raise ValueError("All divisor values are zero!") x0 = numpy.take(x, idx) y0 = numpy.take(y, idx) #sort the values idx = numpy.argsort(x0, kind='mergesort') x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) i = 0 for curve in curves: x, y, legend, info = curve[0:4] if legend == legend0: continue #take the portion ox x between limits idx = numpy.nonzero((x >= xmin) & (x <= xmax))[0] if not len(idx): #no overlap continue x = numpy.take(x, idx) y = numpy.take(y, idx) idx = numpy.nonzero((x0 >= numpy.nanmin(x)) & (x0 <= numpy.nanmax(x)))[0] if not len(idx): #no overlap continue xi = numpy.take(x0, idx) yi = numpy.take(y0, idx) #perform interpolation xi.shape = -1, 1 yw = SpecfitFuns.interpol([x], y, xi, yi.min()) y = yw / yi if i == 0: replace = (self._plotType != "MCA") replot = True i = 1 else: replot = False replace = False # this line is absolutely necessary! xi.shape = y.shape self.addCurve(xi, y, legend=legend, info=info, replot=replot, replace=replace) lastCurve = [xi, y, legend] self.addCurve(lastCurve[0], lastCurve[1], legend=lastCurve[2], info=info, replot=True, replace=False)
def splitpvoigt(self,pars,x): """ Asymmetric pseudovoigt. """ return SpecfitFuns.splitpvoigt(pars,x)
def estimate_periodic_gauss(self,xx,yy,zzz,xscaling=1.0,yscaling=None): if yscaling == None: try: yscaling=self.config['Yscaling'] except: yscaling=1.0 if yscaling == 0: yscaling=1.0 fittedpar=[] zz=SpecfitFuns.subac(yy,1.000,10000) npoints = len(zz) if self.config['AutoFwhm']: search_fwhm=self.guess_fwhm(x=xx,y=yy) else: search_fwhm=int(float(self.config['FwhmPoints'])) search_sens=float(self.config['Sensitivity']) search_mca=int(float(self.config['McaMode'])) if search_fwhm < 3: search_fwhm = 3 self.config['FwhmPoints']=3 if search_sens < 1: search_sens = 1 self.config['Sensitivity']=1 if npoints > 1.5*search_fwhm: peaks=self.seek(yy,fwhm=search_fwhm, sensitivity=search_sens, yscaling=yscaling, mca=search_mca) else: peaks = [] npeaks = len(peaks) if not npeaks: fittedpar = [] cons = numpy.zeros((3,len(fittedpar)), numpy.float) return fittedpar, cons fittedpar = [0.0, 0.0, 0.0, 0.0, 0.0] #The number of peaks fittedpar[0] = npeaks #The separation between peaks in x units delta = 0.0 height = 0.0 for i in range(npeaks): height += yy[int(peaks[i])] - zz [int(peaks[i])] if i != ((npeaks)-1): delta += (xx[int(peaks[i+1])] - xx[int(peaks[i])]) #delta between peaks if npeaks > 1: fittedpar[1] = delta/(npeaks-1) #starting height fittedpar[2] = height/npeaks #position of the first peak fittedpar[3] = xx[int(peaks[0])] #Estimate the fwhm fittedpar[4] = search_fwhm #setup constraints cons = numpy.zeros((3, 5), numpy.float) cons [0] [0] = CFIXED #the number of gaussians if npeaks == 1: cons[0][1] = CFIXED #the delta between peaks else: cons[0][1] = CFREE #the delta between peaks j = 2 #Setup height area constrains if self.config['NoConstrainsFlag'] == 0: if self.config['HeightAreaFlag']: #POSITIVE = 1 cons[0] [j] = 1 cons[1] [j] = 0 cons[2] [j] = 0 j=j+1 #Setup position constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PositionFlag']: #QUOTED = 2 cons[0][j]=2 cons[1][j]=min(xx) cons[2][j]=max(xx) j=j+1 #Setup positive FWHM constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PosFwhmFlag']: #POSITIVE=1 cons[0][j]=1 cons[1][j]=0 cons[2][j]=0 j=j+1 return fittedpar,cons
def splitgauss(self,pars,x): """ Asymmetric gaussian. """ return SpecfitFuns.splitgauss(pars,x)
def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, lineProjectionMode): """Returns the profile values and the polygon in the given ROI. Works in image coordinates. See :func:`getAlignedROIProfileCurve`. """ row0, col0 = roiStart row1, col1 = roiEnd deltaRow = abs(row1 - row0) deltaCol = abs(col1 - col0) if (lineProjectionMode == 'X' or (lineProjectionMode == 'D' and deltaCol >= deltaRow)): nPoints = deltaCol + 1 coordsRange = col0, col1 else: # 'Y' or ('D' and deltaCol < deltaRow) nPoints = deltaRow + 1 coordsRange = row0, row1 if nPoints == 1: # all points are the same if DEBUG: print("START AND END POINT ARE THE SAME!!") return None # the coordinates of the reference points x0 = numpy.arange(image.shape[0], dtype=numpy.float) y0 = numpy.arange(image.shape[1], dtype=numpy.float) if roiWidth < 1: x = numpy.zeros((nPoints, 2), numpy.float) x[:, 0] = numpy.linspace(row0, row1, nPoints) x[:, 1] = numpy.linspace(col0, col1, nPoints) # perform the interpolation ydata = SpecfitFuns.interpol((x0, y0), image, x) roiPolygonCols = numpy.array((col0, col1), dtype=numpy.float) roiPolygonRows = numpy.array((row0, row1), dtype=numpy.float) else: # find m and b in the line y = mx + b m = (row1 - row0) / float((col1 - col0)) # Not used: b = row0 - m * col0 alpha = numpy.arctan(m) # imagine the following sequence # - change origin to the first point # - clock-wise rotation to bring the line on the x axis of a new system # so that the points (col0, row0) and (col1, row1) # become (x0, 0) (x1, 0). # - counter clock-wise rotation to get the points (x0, -0.5 width), # (x0, 0.5 width), (x1, 0.5 * width) and (x1, -0.5 * width) back to # the original system. # - restore the origin to (0, 0) # - if those extremes are inside the image the selection is acceptable cosalpha = numpy.cos(alpha) sinalpha = numpy.sin(alpha) newCol0 = 0.0 newCol1 = (col1 - col0) * cosalpha + (row1 - row0) * sinalpha newRow0 = 0.0 newRow1 = - (col1 - col0) * sinalpha + (row1 - row0) * cosalpha if DEBUG: print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) tmpX = numpy.linspace(newCol0, newCol1, nPoints).astype(numpy.float) rotMatrix = numpy.zeros((2, 2), dtype=numpy.float) rotMatrix[0, 0] = cosalpha rotMatrix[0, 1] = - sinalpha rotMatrix[1, 0] = sinalpha rotMatrix[1, 1] = cosalpha if DEBUG: # test if I recover the original points testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) print("Recovered X0 = %f" % (colRow[0, 0] + col0)) print("Recovered Y0 = %f" % (colRow[1, 0] + row0)) print("It should be = %f, %f" % (col0, row0)) testX[0, 0] = newCol1 testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) print("Recovered X1 = %f" % (colRow[0, 0] + col0)) print("Recovered Y1 = %f" % (colRow[1, 0] + row0)) print("It should be = %f, %f" % (col1, row1)) # find the drawing limits testX = numpy.zeros((2, 4), numpy.float) testX[0, 0] = newCol0 testX[0, 1] = newCol0 testX[0, 2] = newCol1 testX[0, 3] = newCol1 testX[1, 0] = newRow0 - 0.5 * roiWidth testX[1, 1] = newRow0 + 0.5 * roiWidth testX[1, 2] = newRow1 + 0.5 * roiWidth testX[1, 3] = newRow1 - 0.5 * roiWidth colRow = numpy.dot(rotMatrix, testX) colLimits0 = colRow[0, :] + col0 rowLimits0 = colRow[1, :] + row0 for a in rowLimits0: if (a >= image.shape[0]) or (a < 0): print("outside row limits", a) return None for a in colLimits0: if (a >= image.shape[1]) or (a < 0): print("outside column limits", a) return None r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: print("r0 > r1", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, nPoints), numpy.float) # oversampling solves noise introduction issues oversampling = roiWidth + 1 oversampling = min(oversampling, 21) ncontributors = roiWidth * oversampling iterValues = numpy.linspace(-0.5 * roiWidth, 0.5 * roiWidth, ncontributors) tmpMatrix = numpy.zeros((nPoints * len(iterValues), 2), dtype=numpy.float) x[0, :] = tmpX offset = 0 for i in iterValues: x[1, :] = i colRow = numpy.dot(rotMatrix, x) colRow[0, :] += col0 colRow[1, :] += row0 # it is much faster to make one call to the interpolating # routine than making many calls tmpMatrix[offset:(offset + nPoints), 0] = colRow[1, :] tmpMatrix[offset:(offset + nPoints), 1] = colRow[0, :] offset += nPoints ydata = SpecfitFuns.interpol((x0, y0), image, tmpMatrix) ydata.shape = len(iterValues), nPoints ydata = ydata.sum(axis=0) # deal with the oversampling ydata /= oversampling roiPolygonCols, roiPolygonRows = colLimits0, rowLimits0 return {'profileValues': ydata, 'profileCoordsRange': coordsRange, 'roiPolygonCols': roiPolygonCols, 'roiPolygonRows': roiPolygonRows}
def alorentz(self,pars,x): """ Fit function. """ return SpecfitFuns.alorentz(pars,x)
def fftAlignment(self): curves = self.getMonotonicCurves() nCurves = len(curves) if nCurves < 2: raise ValueError("At least 2 curves needed") return # get legend of active curve try: activeCurveLegend = self.getActiveCurve()[2] if activeCurveLegend is None: activeCurveLegend = curves[0][2] for curve in curves: if curve[2] == activeCurveLegend: activeCurve = curve break except: activeCurve = curves[0] activeCurveLegend = activeCurve[2] # apply between graph limits x0, y0 = activeCurve[0:2] xmin, xmax =self.getGraphXLimits() for curve in curves: xmax = float(min(xmax, curve[0][-1])) xmin = float(max(xmin, curve[0][0])) idx = numpy.nonzero((x0 >= xmin) & (x0 <= xmax))[0] x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) #make sure values are regularly spaced xi = numpy.linspace(x0[0], x0[-1], len(idx)).reshape(-1, 1) yi = SpecfitFuns.interpol([x0], y0, xi, y0.min()) x0 = xi * 1 y0 = yi * 1 y0.shape = -1 fft0 = numpy.fft.fft(y0) y0.shape = -1, 1 x0.shape = -1, 1 nChannels = x0.shape[0] # built a couple of temporary array of spectra for handy access shiftList = [] curveList = [] i = 0 activeCurveIndex = 0 for idx in range(nCurves): x, y, legend, info = curves[idx][0:4] if legend == activeCurveLegend: needInterpolation = False activeCurveIndex = idx elif x.size != x0.size: needInterpolation = True elif numpy.allclose(x, x0): # no need for interpolation needInterpolation = False else: needInterpolation = True if needInterpolation: # we have to interpolate x.shape = -1 y.shape = -1 xi = x0[:] * 1 y = SpecfitFuns.interpol([x], y, xi, y0.min()) x = xi y.shape = -1 i += 1 # now calculate the shift ffty = numpy.fft.fft(y) if numpy.allclose(fft0, ffty): shiftList.append(0.0) x.shape = -1 else: shift = numpy.fft.ifft(fft0 * ffty.conjugate()).real shift2 = numpy.zeros(shift.shape, dtype=shift.dtype) m = shift2.size//2 shift2[m:] = shift[:-m] shift2[:m] = shift[-m:] threshold = 0.80*shift2.max() #threshold = shift2.mean() idx = numpy.nonzero(shift2 > threshold)[0] #print("max indices = %d" % (m - idx)) shift = (shift2[idx] * idx/shift2[idx].sum()).sum() #print("shift = ", shift - m, "in x units = ", (shift - m) * (x[1]-x[0])) # shift the curve shift = (shift - m) * (x[1]-x[0]) x.shape = -1 y = numpy.fft.ifft(numpy.exp(-2.0*numpy.pi*numpy.sqrt(numpy.complex(-1))*\ numpy.fft.fftfreq(len(x), d=x[1]-x[0])*shift)*ffty) y = y.real y.shape = -1 curveList.append([x, y, legend + "SHIFT", False, False]) curveList[-1][-2] = True curveList[-1][-1] = False x, y, legend, replot, replace = curveList[activeCurveIndex] self.addCurve(x, y, legend=legend, replot=True, replace=True) for i in range(len(curveList)): if i == activeCurveIndex: continue x, y, legend, replot, replace = curveList[i] self.addCurve(x, y, legend=legend, info=None, replot=replot, replace=False) return
def applyMedianFilter(self, width=3): curves = self.getAllCurves() nCurves = len(curves) if nCurves < width: raise ValueError("At least %d curves needed" % width) return if self.__randomization: indices = numpy.random.permutation(nCurves) else: indices = range(nCurves) # get active curve activeCurve = self.getActiveCurve() if activeCurve is None: activeCurve = curves[0] # apply between graph limits x0 = activeCurve[0][:] y0 = activeCurve[1][:] xmin, xmax =self.getGraphXLimits() idx = numpy.nonzero((x0 >= xmin) & (x0 <= xmax))[0] x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) #sort the values idx = numpy.argsort(x0, kind='mergesort') x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) #remove duplicates x0 = x0.ravel() idx = numpy.nonzero((x0[1:] > x0[:-1]))[0] x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) x0.shape = -1, 1 nChannels = x0.shape[0] # built a couple of temporary array of spectra for handy access tmpArray = numpy.zeros((nChannels, nCurves), numpy.float64) medianSpectra = numpy.zeros((nChannels, nCurves), numpy.float64) i = 0 for idx in indices: x, y, legend, info = curves[idx][0:4] #sort the values x = x[:] idx = numpy.argsort(x, kind='mergesort') x = numpy.take(x, idx) y = numpy.take(y, idx) #take the portion of x between limits idx = numpy.nonzero((x>=xmin) & (x<=xmax))[0] if not len(idx): # no overlap continue x = numpy.take(x, idx) y = numpy.take(y, idx) #remove duplicates x = x.ravel() idx = numpy.nonzero((x[1:] > x[:-1]))[0] x = numpy.take(x, idx) y = numpy.take(y, idx) x.shape = -1, 1 if numpy.allclose(x, x0): # no need for interpolation pass else: # we have to interpolate x.shape = -1 y.shape = -1 xi = x0[:] y = SpecfitFuns.interpol([x], y, xi, y0.min()) y.shape = -1 tmpArray[:, i] = y i += 1 # now perform the median filter for i in range(nChannels): medianSpectra[i, :] = medfilt1d(tmpArray[i,:], kernel_size=width) tmpArray = None # now get the final spectrum y = medianSpectra.sum(axis=1) / nCurves x0.shape = -1 y.shape = x0.shape legend = "%d Median from %s to %s" % (width, curves[0][2], curves[-1][2]) self.addCurve(x0, y, legend=legend, info=None, replot=True, replace=True)
def divideByActiveCurve(self): #all curves curves = self.getAllCurves() nCurves = len(curves) if nCurves < 2: raise ValueError("At least two curves needed") return #get active curve activeCurve = self.getActiveCurve() if activeCurve is None: raise ValueError("Please select an active curve") return x, y, legend0, info = activeCurve xmin, xmax = self.getGraphXLimits() y = y.astype(numpy.float) #get the nonzero values idx = numpy.nonzero(abs(y) != 0.0)[0] if not len(idx): raise ValueError("All divisor values are zero!") x0 = numpy.take(x, idx) y0 = numpy.take(y, idx) #sort the values idx = numpy.argsort(x0, kind='mergesort') x0 = numpy.take(x0, idx) y0 = numpy.take(y0, idx) i = 0 for curve in curves: x, y, legend, info = curve[0:4] if legend == legend0: continue #take the portion ox x between limits idx = numpy.nonzero((x>=xmin) & (x<=xmax))[0] if not len(idx): #no overlap continue x = numpy.take(x, idx) y = numpy.take(y, idx) idx = numpy.nonzero((x0>=x.min()) & (x0<=x.max()))[0] if not len(idx): #no overlap continue xi = numpy.take(x0, idx) yi = numpy.take(y0, idx) #perform interpolation xi.shape = -1, 1 yw = SpecfitFuns.interpol([x], y, xi, yi.min()) y = yw / yi if i == 0: replace = True replot = True i = 1 else: replot = False replace = False self.addCurve(x, y, legend=legend, info=info, replot=replot, replace=replace) lastCurve = [x, y, legend] self.addCurve(lastCurve[0], lastCurve[1], legend=lastCurve[2], info=info, replot=True, replace=False)
def seek(self,y,x=None,yscaling=None, fwhm=None, sensitivity=None, mca=None): """ SpecfitFunctions.seek(self,y, x=None, yscaling=None,fwhm=None,sensitivity=None, mca=None) It searches for peaks in the y array. If x it is given, it gives back the closest x(s) to the position of the peak(s). Otherways it gives back the index of the closest point to the peak. """ if yscaling is None: yscaling=self.config['Yscaling'] if fwhm is None: if self.config['AutoFwhm']: fwhm=self.guess_fwhm(x=x,y=y) else: fwhm=self.config['FwhmPoints'] if sensitivity is None: sensitivity=self.config['Sensitivity'] if mca is None: mca=self.config['McaMode'] search_fwhm=int(max(fwhm,3)) search_sensitivity=max(1.0,sensitivity) mca=1.0 if mca: ysearch = numpy.array(y) * yscaling else: ysearch = numpy.ones([len(y) + 2 * search_fwhm,], numpy.float) if y[0]: ysearch[0:(search_fwhm+1)]=ysearch[0:(search_fwhm+1)]*y[0]*yscaling else: ysearch[0:(search_fwhm+1)]=ysearch[0:(search_fwhm+1)]*yscaling*sum(y[0:3])/3.0 ysearch[-1:-(search_fwhm+1):-1]=ysearch[-1:-(search_fwhm+1):-1]*y[len(y)-1]*yscaling ysearch[search_fwhm:(search_fwhm+len(y))]=y*yscaling npoints=len(ysearch) if npoints > (1.5)*search_fwhm: peaks=SpecfitFuns.seek(ysearch,0,npoints, search_fwhm, search_sensitivity) else: peaks=[] if len(peaks) > 0: if mca == 0: for i in range(len(peaks)): peaks[i]=int(peaks[i]-search_fwhm) for i in peaks: if (i < 1) | (i > (npoints-1)): peaks.remove(i) if x is not None: if len(x) == len(y): for i in range(len(peaks)): peaks[i]=x[int(max(peaks[i],0))] #print "peaks found = ",peaks,"mca =",mca,"fwhm=",search_fwhm,\ # "sensi=",search_sensitivity,"scaling=",yscaling return peaks
self.setEnabled(True) if __name__ == "__main__": import numpy from PyMca5.PyMcaMath.fitting import SpecfitFuns from PyMca5.PyMcaMath.fitting import SimpleFitUserEstimatedFunctions as Functions x = numpy.arange(1000.) data = numpy.zeros((50, 1000), numpy.float) #the peaks to be fitted p0 = [100., 300., 50., 200., 500., 30., 300., 800., 65] #generate the data to be fitted for i in range(data.shape[0]): nPeaks = 3 - i % 3 data[i,:] = SpecfitFuns.gauss(p0[:3*nPeaks],x) #the spectrum for setup y = data.sum(axis=0) oldShape = data.shape data.shape = 1,oldShape[0], oldShape[1] app = qt.QApplication([]) w = StackSimpleFitWindow() w.setSpectrum(x, y) w.setData(x, data) #w.importFunctions(Functions.__file__) #w.fitModule.setFitFunction('Gaussians') w.show() app.exec_()
def estimate_gauss(self,xx,yy,zzz,xscaling=1.0,yscaling=None): if yscaling == None: try: yscaling=self.config['Yscaling'] except: yscaling=1.0 if yscaling == 0: yscaling=1.0 fittedpar=[] zz=SpecfitFuns.subac(yy,1.000,10000) npoints = len(zz) if self.config['AutoFwhm']: search_fwhm=self.guess_fwhm(x=xx,y=yy) else: search_fwhm=int(float(self.config['FwhmPoints'])) search_sens=float(self.config['Sensitivity']) search_mca=int(float(self.config['McaMode'])) if search_fwhm < 3: search_fwhm = 3 self.config['FwhmPoints']=3 if search_sens < 1: search_sens = 1 self.config['Sensitivity']=1 if npoints > 1.5*search_fwhm: peaks=self.seek(yy,fwhm=search_fwhm, sensitivity=search_sens, yscaling=yscaling, mca=search_mca) #print "estimate peaks = ",peaks #peaks=self.seek(yy-zz,fwhm=search_fwhm, # sensitivity=search_sens, # yscaling=yscaling, # mca=search_mca) else: peaks = [] if not len(peaks): mca = int(float(self.config.get('McaMode', 0))) forcePeak = int(float(self.config.get('ForcePeakPresence', 0))) if (not mca) and forcePeak: delta = yy - zz peaks = [int(numpy.nonzero(delta == delta.max())[0])] #print "peaks = ",peaks #print "peaks subac = ",self.seek(yy-zz,fwhm=search_fwhm, # sensitivity=search_sens, # yscaling=yscaling, # mca=search_mca) largest_index = 0 if len(peaks) > 0: j = 0 for i in peaks: if j == 0: sig=5*abs(xx[npoints-1]-xx[0])/npoints peakpos = xx[int(i)] if abs(peakpos) < 1.0e-16: peakpos = 0.0 param = numpy.array([yy[int(i)] - zz[int(i)], peakpos ,sig]) largest = param else: param2 = numpy.array([yy[int(i)] - zz [int(i)], xx[int(i)] ,sig]) param = numpy.concatenate((param,param2)) if (param2[0] > largest[0]): largest = param2 largest_index = j j = j + 1 xw = numpy.resize(xx,(npoints,1)) if (0): sy = numpy.sqrt(abs(yy)) yw = numpy.resize(yy-zz,(npoints,1)) sy = numpy.resize(sy,(npoints,1)) datawork = numpy.concatenate((xw,yw,sy),1) else: yw = numpy.resize(yy-zz,(npoints,1)) datawork = numpy.concatenate((xw,yw),1) cons = numpy.zeros((3,len(param)),numpy.float) cons [0] [0:len(param):3] = CPOSITIVE #force peaks to stay around their position if (1): cons [0] [1:len(param):3] = CQUOTED #This does not work!!!! #FWHM should be given in terms of X not of points! #cons [1] [1:len(param):3] = param[1:len(param):3]-0.5*search_fwhm #cons [2] [1:len(param):3] = param[1:len(param):3]+0.5*search_fwhm if len(xw) > search_fwhm: fwhmx = numpy.fabs(xw[int(search_fwhm)]-xw[0]) cons [1] [1:len(param):3] = param[1:len(param):3]-0.5*fwhmx cons [2] [1:len(param):3] = param[1:len(param):3]+0.5*fwhmx else: cons [1] [1:len(param):3] = numpy.ones(shape(param[1:len(param):3]),numpy.float)*min(xw) cons [2] [1:len(param):3] = numpy.ones(shape(param[1:len(param):3]),numpy.float)*max(xw) if 0: cons [0] [2:len(param):3] = CFACTOR cons [1] [2:len(param):3] = 2 cons [2] [2:len(param):3] = 1.0 cons [0] [2] = CPOSITIVE cons [1] [2] = 0 cons [2] [2] = 0 else: cons [0] [2:len(param):3] = CPOSITIVE fittedpar, chisq, sigmapar = LeastSquaresFit(SpecfitFuns.gauss,param, datawork, weightflag=self.config['WeightFlag'], maxiter=4,constrains=cons.tolist()) #I already have the estimation #yw=yy-zz-SpecfitFuns.gauss(fittedpar,xx) #if DEBUG: # SimplePlot.plot([xx,yw]) #print self.seek(yw,\ # fwhm=search_fwhm,\ # sensitivity=search_sens,\ # yscaling=yscaling) #else: # #Use SPEC estimate ... # peakpos,height,myidx = SpecArithmetic.search_peak(xx,yy-zz) # fwhm,cfwhm = SpecArithmetic.search_fwhm(xx,yy-zz, # peak=peakpos,index=myidx) # xx = numpy.array(xx) # if npoints > 2: # #forget SPEC estimation of fwhm # fwhm=5*abs(xx[npoints-1]-xx[0])/npoints # fittedpar=[height,peakpos,fwhm] # largest=numpy.array([height,peakpos,fwhm]) # peaks=[peakpos] cons = numpy.zeros((3,len(fittedpar)),numpy.float) j=0 for i in range(len(peaks)): #Setup height area constrains if self.config['NoConstrainsFlag'] == 0: if self.config['HeightAreaFlag']: #POSITIVE = 1 cons[0] [j] = 1 cons[1] [j] = 0 cons[2] [j] = 0 j=j+1 #Setup position constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PositionFlag']: #QUOTED = 2 cons[0][j]=2 cons[1][j]=min(xx) cons[2][j]=max(xx) j=j+1 #Setup positive FWHM constrains if self.config['NoConstrainsFlag'] == 0: if self.config['PosFwhmFlag']: #POSITIVE=1 cons[0][j]=1 cons[1][j]=0 cons[2][j]=0 if self.config['SameFwhmFlag']: if (i != largest_index): #FACTOR=4 cons[0][j]=4 cons[1][j]=3*largest_index+2 cons[2][j]=1.0 j=j+1 return fittedpar,cons
def fitMultipleSpectra(self, x=None, y=None, xmin=None, xmax=None, configuration=None, concentrations=False, ysum=None, weight=None, refit=True, livetime=None): """ This method performs the actual fit. The y keyword is the only mandatory input argument. :param x: 1D array containing the x axis (usually the channels) of the spectra. :param y: 3D array containing the spectra as [nrows, ncolumns, nchannels] :param xmin: lower limit of the fitting region :param xmax: upper limit of the fitting region :param weight: 0 Means no weight, 1 Use an average weight, 2 Individual weights (slow) :param concentrations: 0 Means no calculation, 1 Calculate them :param refit: if False, no check for negative results. Default is True. :livetime: It will be used if not different from None and concentrations are to be calculated by using fundamental parameters with automatic time. The default is None. :return: A dictionnary with the parameters, uncertainties, concentrations and names as keys. """ if y is None: raise RuntimeError("y keyword argument is mandatory!") if hasattr(y, "info") and hasattr(y, "data"): data = y.data mcaIndex = y.info.get("McaIndex", -1) else: data = y mcaIndex = -1 if x is None: if hasattr(y, "info") and hasattr(y, "x"): x = y.x[0] if livetime is None: if hasattr(y, "info"): if "McaLiveTime" in y.info: livetime = y.info["McaLiveTime"] t0 = time.time() if configuration is not None: self._mcaTheory.setConfiguration(configuration) elif self._config is None: raise ValueError("Fit configuration missing") else: _logger.debug("Setting default configuration") self._mcaTheory.setConfiguration(self._config) # read the current configuration # it is a copy, we can modify it at will config = self._mcaTheory.getConfiguration() if xmin is None: xmin = config['fit']['xmin'] if xmax is None: xmax = config['fit']['xmax'] toReconfigure = False # if concentrations and use times, it needs to be reconfigured # without using times and correct later on. If the concentrations # are to be calculated from internal standard there is no need to # raise an exception either. autotime = 0 liveTimeFactor = 1.0 if not concentrations: # ignore any time information to prevent unnecessary errors when # setting the fitting data whithout the time information if config['concentrations'].get("useautotime", 0): config['concentrations']["useautotime"] = 0 toReconfigure = True elif config["concentrations"]["usematrix"]: if config['concentrations'].get("useautotime", 0): config['concentrations']["useautotime"] = 0 toReconfigure = True else: # we are calculating concentrations from fundamental parameters autotime = config['concentrations'].get("useautotime", 0) nSpectra = data.size // data.shape[mcaIndex] if autotime: if livetime is None: txt = "Automatic time requested but no time information provided" raise RuntimeError(txt) elif numpy.isscalar(livetime): liveTimeFactor = \ float(config['concentrations']["time"]) / livetime elif livetime.size == nSpectra: liveTimeFactor = \ float(config['concentrations']["time"]) / livetime else: raise RuntimeError( \ "Number of live times not equal number of spectra") config['concentrations']["useautotime"] = 0 toReconfigure = True # use of strategies is not supported for the time being strategy = config['fit'].get('strategyflag', 0) if strategy: raise RuntimeError("Strategies are incompatible with fast fit") # background if config['fit']['stripflag']: if config['fit']['stripalgorithm'] == 1: _logger.debug("SNIP") else: raise RuntimeError("Please use the faster SNIP background") if weight is None: # dictated by the file weight = config['fit']['fitweight'] if weight: # individual pixel weights (slow) weightPolicy = 2 else: # No weight weightPolicy = 0 elif weight == 1: # use average weight from the sum spectrum weightPolicy = 1 if not config['fit']['fitweight']: config['fit']['fitweight'] = 1 toReconfigure = True elif weight == 2: # individual pixel weights (slow) weightPolicy = 2 if not config['fit']['fitweight']: config['fit']['fitweight'] = 1 toReconfigure = True weight = 1 else: # No weight weightPolicy = 0 if config['fit']['fitweight']: config['fit']['fitweight'] = 0 toReconfigure = True weight = 0 if not config['fit']['linearfitflag']: #make sure we force a linear fit config['fit']['linearfitflag'] = 1 toReconfigure = True if toReconfigure: # we must configure again the fit self._mcaTheory.setConfiguration(config) if len(data.shape) != 3: txt = "For the time being only three dimensional arrays supported" raise IndexError(txt) elif mcaIndex not in [-1, 2]: txt = "For the time being only mca arrays supported" raise IndexError(txt) else: # if the cumulated spectrum is present it should be better nRows = data.shape[0] nColumns = data.shape[1] nPixels = nRows * nColumns if ysum is not None: firstSpectrum = ysum elif weightPolicy == 1: # we need to calculate the sum spectrum to derive the uncertainties totalSpectra = data.shape[0] * data.shape[1] jStep = min(5000, data.shape[1]) ysum = numpy.zeros((data.shape[mcaIndex],), numpy.float) for i in range(0, data.shape[0]): if i == 0: chunk = numpy.zeros((data.shape[0], jStep), numpy.float) jStart = 0 while jStart < data.shape[1]: jEnd = min(jStart + jStep, data.shape[1]) ysum += data[i, jStart:jEnd, :].sum(axis=0, dtype=numpy.float) jStart = jEnd firstSpectrum = ysum elif not concentrations: # just one spectrum is enough for the setup firstSpectrum = data[0, 0, :] else: firstSpectrum = data[0, :, :].sum(axis=0, dtype=numpy.float) # make sure we calculate the matrix of the contributions self._mcaTheory.enableOptimizedLinearFit() # initialize the fit # print("xmin = ", xmin) # print("xmax = ", xmax) # print("firstShape = ", firstSpectrum.shape) self._mcaTheory.setData(x=x, y=firstSpectrum, xmin=xmin, xmax=xmax) # and initialize the derivatives self._mcaTheory.estimate() # now we can get the derivatives respect to the free parameters # These are the "derivatives" respect to the peaks # linearMatrix = self._mcaTheory.linearMatrix # but we are still missing the derivatives from the background nFree = 0 freeNames = [] nFreeBackgroundParameters = 0 for i, param in enumerate(self._mcaTheory.PARAMETERS): if self._mcaTheory.codes[0][i] != ClassMcaTheory.Gefit.CFIXED: nFree += 1 freeNames.append(param) if i < self._mcaTheory.NGLOBAL: nFreeBackgroundParameters += 1 if nFree == 0: txt = "No free parameters to be fitted!\n" txt += "No peaks inside fitting region?" raise ValueError(txt) #build the matrix of derivatives derivatives = None idx = 0 for i, param in enumerate(self._mcaTheory.PARAMETERS): if self._mcaTheory.codes[0][i] == ClassMcaTheory.Gefit.CFIXED: continue deriv= self._mcaTheory.linearMcaTheoryDerivative(self._mcaTheory.parameters, i, self._mcaTheory.xdata) deriv.shape = -1 if derivatives is None: derivatives = numpy.zeros((deriv.shape[0], nFree), numpy.float) derivatives[:, idx] = deriv idx += 1 #loop for anchors xdata = self._mcaTheory.xdata if config['fit']['stripflag']: anchorslist = [] if config['fit']['stripanchorsflag']: if config['fit']['stripanchorslist'] is not None: ravelled = numpy.ravel(xdata) for channel in config['fit']['stripanchorslist']: if channel <= ravelled[0]:continue index = numpy.nonzero(ravelled >= channel)[0] if len(index): index = min(index) if index > 0: anchorslist.append(index) if len(anchorslist) == 0: anchorlist = [0, self._mcaTheory.ydata.size - 1] anchorslist.sort() # find the indices to be used for selecting the appropriate data # if the original x data were not ordered we have a problem # TODO: check for original ordering. if x is None: # we have an enumerated channels axis iXMin = xdata[0] iXMax = xdata[-1] else: iXMin = numpy.nonzero(x <= xdata[0])[0][-1] iXMax = numpy.nonzero(x >= xdata[-1])[0][0] # numpy 1.11.0 returns an array on previous expression # and then complains about a future deprecation warning # because of using an array and not an scalar in the selection if hasattr(iXMin, "shape"): if len(iXMin.shape): iXMin = iXMin[0] if hasattr(iXMax, "shape"): if len(iXMax.shape): iXMax = iXMax[0] dummySpectrum = firstSpectrum[iXMin:iXMax+1].reshape(-1, 1) # print("dummy = ", dummySpectrum.shape) # allocate the output buffer results = numpy.zeros((nFree, nRows, nColumns), numpy.float32) uncertainties = numpy.zeros((nFree, nRows, nColumns), numpy.float32) #perform the initial fit _logger.debug("Configuration elapsed = %f", time.time() - t0) t0 = time.time() totalSpectra = data.shape[0] * data.shape[1] jStep = min(100, data.shape[1]) if weightPolicy == 2: SVD = False sigma_b = None elif weightPolicy == 1: # the +1 is to prevent misbehavior due to weights less than 1.0 sigma_b = 1 + numpy.sqrt(dummySpectrum)/nPixels SVD = True else: SVD = True sigma_b = None last_svd = None for i in range(0, data.shape[0]): #print(i) #chunks of nColumns spectra if i == 0: chunk = numpy.zeros((dummySpectrum.shape[0], jStep), numpy.float) jStart = 0 while jStart < data.shape[1]: jEnd = min(jStart + jStep, data.shape[1]) chunk[:,:(jEnd - jStart)] = data[i, jStart:jEnd, iXMin:iXMax+1].T if config['fit']['stripflag']: for k in range(jStep): # obtain the smoothed spectrum background=SpecfitFuns.SavitskyGolay(chunk[:, k], config['fit']['stripfilterwidth']) lastAnchor = 0 for anchor in anchorslist: if (anchor > lastAnchor) and (anchor < background.size): background[lastAnchor:anchor] =\ SpecfitFuns.snip1d(background[lastAnchor:anchor], config['fit']['snipwidth'], 0) lastAnchor = anchor if lastAnchor < background.size: background[lastAnchor:] =\ SpecfitFuns.snip1d(background[lastAnchor:], config['fit']['snipwidth'], 0) chunk[:, k] -= background # perform the multiple fit to all the spectra in the chunk #print("SHAPES") #print(derivatives.shape) #print(chunk[:,:(jEnd - jStart)].shape) ddict=lstsq(derivatives, chunk[:,:(jEnd - jStart)], sigma_b=sigma_b, weight=weight, digested_output=True, svd=SVD, last_svd=last_svd) last_svd = ddict.get('svd', None) parameters = ddict['parameters'] results[:, i, jStart:jEnd] = parameters uncertainties[:, i, jStart:jEnd] = ddict['uncertainties'] jStart = jEnd t = time.time() - t0 _logger.debug("First fit elapsed = %f", t) if t > 0.: _logger.debug("Spectra per second = %f", data.shape[0]*data.shape[1]/float(t)) t0 = time.time() # cleanup zeros # start with the parameter with the largest amount of negative values if refit: negativePresent = True else: negativePresent = False nFits = 0 while negativePresent: zeroList = [] #totalNegative = 0 for i in range(nFree): #we have to skip the background parameters if i >= nFreeBackgroundParameters: t = results[i] < 0 tsum = t.sum() if tsum > 0: zeroList.append((tsum, i, t)) #totalNegative += tsum #print("totalNegative = ", totalNegative) if len(zeroList) == 0: negativePresent = False continue if nFits > (2 * (nFree - nFreeBackgroundParameters)): # we are probably in an endless loop # force negative pixels for item in zeroList: i = item[1] badMask = item[2] results[i][badMask] = 0.0 _logger.warning("WARNING: %d pixels of parameter %s forced to zero", item[0], freeNames[i]) continue zeroList.sort() zeroList.reverse() badParameters = [] badParameters.append(zeroList[0][1]) badMask = zeroList[0][2] if 1: # prevent and endless loop if two or more parameters have common pixels where they are # negative and one of them remains negative when forcing other one to zero for i, item in enumerate(zeroList): if item[1] not in badParameters: if item[0] > 0: #check if they have common negative pixels t = badMask * item[-1] if t.sum() > 0: badParameters.append(item[1]) badMask = t if badMask.sum() < (0.0025 * nPixels): # fit not worth for i in badParameters: results[i][badMask] = 0.0 uncertainties[i][badMask] = 0.0 _logger.debug("WARNING: %d pixels of parameter %s set to zero", badMask.sum(), freeNames[i]) else: _logger.debug("Number of secondary fits = %d", nFits + 1) nFits += 1 A = derivatives[:, [i for i in range(nFree) if i not in badParameters]] #assume we'll not have too many spectra if data.dtype not in [numpy.float32, numpy.float64]: if data.itemsize < 5: data_dtype = numpy.float32 else: data_dtype = numpy.floa64 else: data_dtype = data.dtype try: if data.dtype != data_dtype: spectra = numpy.zeros((int(badMask.sum()), 1 + iXMax - iXMin), data_dtype) spectra[:] = data[badMask, iXMin:iXMax+1] else: spectra = data[badMask, iXMin:iXMax+1] spectra.shape = badMask.sum(), -1 except TypeError: # in case of dynamic arrays, two dimensional indices are not # supported by h5py spectra = numpy.zeros((int(badMask.sum()), 1 + iXMax - iXMin), data_dtype) selectedIndices = numpy.nonzero(badMask > 0) tmpData = numpy.zeros((1, 1 + iXMax - iXMin), data_dtype) oldDataRow = -1 j = 0 for i in range(len(selectedIndices[0])): j = selectedIndices[0][i] if j != oldDataRow: tmpData = data[j] olddataRow = j spectra[i] = tmpData[selectedIndices[1][i], iXMin:iXMax+1] spectra = spectra.T # if config['fit']['stripflag']: for k in range(spectra.shape[1]): # obtain the smoothed spectrum background=SpecfitFuns.SavitskyGolay(spectra[:, k], config['fit']['stripfilterwidth']) lastAnchor = 0 for anchor in anchorslist: if (anchor > lastAnchor) and (anchor < background.size): background[lastAnchor:anchor] =\ SpecfitFuns.snip1d(background[lastAnchor:anchor], config['fit']['snipwidth'], 0) lastAnchor = anchor if lastAnchor < background.size: background[lastAnchor:] =\ SpecfitFuns.snip1d(background[lastAnchor:], config['fit']['snipwidth'], 0) spectra[:, k] -= background ddict = lstsq(A, spectra, sigma_b=sigma_b, weight=weight, digested_output=True, svd=SVD) idx = 0 for i in range(nFree): if i in badParameters: results[i][badMask] = 0.0 uncertainties[i][badMask] = 0.0 else: results[i][badMask] = ddict['parameters'][idx] uncertainties[i][badMask] = ddict['uncertainties'][idx] idx += 1 if refit: t = time.time() - t0 _logger.debug("Fit of negative peaks elapsed = %f", t) t0 = time.time() outputDict = {'parameters':results, 'uncertainties':uncertainties, 'names':freeNames} if concentrations: # check if an internal reference is used and if it is set to auto #################################################### # CONCENTRATIONS cTool = ConcentrationsTool.ConcentrationsTool() cToolConf = cTool.configure() cToolConf.update(config['concentrations']) fitFirstSpectrum = False if config['concentrations']['usematrix']: _logger.debug("USING MATRIX") if config['concentrations']['reference'].upper() == "AUTO": fitFirstSpectrum = True elif autotime: # we have to calculate with the time in the configuration # and correct later on cToolConf["autotime"] = 0 fitresult = {} if fitFirstSpectrum: # we have to fit the "reference" spectrum just to get the reference element mcafitresult = self._mcaTheory.startfit(digest=0, linear=True) # if one of the elements has zero area this cannot be made directly fitresult['result'] = self._mcaTheory.imagingDigestResult() fitresult['result']['config'] = config concentrationsResult, addInfo = cTool.processFitResult(config=cToolConf, fitresult=fitresult, elementsfrommatrix=False, fluorates=self._mcaTheory._fluoRates, addinfo=True) # and we have to make sure that all the areas are positive for group in fitresult['result']['groups']: if fitresult['result'][group]['fitarea'] <= 0.0: # give a tiny area fitresult['result'][group]['fitarea'] = 1.0e-6 config['concentrations']['reference'] = addInfo['ReferenceElement'] else: fitresult['result'] = {} fitresult['result']['config'] = config fitresult['result']['groups'] = [] idx = 0 for i, param in enumerate(self._mcaTheory.PARAMETERS): if self._mcaTheory.codes[0][i] == Gefit.CFIXED: continue if i < self._mcaTheory.NGLOBAL: # background pass else: fitresult['result']['groups'].append(param) fitresult['result'][param] = {} # we are just interested on the factor to be applied to the area to get the # concentrations fitresult['result'][param]['fitarea'] = 1.0 fitresult['result'][param]['sigmaarea'] = 1.0 idx += 1 concentrationsResult, addInfo = cTool.processFitResult(config=cToolConf, fitresult=fitresult, elementsfrommatrix=False, fluorates=self._mcaTheory._fluoRates, addinfo=True) nValues = 1 if len(concentrationsResult['layerlist']) > 1: nValues += len(concentrationsResult['layerlist']) nElements = len(list(concentrationsResult['mass fraction'].keys())) massFractions = numpy.zeros((nValues * nElements, nRows, nColumns), numpy.float32) referenceElement = addInfo['ReferenceElement'] referenceTransitions = addInfo['ReferenceTransitions'] _logger.debug("Reference <%s> transition <%s>", referenceElement, referenceTransitions) if referenceElement in ["", None, "None"]: _logger.debug("No reference") counter = 0 for i, group in enumerate(fitresult['result']['groups']): if group.lower().startswith("scatter"): _logger.debug("skept %s", group) continue outputDict['names'].append("C(%s)" % group) if counter == 0: if hasattr(liveTimeFactor, "shape"): liveTimeFactor.shape = results[nFreeBackgroundParameters+i].shape massFractions[counter] = liveTimeFactor * \ results[nFreeBackgroundParameters+i] * \ (concentrationsResult['mass fraction'][group] / \ fitresult['result'][group]['fitarea']) counter += 1 if len(concentrationsResult['layerlist']) > 1: for layer in concentrationsResult['layerlist']: outputDict['names'].append("C(%s)-%s" % (group, layer)) massFractions[counter] = liveTimeFactor * \ results[nFreeBackgroundParameters+i] * \ (concentrationsResult[layer]['mass fraction'][group] / \ fitresult['result'][group]['fitarea']) counter += 1 else: _logger.debug("With reference") idx = None testGroup = referenceElement+ " " + referenceTransitions.split()[0] for i, group in enumerate(fitresult['result']['groups']): if group == testGroup: idx = i if idx is None: raise ValueError("Invalid reference: <%s> <%s>" %\ (referenceElement, referenceTransitions)) group = fitresult['result']['groups'][idx] referenceArea = fitresult['result'][group]['fitarea'] referenceConcentrations = concentrationsResult['mass fraction'][group] goodIdx = results[nFreeBackgroundParameters+idx] > 0 massFractions[idx] = referenceConcentrations counter = 0 for i, group in enumerate(fitresult['result']['groups']): if group.lower().startswith("scatter"): _logger.debug("skept %s", group) continue outputDict['names'].append("C(%s)" % group) goodI = results[nFreeBackgroundParameters+i] > 0 tmp = results[nFreeBackgroundParameters+idx][goodI] massFractions[counter][goodI] = (results[nFreeBackgroundParameters+i][goodI]/(tmp + (tmp == 0))) *\ ((referenceArea/fitresult['result'][group]['fitarea']) *\ (concentrationsResult['mass fraction'][group])) counter += 1 if len(concentrationsResult['layerlist']) > 1: for layer in concentrationsResult['layerlist']: outputDict['names'].append("C(%s)-%s" % (group, layer)) massFractions[counter][goodI] = (results[nFreeBackgroundParameters+i][goodI]/(tmp + (tmp == 0))) *\ ((referenceArea/fitresult['result'][group]['fitarea']) *\ (concentrationsResult[layer]['mass fraction'][group])) counter += 1 outputDict['concentrations'] = massFractions t = time.time() - t0 _logger.debug("Calculation of concentrations elapsed = %f", t) #################################################### return outputDict
def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, lineProjectionMode): """Returns the profile values and the polygon in the given ROI. Works in image coordinates. See :func:`getAlignedROIProfileCurve`. """ row0, col0 = roiStart row1, col1 = roiEnd deltaRow = abs(row1 - row0) deltaCol = abs(col1 - col0) if (lineProjectionMode == 'X' or (lineProjectionMode == 'D' and deltaCol >= deltaRow)): nPoints = deltaCol + 1 coordsRange = col0, col1 else: # 'Y' or ('D' and deltaCol < deltaRow) nPoints = deltaRow + 1 coordsRange = row0, row1 if nPoints == 1: # all points are the same if DEBUG: print("START AND END POINT ARE THE SAME!!") return None # the coordinates of the reference points x0 = numpy.arange(image.shape[0], dtype=numpy.float) y0 = numpy.arange(image.shape[1], dtype=numpy.float) if roiWidth < 1: x = numpy.zeros((nPoints, 2), numpy.float) x[:, 0] = numpy.linspace(row0, row1, nPoints) x[:, 1] = numpy.linspace(col0, col1, nPoints) # perform the interpolation ydata = SpecfitFuns.interpol((x0, y0), image, x) roiPolygonCols = numpy.array((col0, col1), dtype=numpy.float) roiPolygonRows = numpy.array((row0, row1), dtype=numpy.float) else: # find m and b in the line y = mx + b m = (row1 - row0) / float((col1 - col0)) # Not used: b = row0 - m * col0 alpha = numpy.arctan(m) # imagine the following sequence # - change origin to the first point # - clock-wise rotation to bring the line on the x axis of a new system # so that the points (col0, row0) and (col1, row1) # become (x0, 0) (x1, 0). # - counter clock-wise rotation to get the points (x0, -0.5 width), # (x0, 0.5 width), (x1, 0.5 * width) and (x1, -0.5 * width) back to # the original system. # - restore the origin to (0, 0) # - if those extremes are inside the image the selection is acceptable cosalpha = numpy.cos(alpha) sinalpha = numpy.sin(alpha) newCol0 = 0.0 newCol1 = (col1 - col0) * cosalpha + (row1 - row0) * sinalpha newRow0 = 0.0 newRow1 = -(col1 - col0) * sinalpha + (row1 - row0) * cosalpha if DEBUG: print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) tmpX = numpy.linspace(newCol0, newCol1, nPoints).astype(numpy.float) rotMatrix = numpy.zeros((2, 2), dtype=numpy.float) rotMatrix[0, 0] = cosalpha rotMatrix[0, 1] = -sinalpha rotMatrix[1, 0] = sinalpha rotMatrix[1, 1] = cosalpha if DEBUG: # test if I recover the original points testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) print("Recovered X0 = %f" % (colRow[0, 0] + col0)) print("Recovered Y0 = %f" % (colRow[1, 0] + row0)) print("It should be = %f, %f" % (col0, row0)) testX[0, 0] = newCol1 testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) print("Recovered X1 = %f" % (colRow[0, 0] + col0)) print("Recovered Y1 = %f" % (colRow[1, 0] + row0)) print("It should be = %f, %f" % (col1, row1)) # find the drawing limits testX = numpy.zeros((2, 4), numpy.float) testX[0, 0] = newCol0 testX[0, 1] = newCol0 testX[0, 2] = newCol1 testX[0, 3] = newCol1 testX[1, 0] = newRow0 - 0.5 * roiWidth testX[1, 1] = newRow0 + 0.5 * roiWidth testX[1, 2] = newRow1 + 0.5 * roiWidth testX[1, 3] = newRow1 - 0.5 * roiWidth colRow = numpy.dot(rotMatrix, testX) colLimits0 = colRow[0, :] + col0 rowLimits0 = colRow[1, :] + row0 for a in rowLimits0: if (a >= image.shape[0]) or (a < 0): if DEBUG: print("outside row limits", a) return None for a in colLimits0: if (a >= image.shape[1]) or (a < 0): if DEBUG: print("outside column limits", a) return None r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: if DEBUG: print("r0 > r1", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, nPoints), numpy.float) # oversampling solves noise introduction issues oversampling = roiWidth + 1 oversampling = min(oversampling, 21) ncontributors = roiWidth * oversampling iterValues = numpy.linspace(-0.5 * roiWidth, 0.5 * roiWidth, ncontributors) tmpMatrix = numpy.zeros((nPoints * len(iterValues), 2), dtype=numpy.float) x[0, :] = tmpX offset = 0 for i in iterValues: x[1, :] = i colRow = numpy.dot(rotMatrix, x) colRow[0, :] += col0 colRow[1, :] += row0 # it is much faster to make one call to the interpolating # routine than making many calls tmpMatrix[offset:(offset + nPoints), 0] = colRow[1, :] tmpMatrix[offset:(offset + nPoints), 1] = colRow[0, :] offset += nPoints ydata = SpecfitFuns.interpol((x0, y0), image, tmpMatrix) ydata.shape = len(iterValues), nPoints ydata = ydata.sum(axis=0) # deal with the oversampling ydata /= oversampling roiPolygonCols, roiPolygonRows = colLimits0, rowLimits0 return { 'profileValues': ydata, 'profileCoordsRange': coordsRange, 'roiPolygonCols': roiPolygonCols, 'roiPolygonRows': roiPolygonRows }