nodfitting.py
11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
import random
import numpy
from scipy import optimize, signal
def Gaussian(off, dx, dy, height, center_x, center_y, width_x, width_y):
"""Returns a gaussian function with the given parameters"""
return lambda x,y: off + dx*x + dy*y + height*numpy.exp(
-(((center_x-x)/width_x)**2+((center_y-y)/width_y)**2)/2)
def gaussian(off, dx, dy, height, x0, y0, wx, wy, rot):
"""Returns a gaussian function with the given parameters"""
return lambda x,y: off + dx*x + dy*y + height*numpy.exp(
-((((x-x0)*numpy.cos(rot)-(y-y0)*numpy.sin(rot))/wx)**2
+ (((x-x0)*numpy.sin(rot)+(y-y0)*numpy.cos(rot))/wy)**2)/2)
def gaussmoments(data, x, y):
"""Returns (off, dx, dy, height, x, y, width_x, width_y, rotation)
the gaussian parameters of a 2D distribution by calculating its
moments """
off = 0.0
dx = 0.0
dy = 0.0
rot = 0.0
width_x = 1.0
width_y = 1.0
height = data.max() - data.min()
return off, dx, dy, height, x, y, width_x, width_y, rot
def fitgaussian(data, l, b, fast=False):
"""Returns (off, dx, dy, height, x, y, width_x, width_y, rot)
the gaussian parameters of a 2D distribution found by a fit"""
params = gaussmoments(data, l, b)
#errorfunction = lambda p: numpy.ravel(gaussian(*p)(*numpy.indices(data.shape)) - data)
errorfunction = lambda p: numpy.ravel(gaussian(*p)(*numpy.indices(data.shape)) - data)
if fast:
try:
p, cov, info, mesg, success = \
optimize.leastsq(errorfunction, params, full_output=1, \
maxfev=200, ftol=1.e-6, xtol=1.e-5)
except:
return None, None
else:
try:
p, cov, info, mesg, success = \
optimize.leastsq(errorfunction, params, full_output=1)
except:
return None, None
#if success > 0 and success < 3:
if numpy.shape(cov) != ():
chisq = numpy.sum(info["fvec"]*info["fvec"])
dof = len(p)
rms = numpy.sqrt(chisq/float(dof))
error = []
for i in range(dof):
error.append(numpy.sqrt(numpy.abs(cov[i,i]))*rms)
return p, error
else:
return None, None
def gauss_kern(size, sizey=None):
""" Returns a normalized 2D gauss kernel array for convolutions """
size = int(size)
if not sizey:
sizey = size
else:
sizey = int(sizey)
x, y = numpy.mgrid[-size:size+1, -sizey:sizey+1]*0.1
g = numpy.exp(-(x**2/float(size) + y**2/float(sizey)))
return g / g.sum()
def smooth_image(im, n, ny=None):
""" blurs the image by convolving with a gaussian kernel of typical
size n. The optional keyword argument ny allows for a different
size in the y direction.
"""
g = gauss_kern(n, sizey=ny)
improc = signal.convolve(im, g, mode='same')
return(improc)
def sinc_kern(size, sizey=None):
""" Returns a normalized 2D sinc kernel array for convolutions """
size = int(size)
if not sizey:
sizey = size
else:
sizey = int(sizey)
x, y = numpy.mgrid[-size:size+1, -sizey:sizey+1] * numpy.pi/180
g = numpy.sinc(x * y)
return g / g.sum()
def pump_image(im, n, ny=None):
""" resize the image by convolving with a sinc kernel of typical
size n. The optional keyword argument ny allows for a different
size in the y direction.
"""
g = sinc_kern(n, sizey=ny)
improc = signal.convolve(im, g, mode='same')
return improc
def ausgleich(inp, g, EPS=1e-9):
n = len(inp[0])
m = len(inp)
if m < n:
n = min(n, m)
for i in range(m):
inp[i][n-1] = inp[i][-1]
q = []
out = []
mat = []
sum = []
for i in range(n):
q.append(0.0)
out.append(0.0)
mat.append([])
for j in range(n):
mat[i].append(0.0)
for i in range(n):
sum.append([])
for j in range(n):
sum[i].append([])
for k in range(n):
sum[i][j].append(0.0)
for i in range(n):
for j in range(i,n):
for k in range(m):
sum[0][i][j] += inp[k][i] * inp[k][j] * g[k]
for k in range(n-1):
if abs(sum[k][k][k]) < EPS:
print "no solution possible", k, '1'
return None
for j in range(k, n-1):
for i in range(j, n-1):
sum[k+1][j+1][i+1] = sum[k][j+1][i+1] - sum[k][k][j+1] \
* sum[k][k][i+1] / sum[k][k][k]
for k in range(n-2, -1, -1):
if abs(sum[k][k][k]) < EPS:
print "no solution possible", k, '2'
return None
for i in range(n-1, k, -1):
if i == n-1:
out[k] = sum[k][k][i] / sum[k][k][k]
else:
out[k] -= out[i] * sum[k][k][i] / sum[k][k][k]
return out[:-1]
def _general_function(params, xdata, ydata, function):
return function(xdata, *params) - ydata
def _weighted_general_function(params, xdata, ydata, function, weights):
return weights * (function(xdata, *params) - ydata)
def curve_fit(f, xdata, ydata, p0=None, sigma=None, absolute_sigma=False, **kw):
"""
Use non-linear least squares to fit a function, f, to data.
Assumes ``ydata = f(xdata, *params) + eps``
Parameters
----------
f : callable
The model function, f(x, ...). It must take the independent
variable as the first argument and the parameters to fit as
separate remaining arguments.
xdata : An M-length sequence or an (k,M)-shaped array
for functions with k predictors.
The independent variable where the data is measured.
ydata : M-length sequence
The dependent data --- nominally f(xdata, ...)
p0 : None, scalar, or N-length sequence
Initial guess for the parameters. If None, then the initial
values will all be 1 (if the number of parameters for the function
can be determined using introspection, otherwise a ValueError
is raised).
sigma : None or M-length sequence, optional
If not None, these values are used as weights in the
least-squares problem.
absolute_sigma : bool, optional
If False, `sigma` denotes relative weights of the data points.
The returned covariance matrix `pcov` is based on *estimated*
errors in the data, and is not affected by the overall
magnitude of the values in `sigma`. Only the relative
magnitudes of the `sigma` values matter.
If True, `sigma` describes one standard deviation errors of
the input data points. The estimated covariance in `pcov` is
based on these values.
Returns
-------
popt : array
Optimal values for the parameters so that the sum of the squared error
of ``f(xdata, *popt) - ydata`` is minimized
pcov : 2d array
The estimated covariance of popt. The diagonals provide the variance
of the parameter estimate. To compute one standard deviation errors
on the parameters use ``perr = np.sqrt(np.diag(pcov))``.
How the `sigma` parameter affects the estimated covariance
depends on `absolute_sigma` argument, as described above.
See Also
--------
leastsq
Notes
-----
The algorithm uses the Levenberg-Marquardt algorithm through `leastsq`.
Additional keyword arguments are passed directly to that algorithm.
Examples
--------
>>> import numpy as np
>>> from scipy.optimize import curve_fit
>>> def func(x, a, b, c):
... return a * np.exp(-b * x) + c
>>> xdata = np.linspace(0, 4, 50)
>>> y = func(xdata, 2.5, 1.3, 0.5)
>>> ydata = y + 0.2 * np.random.normal(size=len(xdata))
>>> popt, pcov = curve_fit(func, xdata, ydata)
"""
if p0 is None:
# determine number of parameters by inspecting the function
import inspect
args, varargs, varkw, defaults = inspect.getargspec(f)
if len(args) < 2:
msg = "Unable to determine number of fit parameters."
#raise ValueError(msg)
return ValueError(msg), []
if 'self' in args:
p0 = [1.0] * (len(args)-2)
else:
p0 = [1.0] * (len(args)-1)
# Check input arguments
if numpy.isscalar(p0):
p0 = array([p0])
ydata = numpy.asanyarray(ydata)
if isinstance(xdata, (list, tuple)):
# `xdata` is passed straight to the user-defined `f`, so allow
# non-array_like `xdata`.
xdata = numpy.asarray(xdata)
args = (xdata, ydata, f)
if sigma is None:
func = _general_function
else:
func = _weighted_general_function
args += (1.0 / numpy.asarray(sigma),)
# Remove full_output from kw, otherwise we're passing it in twice.
return_full = kw.pop('full_output', False)
res = optimize.leastsq(func, p0, args=args, full_output=1, **kw)
(popt, pcov, infodict, errmsg, ier) = res
if ier not in [1, 2, 3, 4]:
msg = "Optimal parameters not found: " + errmsg
raise RuntimeError(msg)
#return RuntimeError(msg), []
if pcov is None:
# indeterminate covariance
pcov = numpy.zeros((len(popt), len(popt)), dtype=float)
pcov.fill(numpy.inf)
reduced_chi_sq = 99999999.0
elif not absolute_sigma:
if len(ydata) > len(p0):
s_sq = (numpy.asarray(func(popt, *args))**2).sum() / (len(ydata) - len(p0) - 1)
pcov = pcov * s_sq
else:
pcov.fill(numpy.inf)
reduced_chi_sq = 99999999.0
if return_full:
return popt, pcov, infodict, errmsg, ier
else:
chi_sq = (numpy.asarray(func(popt, *args))**2).sum()
return popt, pcov
def correlate(data1, data2, fmax=0.0, out=False, sort=False):
d1 = numpy.ravel(1.0*data1)
d2 = numpy.ravel(1.0*data2)
mask1 = ~numpy.isnan(d1) * ~numpy.isnan(d2)
mask2 = (d1 > fmax) * (d2 > fmax)
mask = mask1*mask2
d1 = d1[mask]
d2 = d2[mask]
if sort:
d1.sort()
d2.sort()
d = numpy.array([d1, numpy.ones(len(d1))])
x = numpy.linalg.lstsq(d.T, d2)
b, a = x[0]
if out:
return a, b, d1, d2
else:
return a, b
def main():
import pylab
r = numpy.pi/180
# Create the gaussian data
Xin, Yin = numpy.mgrid[0:201, 0:201]
#data = gaussian(0, 0.0133333, 0.01, 3, 100, 100, 50, 40)(Xin, Yin) #+ random.random()
params = (0, 0.0, 0.0, 1, 101, 101, 15, 25, 30*r)
data = gaussian(*params)(Xin, Yin)
#pylab.matshow(data, cmap=pylab.cm.gist_earth_r)
pylab.matshow(data)
dmax = data.argmax()
ydim, xdim = data.shape
l = dmax/xdim
b = dmax - l*xdim
params = fitgaussian(data, l, b)
bparams = [0.0, 0.0, 0.0] + list(params[0])[3:]
fit = gaussian(*bparams)
#pylab.contour(data-fit(*numpy.indices(data.shape)), cmap=pylab.cm.copper)
pylab.contour(fit(*numpy.indices(data.shape)))
ax = pylab.gca()
rot = 0
(off, a, b, height, x, y, width_x, width_y, rot) = bparams
pylab.text(0.95, 0.05, """
x : %.1f
y : %.1f
rot : %.1f
width_x : %.1f
width_y : %.1f""" %(x, y, (rot/r % 360.0), width_x, width_y),
fontsize=16, horizontalalignment='right',
verticalalignment='bottom', transform=ax.transAxes)
pylab.show()
if __name__ == '__main__':
main()