4from scipy.optimize
import minimize
5from astropy.io
import fits
14 conv_rad_to_mas = 206264806
15 """ This class holds the variable necessary to establish a fit to a camtip image """
16 def __init__(self, w_in=2, w_out=20, mod_r=3.0):
33 return R * rads_to_mas / self.
px_scale
36 ''' data - frame from the camera
37 Subtracts first 100 pixels from the data...'''
55 lab_file_path = lab_dir + file
56 lab_avg = fits.open(lab_file_path)[0].data
67 '''Take a lab fit to compare for with SR'''
68 self.lab_fit = lab_fit # ideally would have radius
69 self.lab_sig = lab_fit[0]
70 self.lab_rad = lab_fit[1]
71 self.lab_amp = lab_fit[2]
74 def gen_fit_img(self):
75 #create an image of the optimal fit, but centered
77 opt_fit = [ft[0], ft[1], ft[2], 0, 0]
78 # this optimum lab image can be used
79 self.lab_fit_img = make_rad_gaus_model(opt_fit, self.grid)
83 x0, y0 = self.lab_fit[3], self.lab_fit[4]
85 self.lab_EE = calc_EE(data, x0, y0, self.grid, self.lab_rad, w_in=self.w_in, w_out = self.w_out)
90 data_fit = fit_img_gauss(self.data_bg_sub, self.lab_rad, self.grid)
91 self.data_fit = data_fit
96 Calculate the SR, requires lab fits and sky fits
98 lab_sigma = self.lab_sig
99 sky_sigma = self.data_fit[0]
100 self.SR = lab_sigma / sky_sigma
102 # check quality of this fit, if invalid, not going to list.
103 # if self.SR > 1 or self.SR < 0:
108 def calc_SR_dumb(self, m=64):
109 img = self.data_bg_sub
110 avg_max = np.average([np.max(img[m,:m]), np.max(img[m,m:]), np.max(img[:m,m]), np.max(img[m:,m])])
111 normed_peak = avg_max / np.sum(img)
112 self.SR_dumb = normed_peak
115 def calc_SR_EE(self):
116 img = self.data_bg_sub
117 # use the lab image to determine shift
118 y0, x0 = calc_idx_shift(img, self.lab_fit_img, self.size)
121 # send shift into the calc SR EE image
122 self.sky_EE = calc_EE(img, x0, y0, self.grid, self.lab_rad, w_in=self.w_in, w_out=self.w_out)
123 # divide by the saved lab EE value
124 self.SR_EE = self.sky_EE / self.lab_EE
128####### Helper funcitons ########
130def calc_idx_shift(data, kernel, width):
131 data_fft = np.fft.fft2(data)
132 kernel_fft = np.fft.fft2(kernel.shaped)
133 data_corr = np.fft.ifft2(data_fft*np.conj(kernel_fft))
134 idx_shift_raw = np.array(np.unravel_index(np.argmax(data_corr), data_corr.shape))
135 idx_shift = ((idx_shift_raw + width//2) % width) - width//2
138def calc_idx_shift_stack(data_stack, kernel, width):
140 kernel_fft = np.fft.fft2(kernel)
142 data_fft_stack = np.fft.fftn(data_stack, axes=(1,2))
143 data_corr_fft = data_fft_stack * kernel_fft[np.newaxis, :, :]
144 data_corr = np.fft.ifftn(data_corr_fft, axes=(1,2))
145 # unwravel the returned indexes
146 idx_shift_raw = np.array([np.unravel_index(np.argmax(data_corr[i]), data_corr[i].shape) for i in range(data_corr.shape[0])])
147 # accounts for negative shifts
148 idx_shift = ((idx_shift_raw + width//2) % width) - width//2
149 return np.array(idx_shift)
151def calc_EE(data, x0, y0, grid, rad, w_in=2, w_out=18):
152 R_mask_lab = make_R_filter(x0, y0, grid, rad, w=w_in)
153 R_mask_norm = make_R_filter(x0, y0, grid, rad, w=w_out)
154 R_EE = np.sum(data*R_mask_lab.shaped) / np.sum(data*R_mask_norm.shaped)
157def make_R_filter(x0, y0, grid, rad, w=2):
158 R = grid.shifted([-x0, -y0]).as_("polar").r
159 R_mask = hp.Field(np.where((R>rad-w) & (R<rad+w), 1, 0), grid)
162# specifically for smaller images
164 bg_mask = np.zeros_like(img)
167 # average ans subtract out background
168 bg_column = np.sum(img * bg_mask, axis=1, keepdims=True) / 10
169 # subtract of the bg:
170 return img - bg_column
172# "LAB" fits are full, radius also uknown
174def make_fit_func(img, grid):
176 Returns a fitting function
177 Based on the Gaussian rotation.
179 def fitting_func(theta):
181 rad_gaus = make_rad_gaus_model(theta, grid)
182 # returning the cost function of the difference of our model vs the guess
183 return np.sum((img.ravel() - rad_gaus)**2)
186def make_rad_gaus_model(theta, grid):
187 # theta is a 1D array of parameters
189 mod_rad = theta[1] #32.5
193 # Create the shifted grid
194 R = grid.shifted([-x0, -y0]).as_("polar").r
196 rad_gaus = hp.Field(amp*np.exp(-(R.ravel()-mod_rad)**2/sig**2), grid)
199def lab_fit_params(data, grid):
201 Lab fits radius in addition to sig, amp, x0, y0
204 theta0 = [5, 32, 1, 0, 0]
205 # seeding the funciton with lab image
206 rad_gaus_fn = make_fit_func(data, grid)
208 theta_opt = scipy.optimize.minimize(rad_gaus_fn, theta0)
209 #creating an image from an optimized fit
212# "SKY" fits are partial, radius is known
214def make_fit_func_sky(img, mod_rad, grid):
215 """ Sky fits use precalculated mod_radius"""
216 def fitting_func(phi):
218 rad_gaus = make_rad_gaus_model_sky(phi, mod_rad, grid)
219 # returning the cost function of the difference of our model vs the guess
220 # COST FUNCTION IS L2 NORM
221 return np.sum((img.ravel() - rad_gaus)**2)
224def make_rad_gaus_model_sky(theta, mod_rad, grid):
225 # theta is a 1D array of parameters
230 # Create the shifted grid
231 R = grid.shifted([-x0, -y0]).as_("polar").r
233 rad_gaus = hp.Field(amp*np.exp(-(R.ravel()-mod_rad)**2/sig**2), grid)
236def fit_img_gauss(img, lab_R, grid):
238 img_sub = sub_bg_img(img)
240 max_pix = np.quantile(img_sub, 0.98)
242 phi0 = [5, max_pix, 0, 0]
243 # create the sky fitting function
244 rad_gaus_fn = make_fit_func_sky(img_sub, lab_R, grid)
245 # optimize parametets
246 theta_opt = scipy.optimize.minimize(rad_gaus_fn, phi0)
247 # just taking parameters, could also take jacobian later
248 return theta_opt['x']
setup_lab(self, lab_dir, file)
set_lab_data(self, lab_dir, file)
__init__(self, w_in=2, w_out=20, mod_r=3.0)
lab_fit_params(data, grid)