Ajustar una función Gaussian 2D usando scipy.optimize.curve_fit – ValueError y minpack.error

Tengo la intención de adaptar una función Gaussiana 2D a las imágenes que muestran un rayo láser para obtener sus parámetros como FWHM y posición. Hasta ahora traté de entender cómo definir una función Gaussiana 2D en Python y cómo pasarle las variables x e y.

He escrito un pequeño script que define esa función, la grafica, le agrega algo de ruido y luego trata de ajustarlo usando curve_fit . Todo parece funcionar, excepto el último paso en el que bash ajustar la función de mi modelo a los datos ruidosos. Aquí está mi código:

 import scipy.optimize as opt import numpy as np import pylab as plt #define model function and pass independant variables x and y as a list def twoD_Gaussian((x,y), amplitude, xo, yo, sigma_x, sigma_y, theta, offset): xo = float(xo) yo = float(yo) a = (np.cos(theta)**2)/(2*sigma_x**2) + (np.sin(theta)**2)/(2*sigma_y**2) b = -(np.sin(2*theta))/(4*sigma_x**2) + (np.sin(2*theta))/(4*sigma_y**2) c = (np.sin(theta)**2)/(2*sigma_x**2) + (np.cos(theta)**2)/(2*sigma_y**2) return offset + amplitude*np.exp( - (a*((x-xo)**2) + 2*b*(x-xo)*(y-yo) + c*((y-yo)**2))) # Create x and y indices x = np.linspace(0, 200, 201) y = np.linspace(0, 200, 201) x,y = np.meshgrid(x, y) #create data data = twoD_Gaussian((x, y), 3, 100, 100, 20, 40, 0, 10) # plot twoD_Gaussian data generated above plt.figure() plt.imshow(data) plt.colorbar() # add some noise to the data and try to fit the data generated beforehand initial_guess = (3,100,100,20,40,0,10) data_noisy = data + 0.2*np.random.normal(size=len(x)) popt, pcov = opt.curve_fit(twoD_Gaussian, (x,y), data_noisy, p0 = initial_guess) 

Aquí está el mensaje de error que recibo cuando winpython 64-bit el script con winpython 64-bit Python 2.7 winpython 64-bit :

 ValueError: object too deep for desired array Traceback (most recent call last): File "", line 1, in  File "C:\Python\WinPython-64bit-2.7.6.2\python-2.7.6.amd64\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 540, in runfile execfile(filename, namespace) File "E:/Work Computer/Software/Python/Fitting scripts/2D Gaussian function fit/2D_Gaussian_LevMarq_v2.py", line 39, in  popt, pcov = opt.curve_fit(twoD_Gaussian, (x,y), data_noisy, p0 = initial_guess) File "C:\Python\WinPython-64bit-2.7.6.2\python-2.7.6.amd64\lib\site-packages\scipy\optimize\minpack.py", line 533, in curve_fit res = leastsq(func, p0, args=args, full_output=1, **kw) File "C:\Python\WinPython-64bit-2.7.6.2\python-2.7.6.amd64\lib\site-packages\scipy\optimize\minpack.py", line 378, in leastsq gtol, maxfev, epsfcn, factor, diag) minpack.error: Result from function call is not a proper array of floats. 

¿Qué es lo que estoy haciendo mal? ¿Es así como paso las variables independientes a la function/curve_fit modelo function/curve_fit ?

La salida de twoD_Gaussian debe ser 1D. Lo que puedes hacer es agregar un .ravel() al final de la última línea, como esto:

 def twoD_Gaussian((x, y), amplitude, xo, yo, sigma_x, sigma_y, theta, offset): xo = float(xo) yo = float(yo) a = (np.cos(theta)**2)/(2*sigma_x**2) + (np.sin(theta)**2)/(2*sigma_y**2) b = -(np.sin(2*theta))/(4*sigma_x**2) + (np.sin(2*theta))/(4*sigma_y**2) c = (np.sin(theta)**2)/(2*sigma_x**2) + (np.cos(theta)**2)/(2*sigma_y**2) g = offset + amplitude*np.exp( - (a*((x-xo)**2) + 2*b*(x-xo)*(y-yo) + c*((y-yo)**2))) return g.ravel() 

Obviamente, necesitarás remodelar la salida para trazar, por ejemplo:

 # Create x and y indices x = np.linspace(0, 200, 201) y = np.linspace(0, 200, 201) x, y = np.meshgrid(x, y) #create data data = twoD_Gaussian((x, y), 3, 100, 100, 20, 40, 0, 10) # plot twoD_Gaussian data generated above plt.figure() plt.imshow(data.reshape(201, 201)) plt.colorbar() 

Haz el ajuste como antes:

 # add some noise to the data and try to fit the data generated beforehand initial_guess = (3,100,100,20,40,0,10) data_noisy = data + 0.2*np.random.normal(size=data.shape) popt, pcov = opt.curve_fit(twoD_Gaussian, (x, y), data_noisy, p0=initial_guess) 

Y trazar los resultados:

 data_fitted = twoD_Gaussian((x, y), *popt) fig, ax = plt.subplots(1, 1) ax.hold(True) ax.imshow(data_noisy.reshape(201, 201), cmap=plt.cm.jet, origin='bottom', extent=(x.min(), x.max(), y.min(), y.max())) ax.contour(x, y, data_fitted.reshape(201, 201), 8, colors='w') plt.show() 

introduzca la descripción de la imagen aquí

Para ampliar un poco la respuesta de Dietrich, recibí el siguiente error al ejecutar la solución sugerida con Python 3.4 (en Ubuntu 14.04):

 def twoD_Gaussian((x, y), amplitude, xo, yo, sigma_x, sigma_y, theta, offset): ^ SyntaxError: invalid syntax 

Ejecutar 2to3 sugirió la siguiente solución simple:

 def twoD_Gaussian(xdata_tuple, amplitude, xo, yo, sigma_x, sigma_y, theta, offset): (x, y) = xdata_tuple xo = float(xo) yo = float(yo) a = (np.cos(theta)**2)/(2*sigma_x**2) + (np.sin(theta)**2)/(2*sigma_y**2) b = -(np.sin(2*theta))/(4*sigma_x**2) + (np.sin(2*theta))/(4*sigma_y**2) c = (np.sin(theta)**2)/(2*sigma_x**2) + (np.cos(theta)**2)/(2*sigma_y**2) g = offset + amplitude*np.exp( - (a*((x-xo)**2) + 2*b*(x-xo)*(y-yo) + c*((y-yo)**2))) return g.ravel() 

El motivo de esto es que el desempaquetado automático de tuplas cuando se pasa a una función como parámetro se ha eliminado desde Python 3. Para obtener más información, consulte aquí: PEP 3113

curve_fit() quiere que la dimensión de xdata sea (2,n*m) y no (2,n,m) . ydata debe tener forma (n*m) no (n,m) respectivamente. Así que usas ravel() para aplanar tus arreglos 2D:

 xdata = np.vstack((xx.ravel(),yy.ravel())) ydata = data_noisy.ravel() popt, pcov = opt.curve_fit(twoD_Gaussian, xdata, ydata, p0=initial_guess) 

Por cierto: no estoy seguro de que la parametrización con los términos trigonométricos sea la mejor. Por ejemplo, tomar el descrito aquí podría ser un poco más sólido en cuanto a aspectos numéricos y grandes desviaciones.