1
+ import numpy as np
2
+ from scipy import optimize , io as spio , stats , misc
3
+ import matplotlib .pyplot as plt
4
+ from datetime import datetime
5
+
6
+ def main ():
7
+ filepath = 'E:/Software/StrahlprofiL2.1/TestProfil'
8
+ data_dict = spio .loadmat (filepath )
9
+ data = data_dict ['Profil' ]
10
+ # gaussFig = misc.imread('gauss01.png')
11
+ # print(gaussFig.shape)
12
+ # data = gaussFig.sum(axis=2)
13
+ t0 = datetime .now ()
14
+
15
+ init_p = (height , amplitude , x , y , width_x , width_y , rota ) = (1 ,1 ,200 ,350 ,20 ,20 ,0 )
16
+ params = gaussfit (data ,return_all = 1 )
17
+ fit = twodgaussian (params [0 ], 0 ,1 ,1 )
18
+ plt .imshow (data , cmap = plt .cm .gist_earth_r )
19
+ plt .contour (fit (* np .indices (data .shape )), cmap = plt .cm .copper )
20
+
21
+ plt .show ()
22
+
23
+ print (params )
24
+ print ('it took {} seconds' .format (datetime .now ()- t0 ))
25
+
26
+ def moments_ (data ,circle ,rotate ,vheight ):
27
+ """Returns (height, x, y, width_x, width_y)
28
+ the gaussian parameters of a 2D distribution by calculating its
29
+ moments """
30
+ total = data .sum ()
31
+ X , Y = np .indices (data .shape )
32
+ x = (X * data ).sum () / total
33
+ y = (Y * data ).sum () / total
34
+ col = data [:, int (y )]
35
+ width_x = np .sqrt (np .abs ((np .arange (col .size ) - y ) ** 2 * col ).sum () / col .sum ())
36
+ row = data [int (x ), :]
37
+ width_y = np .sqrt (np .abs ((np .arange (row .size ) - x ) ** 2 * row ).sum () / row .sum ())
38
+ height = data .max ()
39
+ return (height , x , y , width_x , width_y )
40
+
41
+
42
+ def moments (data ,circle ,rotate ,vheight ):
43
+ """Returns (height, amplitude, x, y, width_x, width_y, rotation angle)
44
+ the gaussian parameters of a 2D distribution by calculating its
45
+ moments. Depending on the input parameters, will only output
46
+ a subset of the above"""
47
+ total = data .sum ()
48
+ X , Y = np .indices (data .shape )
49
+ x = (X * data ).sum ()/ total
50
+ y = (Y * data ).sum ()/ total
51
+ col = data [:, int (y )]
52
+ width_x = np .sqrt (abs ((np .arange (col .size )- y )** 2 * col ).sum ()/ col .sum ())
53
+ row = data [int (x ), :]
54
+ width_y = np .sqrt (abs ((np .arange (row .size )- x )** 2 * row ).sum ()/ row .sum ())
55
+ width = ( width_x + width_y ) / 2.
56
+ # height = stats.mode(data.ravel())[0][0]
57
+ # height = data.max()
58
+
59
+ amplitude = data .max ()#-height
60
+ mylist = [amplitude ,x ,y ]
61
+ if vheight == 1 :
62
+ mylist = [height ] + mylist
63
+ if circle == 0 :
64
+ mylist = mylist + [width_x ,width_y ]
65
+ else :
66
+ mylist = mylist + [width ]
67
+ if rotate == 1 :
68
+ mylist = mylist + [0. ] #rotation "moment" is just zero...
69
+ return tuple (mylist )
70
+
71
+ def twodgaussian (inpars , circle , rotate , vheight ):
72
+ """Returns a 2d gaussian function of the form:
73
+ x' = cos(rota) * x - sin(rota) * y
74
+ y' = sin(rota) * x + cos(rota) * y
75
+ (rota should be in degrees)
76
+ g = b + a exp ( - ( ((x-center_x)/width_x)**2 +
77
+ ((y-center_y)/width_y)**2 ) / 2 )
78
+
79
+ However, the above values are passed by list. The list should be:
80
+ inpars = (height,amplitude,center_x,center_y,width_x,width_y,rota)
81
+
82
+ You can choose to ignore / neglect some of the above input parameters using the following options:
83
+ circle=0 - default is an elliptical gaussian (different x, y widths), but can reduce
84
+ the input by one parameter if it's a circular gaussian
85
+ rotate=1 - default allows rotation of the gaussian ellipse. Can remove last parameter
86
+ by setting rotate=0
87
+ vheight=1 - default allows a variable height-above-zero, i.e. an additive constant
88
+ for the Gaussian function. Can remove first parameter by setting this to 0
89
+ """
90
+ inpars_old = inpars
91
+ inpars = list (inpars )
92
+ if vheight == 1 :
93
+ height = inpars .pop (0 )
94
+ height = float (height )
95
+ else :
96
+ height = float (0 )
97
+ amplitude , center_x , center_y = inpars .pop (0 ),inpars .pop (0 ),inpars .pop (0 )
98
+ amplitude = float (amplitude )
99
+ center_x = float (center_x )
100
+ center_y = float (center_y )
101
+ if circle == 1 :
102
+ width = inpars .pop (0 )
103
+ width_x = float (width )
104
+ width_y = float (width )
105
+ else :
106
+ width_x , width_y = inpars .pop (0 ),inpars .pop (0 )
107
+ width_x = float (width_x )
108
+ width_y = float (width_y )
109
+ if rotate == 1 :
110
+ rota = inpars .pop (0 )
111
+ rota = np .pi / 180. * float (rota )
112
+ rcen_x = center_x * np .cos (rota ) - center_y * np .sin (rota )
113
+ rcen_y = center_x * np .sin (rota ) + center_y * np .cos (rota )
114
+ else :
115
+ rcen_x = center_x
116
+ rcen_y = center_y
117
+ if len (inpars ) > 0 :
118
+ raise ValueError ("There are still input parameters:" + str (inpars ) + \
119
+ " and you've input: " + str (inpars_old ) + " circle=%d, rotate=%d, vheight=%d" % (circle ,rotate ,vheight ) )
120
+
121
+ def rotgauss (x ,y ):
122
+ if rotate == 1 :
123
+ xp = x * np .cos (rota ) - y * np .sin (rota )
124
+ yp = x * np .sin (rota ) + y * np .cos (rota )
125
+ else :
126
+ xp = x
127
+ yp = y
128
+ g = height + amplitude * np .exp (
129
+ - (((rcen_x - xp )/ width_x )** 2 +
130
+ ((rcen_y - yp )/ width_y )** 2 )/ 2. )
131
+ return g
132
+ return rotgauss
133
+
134
+ def gaussfit (data ,err = None ,params = [],autoderiv = 1 ,return_all = 0 ,circle = 0 ,rotate = 1 ,vheight = 0 ):
135
+ """
136
+ Gaussian fitter with the ability to fit a variety of different forms of 2-dimensional gaussian.
137
+
138
+ Input Parameters:
139
+ data - 2-dimensional data array
140
+ err=None - error array with same size as data array
141
+ params=[] - initial input parameters for Gaussian function.
142
+ (height, amplitude, x, y, width_x, width_y, rota)
143
+ autoderiv=1 - use the autoderiv provided in the lmder.f function (the alternative
144
+ is to us an analytic derivative with lmdif.f: this method is less robust)
145
+ return_all=0 - Default is to return only the Gaussian parameters. See below for
146
+ detail on output
147
+ circle=0 - default is an elliptical gaussian (different x, y widths), but can reduce
148
+ the input by one parameter if it's a circular gaussian
149
+ rotate=1 - default allows rotation of the gaussian ellipse. Can remove last parameter
150
+ by setting rotate=0
151
+ vheight=1 - default allows a variable height-above-zero, i.e. an additive constant
152
+ for the Gaussian function. Can remove first parameter by setting this to 0
153
+
154
+ Output:
155
+ Default output is a set of Gaussian parameters with the same shape as the input parameters
156
+ Can also output the covariance matrix, 'infodict' that contains a lot more detail about
157
+ the fit (see scipy.optimize.leastsq), and a message from leastsq telling what the exit
158
+ status of the fitting routine was
159
+ """
160
+ if params == []:
161
+ params = (moments (data ,circle ,rotate ,vheight ))
162
+ if err == None :
163
+ errorfunction = lambda p : np .ravel ((twodgaussian (p ,circle ,rotate ,vheight )(* np .indices (data .shape )) - data ))
164
+ else :
165
+ errorfunction = lambda p : np .ravel ((twodgaussian (p ,circle ,rotate ,vheight )(* np .indices (data .shape )) - data )/ err )
166
+ if autoderiv == 0 :
167
+ raise ValueError ("I'm sorry, I haven't implemented this feature yet." )
168
+ else :
169
+ p , cov , infodict , errmsg , success = optimize .leastsq (errorfunction , params , full_output = 1 )
170
+ if return_all == 0 :
171
+ return p
172
+ elif return_all == 1 :
173
+ return p ,cov ,infodict ,errmsg
174
+
175
+
176
+ if __name__ == "__main__" :
177
+
178
+ main ()
0 commit comments