def run_ba(width, height, pp_x, pp_y, fl, dist_coefs, poses, pts3d, pts2d, cam_idxs, pt3d_idxs, meas_r, meas_aa, meas_idxs, optimize_dist=False, n_cam_intr=0): dist_coefs5 = [0.0] * 5 for i in range(len(dist_coefs)): dist_coefs5[i] = dist_coefs[i] if optimize_dist: dist_coefs = dist_coefs5[:2] else: dist_coefs = None cam = Camera(width, height, cam_mx=np.array([[fl, 0, pp_x], [0, fl, pp_y], [0, 0, 1]]), dist_coefs=np.array(dist_coefs5, dtype=np.float32)) norm_pts2d = pts2d.squeeze() if optimize_dist else cam.undistort( pts2d).squeeze() new_poses, new_pts3d, dist_ba, cam_intr_ba, _, _, ba_errs = vis_gps_bundle_adj( poses, pts3d, norm_pts2d, np.zeros((0, 1)), cam_idxs, pt3d_idxs, cam.intrinsic_camera_mx(), dist_coefs, np.array([PX_ERR_SD]), meas_r, meas_aa, np.zeros((0, 1)), meas_idxs, np.array(LOC_ERR_SD), np.array(ORI_ERR_SD), px_err_weight=np.array([1]), n_cam_intr=n_cam_intr, log_writer=None, max_nfev=5, skip_pose_n=1, poses_only=False, huber_coef=(1, 5, 0.5)) new_poses = np.concatenate((poses[:1, :], new_poses), axis=0) return new_poses, new_pts3d, dist_ba, cam_intr_ba, ba_errs
def prepare(self, scene): w, h = scene.width, scene.height if self.target is not None: # change orientation so that target is on the camera bore-sight self._update_target() if self.is_dirty() or self.model.width != w or self.model.height != h: x_fov = math.degrees( 2 * math.atan(self.sensor_width / 2 / self.focal_length)) y_fov = x_fov * h / w params = RenderCamera.DEFAULTS.copy() params.update(self.extra) if 'aperture' in params: params.pop('f_stop', False) if 'px_saturation_e' not in self.extra: params['px_saturation_e'] *= ((self.sensor_width / w) / 5.5e-3)**2 if 'dark_noise_mu' not in self.extra: params['dark_noise_mu'] *= params[ 'px_saturation_e'] / self.DEFAULTS['px_saturation_e'] if 'dark_noise_sd' not in self.extra: params['dark_noise_sd'] = np.sqrt(params['dark_noise_mu']) self.model = Camera(w, h, x_fov, y_fov, focal_length=self.focal_length, **params) self.clear_dirty()
def get_cam(): common_kwargs_worst = { 'sensor_size': (2048 * 0.0022, 1944 * 0.0022), 'quantum_eff': 0.30, 'px_saturation_e': 2200, # snr_max = 20*log10(sqrt(sat_e)) dB 'lambda_min': 350e-9, 'lambda_eff': 580e-9, 'lambda_max': 800e-9, 'dark_noise_mu': 40, 'dark_noise_sd': 6.32, 'readout_noise_sd': 15, # dark_noise_sd should be sqrt(dark_noise_mu) 'emp_coef': 1, # dynamic range = 20*log10(sat_e/readout_noise)) 'exclusion_angle_x': 55, 'exclusion_angle_y': 90, } common_kwargs_best = dict(common_kwargs_worst) common_kwargs_best.update({ 'quantum_eff': 0.4, 'px_saturation_e': 3500, 'dark_noise_mu': 25, 'dark_noise_sd': 5, 'readout_noise_sd': 5, }) common_kwargs = common_kwargs_best return Camera( 2048, # width in pixels 1944, # height in pixels 7.7, # x fov in degrees (could be 6 & 5.695, 5.15 & 4.89, 7.7 & 7.309) 7.309, # y fov in degrees f_stop=5, # TODO: put better value here point_spread_fn=0.50, # ratio of brightness in center pixel scattering_coef=2e-10, # affects strength of haze/veil when sun shines on the lens **common_kwargs )
def __init__(self, hi_res_shape_model=False, rosetta_batch='mtp006', focused_attenuated=True, res_mult=1.0): # gives some unnecessary warning about "dubious year" even when trying to ignore it with warnings.catch_warnings(): warnings.simplefilter("ignore") min_time = Time('2015-01-01 00:00:00', scale='utc', format='iso') fa = focused_attenuated # else defocused not attenuated super(RosettaSystemModel, self).__init__( asteroid=ChuryumovGerasimenko( hi_res_shape_model=hi_res_shape_model, rosetta_batch=rosetta_batch), # see https://pds-smallbodies.astro.umd.edu/holdings/ro-c-navcam-2-esc4-mtp023-v1.0/document/ro-sgs-if-0001.pdf camera=Camera( int(1024 * res_mult), # width in pixels int(1024 * res_mult), # height in pixels 5, # x fov in degrees 5, # y fov in degrees focal_length=152.5, # in mm # sensor_size=(1024*0.013, 1024*0.013), aperture=30 if fa else 70, # attenuated mode (in non-attenuated mode would be 70mm) # f_stop=5.1, # attenuated mode (in non-attenuated mode would be 2.2) quantum_eff= 0.80, # from https://www.e2v.com/resources/account/download-datasheet/1427 px_saturation_e=1e5, # same source as above # gain can be 1.0 (low) or 1.7 (high) # from https://pds-smallbodies.astro.umd.edu/holdings/ro-c-navcam-2-esc4-mtp023-v1.0/document/ro-sgs-if-0001.pdf lambda_min=500e-9, lambda_eff=650e-9, lambda_max=800e-9, # for bandwidth calc dark_noise_mu=250, dark_noise_sd=60, readout_noise_sd=2, # noise params (e-/s, e-) point_spread_fn=0.65 if fa else 0.25, # 0.50-0.55 for defocused, 0.65-0.70 for focused scattering_coef= 5e-9, # affects strength of haze/veil when sun shines on the lens (TODO: just a guess now) exclusion_angle_x=15, exclusion_angle_y=15, # 30mm aperture and attenuation filter result in an attenuation factor of ∼580 relative to 70mm aperture and no filter # => if aperture is 30, need extra attenuation coef: x*(30/70)**2==1/580 => x==0.009387 # empirical coef used to tune the synthetic images to match real brightnesses (includes attenuation filter effect) # - based on star brightnesses, note that point_spread_fn affects this a lot emp_coef=(0.009387 * 2.3 if fa else 2.3), ), limits=( 25, # min_distance in km 70, # min_med_distance in km 400, # max_med_distance in km #640 1250, # max_distance in km 40, # min_elong in deg min_time, # min time instant )) self.mission_id = 'rose'
def cost_fn(x, Y, X, K, iK): dx, dy, th, *dist_coefs = x A = np.array([ [math.cos(th), -math.sin(th), dx], [math.sin(th), math.cos(th), dy], ]) Xdot = Camera.distort(X.dot(A.T), dist_coefs, K, iK) return tools.pseudo_huber_loss(np.linalg.norm(Y - Xdot, axis=1), delta=3)
def sensed_electron_flux_star_spectrum(path, bayer, mag_v, Teff, log_g, fe_h, lam_min, lam_max, qeff_coefs, gomos_mag_v=None): spectrum_fn = get_star_spectrum(path, bayer, mag_v, Teff, log_g, fe_h, lam_min, lam_max, gomos_mag_v) electrons, _ = Camera.electron_flux_in_sensed_spectrum_fn( qeff_coefs, spectrum_fn, lam_min, lam_max) return electrons
def init_cam(self): w, h = 1280, 960 common_kwargs_worst = { 'sensor_size': (w * 0.00375, h * 0.00375), 'quantum_eff': 0.30, 'px_saturation_e': 2200, # snr_max = 20*log10(sqrt(sat_e)) dB 'lambda_min': 350e-9, 'lambda_eff': 580e-9, 'lambda_max': 800e-9, 'dark_noise_mu': 40, 'dark_noise_sd': 6.32, 'readout_noise_sd': 15, # dark_noise_sd should be sqrt(dark_noise_mu) 'emp_coef': 1, # dynamic range = 20*log10(sat_e/readout_noise)) 'exclusion_angle_x': 55, 'exclusion_angle_y': 90, } common_kwargs_best = dict(common_kwargs_worst) common_kwargs_best.update({ 'quantum_eff': 0.4, 'px_saturation_e': 3500, 'dark_noise_mu': 25, 'dark_noise_sd': 5, 'readout_noise_sd': 5, }) common_kwargs = common_kwargs_best cam = Camera( w, # width in pixels h, # height in pixels 43.6, # x fov in degrees (could be 6 & 5.695, 5.15 & 4.89, 7.7 & 7.309) 33.4, # y fov in degrees f_stop=5, # TODO: put better value here point_spread_fn=0.50, # ratio of brightness in center pixel scattering_coef= 2e-10, # affects strength of haze/veil when sun shines on the lens dist_coefs=[ -3.79489919e-01, 2.55784821e-01, 9.52433459e-04, 1.27543923e-04, -2.74301340e-01 ], # by using calibrate.py cam_mx=np.array([[1.60665503e+03, 0.00000000e+00, 6.12522544e+02], [0.00000000e+00, 1.60572265e+03, 4.57510418e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]), **common_kwargs) return cam
def init_cam(self): w, h, p = 1920, 1080, 0.00375 common_kwargs_worst = { 'sensor_size': (w * p, h * p), 'quantum_eff': 0.30, 'px_saturation_e': 2200, # snr_max = 20*log10(sqrt(sat_e)) dB 'lambda_min': 350e-9, 'lambda_eff': 580e-9, 'lambda_max': 800e-9, 'dark_noise_mu': 40, 'dark_noise_sd': 6.32, 'readout_noise_sd': 15, # dark_noise_sd should be sqrt(dark_noise_mu) 'emp_coef': 1, # dynamic range = 20*log10(sat_e/readout_noise)) 'exclusion_angle_x': 55, 'exclusion_angle_y': 90, } common_kwargs_best = dict(common_kwargs_worst) common_kwargs_best.update({ 'quantum_eff': 0.4, 'px_saturation_e': 3500, 'dark_noise_mu': 25, 'dark_noise_sd': 5, 'readout_noise_sd': 5, }) common_kwargs = common_kwargs_best cam = Camera( w, # width in pixels h, # height in pixels 62.554, # x fov in degrees (could be 6 & 5.695, 5.15 & 4.89, 7.7 & 7.309) 37.726, # y fov in degrees f_stop=5, # TODO: put better value here point_spread_fn=0.50, # ratio of brightness in center pixel scattering_coef= 2e-10, # affects strength of haze/veil when sun shines on the lens dist_coefs=None if 0 else [-0.11250615, 0.14296794, -0.00175085, 0.00057391, -0.11678778], cam_mx=np.array([[1.58174667e+03, 0.00000000e+00, 9.97176182e+02], [0.00000000e+00, 1.58154569e+03, 5.15553843e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]), **common_kwargs) return cam
def __init__(self, hi_res_shape_model=False, res_mult=1.0): # gives some unnecessary warning about "dubious year" even when trying to ignore it with warnings.catch_warnings(): warnings.simplefilter("ignore") min_time = Time('2019-03-01 00:00:00', scale='utc', format='iso') super(BennuSystemModel, self).__init__( asteroid=Bennu(hi_res_shape_model=hi_res_shape_model), # see https://sbnarchive.psi.edu/pds4/orex/orex.tagcams/document/tagcams_inst_desc.pdf # sensor datasheet: https://www.onsemi.com/pdf/datasheet/mt9p031-d.pdf # lens reference: http://www.msss.com/brochures/xfov.pdf camera=Camera( int(2592 * res_mult), # width in pixels int(1944 * res_mult), # height in pixels 44, # x fov in degrees 32, # y fov in degrees focal_length=7.7, # in mm f_stop=3.5, quantum_eff=0.65, px_saturation_e= 6456, # SNR_MAX = 20*log10(sqrt(sat_e)), if SNR_MAX=38.1 dB, then sat_e=6456 e- lambda_min=400e-9, lambda_eff=580e-9, lambda_max=700e-9, # for bandwidth calc dark_noise_mu=250, dark_noise_sd=60, readout_noise_sd=6.7, # noise params (e-/s, e-) point_spread_fn=0.4, scattering_coef= 5e-9, # affects strength of haze/veil when sun shines on the lens (TODO: just a guess now) exclusion_angle_x=60, exclusion_angle_y=50, emp_coef=1.0, ), limits=( 0.7, # min_distance in km 1.1, # min_med_distance in km 3.5, # max_med_distance in km #640 30, # max_distance in km 180 - 130, # min_elong in deg (180 - phase angle) min_time, # min time instant )) self.mission_id = 'orex'
def calc_source_kernel(cams, spectrum_fn, patch_size, points=None): # detect source kernel = ImageProc.bsphkern( tuple(map(int, patch_size)) if '__iter__' in dir(patch_size) else int(patch_size)) expected_bgr = np.zeros(3) for i, cam in enumerate(cams): ef, _ = Camera.electron_flux_in_sensed_spectrum_fn(cam.qeff_coefs, spectrum_fn, cam.lambda_min, cam.lambda_max, fast=False, points=points) expected_bgr[i] = cam.gain * cam.aperture_area * cam.emp_coef * ef kernel = np.repeat(np.expand_dims(kernel, axis=2), 3, axis=2) * expected_bgr return kernel
import cv2 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import from visnav.algo import tools from visnav.algo.model import Camera, SystemModel from visnav.algo.odometry import VisualOdometry, Pose from visnav.missions.didymos import DidymosSystemModel from visnav.render.render import RenderEngine from visnav.settings import * if __name__ == '__main__': sm = DidymosSystemModel(use_narrow_cam=False, target_primary=False, hi_res_shape_model=False) sm.cam = Camera(1024, 1024, 20, 20) re = RenderEngine(sm.cam.width, sm.cam.height, antialias_samples=0) re.set_frustum(sm.cam.x_fov, sm.cam.y_fov, 0.002, 2) ast_v = np.array([0, 0, -0.3]) #q = tools.angleaxis_to_q((math.radians(2), 0, 1, 0)) #q = tools.angleaxis_to_q((math.radians(1), 1, 0, 0)) #q = tools.rand_q(math.radians(2)) #lowq_obj = sm.asteroid.load_noisy_shape_model(Asteroid.SM_NOISE_HIGH) obj = sm.asteroid.real_shape_model obj_idx = re.load_object(obj) # obj_idx = re.load_object(sm.asteroid.hires_target_model_file) t0 = datetime.now().timestamp() odo = VisualOdometry( sm,
def render_itokawa(show=False): cam = Camera(4096, 4096, aperture=1.5, focal_length=120.8, sensor_size=[12.288] * 2, px_saturation_e=30e3) shape_model_file = [ r'C:\projects\sispo\data\models\itokawa_16k.obj', r'C:\projects\sispo\data\models\itokawa_f3145728.obj', ][0] RenderEngine.REFLMOD_PARAMS[RenderEngine.REFLMOD_HAPKE] = [ 553.38, # J 0 26, # th_p 26 0.31, # w 0.31 -0.35, # b -0.35 0, # c 0 0.86, # B_SH0 0.86 0.00021, # hs 0.00021 0, # B_CB0 0 0.005, # hc 0.005 1, # K 1 ] re = RenderEngine(cam.width, cam.height, antialias_samples=0) #, enable_extra_data=True) re.set_frustum(cam.x_fov, cam.y_fov, 0.05, 12) obj_idx = re.load_object(shape_model_file) if 1: g_sc_q = tools.angleaxis_to_q( [1.847799, -0.929873, 0.266931, -0.253146]) #x, y, z = -13.182141, -64.694813, 116.263134 x, y, z = 93.818, -8.695, 1.263 g_ast_q = get_ast_q(x, y, z) g_sol_ast_v = np.array([146226194732.0, -68812326932.0, -477863381.0 ]) * 1e-3 g_sol_sc_v = np.array([146226188132.0, -68812322442.0, -477863711.0 ]) * 1e-3 g_sc_ast_v = g_sol_ast_v - g_sol_sc_v l_ast_sc_v = tools.q_times_v( SystemModel.sc2gl_q.conj() * g_sc_q.conj(), g_sc_ast_v) l_ast_q = SystemModel.sc2gl_q.conj() * g_sc_q.conj( ) * g_ast_q * SystemModel.sc2gl_q l_light_v = tools.q_times_v(SystemModel.sc2gl_q.conj() * g_sc_q.conj(), g_sol_ast_v / np.linalg.norm(g_sol_ast_v)) else: l_ast_sc_v = np.array([0, 0, -7.990 * 1]) l_ast_q = tools.angleaxis_to_q((math.radians(20), 0, 1, 0)) l_light_v = np.array([1, 0, -1]) / math.sqrt(2) sc_mode = 0 a, b, c = [0] * 3 ds, dq = 0.135, tools.ypr_to_q(math.radians(1.154), 0, math.radians(-5.643)) # ds, dq = 0.535, quaternion.one # itokawa centered while True: img = re.render(obj_idx, tools.q_times_v(dq, l_ast_sc_v * ds), l_ast_q, l_light_v, flux_density=1.0, gamma=1.0, get_depth=False, shadows=True, textures=True, reflection=RenderEngine.REFLMOD_HAPKE) # data = re.render_extra_data(obj_idx, tools.q_times_v(dq, l_ast_sc_v*ds), l_ast_q, l_light_v) # import matplotlib.pyplot as plt # plt.imshow(data[:, :, 0]) k = output(img, show, maxval=0.90) if k is None or k == 27: break tmp = 1 if k in (ord('a'), ord('s'), ord('q')) else -1 if k in (ord('a'), ord('d')): if sc_mode: dq = tools.ypr_to_q(math.radians(tmp * 0.033), 0, 0) * dq else: b += tmp if k in (ord('w'), ord('s')): if sc_mode: dq = tools.ypr_to_q(0, 0, math.radians(tmp * 0.033)) * dq else: a += tmp if k in (ord('q'), ord('e')): if sc_mode: ds *= 0.9**tmp else: c += tmp if k == ord('i'): y, p, r = tools.q_to_ypr(dq) print('+c: %.3f, %.3f, %.3f' % (x + a, y + b, z + c)) print('ds, h, v: %.3f, %.3f, %.3f' % (ds, math.degrees(y), math.degrees(r))) g_ast_q = get_ast_q(x + a, y + b, z + c) l_ast_q = SystemModel.sc2gl_q.conj() * g_sc_q.conj( ) * g_ast_q * SystemModel.sc2gl_q
def get_bgr_cam(thumbnail=False, estimated=False, final=False): if 0: bgr = ( { 'qeff_coefs': [.05] * 2 + [0.4] + [.05] * 10, 'lambda_min': 350e-9, 'lambda_eff': 465e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': [.05] * 4 + [0.4] + [.05] * 8, 'lambda_min': 350e-9, 'lambda_eff': 540e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': [.05] * 6 + [0.4] + [.05] * 6, 'lambda_min': 350e-9, 'lambda_eff': 650e-9, 'lambda_max': 1000e-9 }, ) elif estimated: array = tuple if final: # CURR RESULT tmp = [ array([ 0.04597736, 0.18328294, 0.35886904, 0.20962509, 0.07183794, 0.06324343, 0.06437495, 0.09206215, 0.07814311, 0.07806824, 0.06221573, 0.06078359, 0.01155586, 0.00993577 ]), array([ 0.0365881, 0.07068747, 0.07992755, 0.24703731, 0.32896325, 0.14336659, 0.04652175, 0.08300058, 0.12259696, 0.04717292, 0.06284823, 0.06651458, 0.06912351, 0.06644719 ]), array([ 4.09008330e-02, 6.04753849e-02, 1.09132383e-04, 9.39146573e-03, 4.33269662e-02, 3.12552300e-01, 3.00956896e-01, 2.20188491e-01, 1.88448761e-01, 1.38525314e-01, 9.33445846e-02, 5.93350748e-02, 2.43120789e-02, 3.52109874e-02 ]) ] else: # CURR RESULT tmp = [ array([ 0.04597736, 0.18328294, 0.35886904, 0.20962509, 0.07183794, 0.06324343, 0.06437495, 0.09206215, 0.07814311, 0.07806824, 0.06221573, 0.06078359, 0.01155586, 0.00993577 ]), array([ 0.0365881, 0.07068747, 0.07992755, 0.24703731, 0.32896325, 0.14336659, 0.04652175, 0.08300058, 0.12259696, 0.04717292, 0.06284823, 0.06651458, 0.06912351, 0.06644719 ]), array([ 4.09008330e-02, 6.04753849e-02, 1.09132383e-04, 9.39146573e-03, 4.33269662e-02, 3.12552300e-01, 3.00956896e-01, 2.20188491e-01, 1.88448761e-01, 1.38525314e-01, 9.33445846e-02, 5.93350748e-02, 2.43120789e-02, 3.52109874e-02 ]) ] bgr = ( { 'qeff_coefs': tmp[0], 'lambda_min': 350e-9, 'lambda_eff': 465e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': tmp[1], 'lambda_min': 350e-9, 'lambda_eff': 540e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': tmp[2], 'lambda_min': 350e-9, 'lambda_eff': 650e-9, 'lambda_max': 1000e-9 }, ) elif 0: bgr = ( { 'qeff_coefs': list( reversed([ .05 * .9, .15 * .9, .33 * .95, .22 * .95, .07 * .95, .05 * .95, .05 * .95, .05 * .95, .05 * .95 ])), 'lambda_min': 350e-9, 'lambda_eff': 465e-9, 'lambda_max': 750e-9 }, { 'qeff_coefs': list( reversed([ .05 * .9, .05 * .95, .23 * .95, .35 * .95, .17 * .95, .07 * .95, .11 * .95, .12 * .95, .05 * .95 ])), 'lambda_min': 400e-9, 'lambda_eff': 540e-9, 'lambda_max': 800e-9 }, { 'qeff_coefs': list( reversed([ .05 * .95, .05 * .95, .35 * .95, .35 * .95, .27 * .95, .23 * .95, .18 * .925, .13 * .9, .09 * .9, .05 * .875 ])), 'lambda_min': 500e-9, 'lambda_eff': 650e-9, 'lambda_max': 950e-9 }, ) elif 1: # DEFAULT bgr = ( { 'qeff_coefs': np.array([ .05 * .05, .15 * .9, .33 * .95, .22 * .95, .07 * .95, .05 * .95, .04 * .95, .05 * .95, .05 * .95, .05 * .925, .05 * .9, .05 * .9, .05 * .875, .05 * .85 ]) * INIT_QEFF_ADJ[0], 'lambda_min': 350e-9, 'lambda_eff': 465e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': np.array([ .03 * .05, .03 * .9, .05 * .95, .23 * .95, .35 * .95, .17 * .95, .07 * .95, .11 * .95, .12 * .95, .05 * .925, .05 * .9, .05 * .9, .05 * .875, .05 * .85 ]) * INIT_QEFF_ADJ[1], 'lambda_min': 350e-9, 'lambda_eff': 540e-9, 'lambda_max': 1000e-9 }, { 'qeff_coefs': np.array([ .05 * .05, .05 * .9, .01 * .95, .03 * .95, .05 * .95, .35 * .95, .35 * .95, .27 * .95, .23 * .95, .18 * .925, .13 * .9, .09 * .9, .05 * .875, .05 * .85 ]) * INIT_QEFF_ADJ[2], 'lambda_min': 350e-9, 'lambda_eff': 650e-9, 'lambda_max': 1000e-9 }, ) elif 0: bgr = ( { 'qeff_coefs': np.array([ .05 * .05, .05 * .05, .15 * .9, .33 * .95, .22 * .95, .07 * .95, .05 * .95, .04 * .95, .05 * .95, .05 * .95, .05 * .925, .05 * .9, .05 * .9, .05 * .875 ]) * INIT_QEFF_ADJ[0], 'lambda_min': 300e-9, 'lambda_eff': 465e-9, 'lambda_max': 950e-9 }, { 'qeff_coefs': np.array([ .03 * .05, .03 * .05, .03 * .9, .05 * .95, .23 * .95, .35 * .95, .17 * .95, .07 * .95, .11 * .95, .12 * .95, .05 * .925, .05 * .9, .05 * .9, .05 * .85 ]) * INIT_QEFF_ADJ[1], 'lambda_min': 300e-9, 'lambda_eff': 540e-9, 'lambda_max': 950e-9 }, { 'qeff_coefs': np.array([ .05 * .05, .05 * .05, .05 * .9, .01 * .95, .03 * .95, .05 * .95, .35 * .95, .35 * .95, .27 * .95, .23 * .95, .18 * .925, .13 * .9, .09 * .9, .05 * .85 ]) * INIT_QEFF_ADJ[2], 'lambda_min': 300e-9, 'lambda_eff': 650e-9, 'lambda_max': 950e-9 }, ) else: bgr = ( { 'qeff_coefs': list( reversed([ .05 * .9, .15 * .9, .33 * .95, .22 * .95, .07 * .95, .05 * .95, .04 * .95, .05 * .95, .05 * .95, .05 * .925, .05 * .9, .05 * .9 ])), 'lambda_min': 350e-9, 'lambda_eff': 465e-9, 'lambda_max': 900e-9 }, { 'qeff_coefs': list( reversed([ .05 * .9, .05 * .9, .05 * .95, .23 * .95, .35 * .95, .17 * .95, .07 * .95, .11 * .95, .12 * .95, .05 * .925, .05 * .9, .05 * .9 ])), 'lambda_min': 350e-9, 'lambda_eff': 540e-9, 'lambda_max': 900e-9 }, { 'qeff_coefs': list( reversed([ .01 * .9, .01 * .9, .01 * .95, .03 * .95, .05 * .95, .35 * .95, .35 * .95, .27 * .95, .23 * .95, .18 * .925, .13 * .9, .09 * .9 ])), 'lambda_min': 350e-9, 'lambda_eff': 650e-9, 'lambda_max': 900e-9 }, ) bgr_cam = [] for i in range(3): # snr_max = 20*log10(sqrt(sat_e)) # sat_e = (10**(43/20))**2 => 19952 bgr_cam.append( Camera(2048, 1536, None, None, sensor_size=(2048 * 3.2e-3, 1536 * 3.2e-3), focal_length=8.2, f_stop=1.4, px_saturation_e=20000, emp_coef=1 / 16**2 if thumbnail else 1, dark_noise_mu=500, readout_noise_sd=15, point_spread_fn=0.5, scattering_coef=5e-9, **bgr[i])) if PLOT_INITIAL_QEFF_FN: plot_bgr_qeff(bgr_cam) return bgr_cam
def expected_du(self, pre_sat_gain=1, post_sat_gain=1, qeff_coefs=None, psf_coef=(1, 1, 1), plot=False): f, c = self.frame, self.frame.cam[self.cam_i] g, clat, clon, slon = f.phase_angle, f.cam_moon_lat, f.cam_moon_lon, f.sun_moon_lon smd, cmd = f.sun_moon_dist, f.cam_moon_dist spectrum_fn = MoonMeasure.lunar_disk_irr_fn( MoonMeasure.lunar_disk_refl_fn(g, clat, clon, slon), smd, cmd) if plot and self.cam_i == 0: lam = np.linspace(c.lambda_min, c.lambda_max, 1000) albedo_fn = MoonMeasure.lunar_disk_refl_fn(g, clat, clon, slon) moon_sr = 6.4177e-5 * (384.4e6 / cmd)**2 ssi_fn = lambda lam: spectrum_fn(lam) / albedo_fn( lam) / moon_sr * np.pi one_fig = False plt.rcParams.update({'font.size': 16}) for i in range(1 if one_fig else 2): fig = plt.figure(figsize=[6.4, 4.8]) if one_fig: axs = fig.subplots(1, 2) else: axs = [None] * 2 axs[i] = fig.subplots(1, 1) if i == 0 or one_fig: ax_da = axs[0].twinx() ax_da.plot(lam * 1e9, albedo_fn(lam), color='orange') ax_da.set_ylabel('Disk equivalent albedo', color='orange') ax_da.tick_params(axis='y', labelcolor='orange') # ax_da.title('ROLO-model 2018-12-14 19:00 UTC') axs[0].plot(lam * 1e9, ssi_fn(lam) * 1e-9, color='tab:blue') # [W/m2/nm] axs[0].set_xlabel('Wavelength [nm]') axs[0].set_ylabel( r'Spectral Irradiance [$\mathregular{W/m^{2}/nm}$]', color='tab:blue') axs[0].tick_params(axis='y', labelcolor='tab:blue') # axs[0].title('Sunlight SI on 2018-12-14') if i == 1 or one_fig: ax_qe = axs[1].twinx() ax_qe.set_ylim([None, 45]) ax_qe.set_ylabel('Quantum efficiency [%]') plot_bgr_qeff(self.frame.cam, ax=ax_qe, color=('lightblue', 'lightgreen', 'pink'), linestyle='dashed', linewidth=1, marker="") axs[1].plot(lam * 1e9, spectrum_fn(lam) * 1e-9, color='tab:blue') # [W/m2/nm] axs[1].set_xlabel('Wavelength [nm]') axs[1].set_ylabel( r'Spectral Irradiance [$\mathregular{W/m^{2}/nm}$]', color='tab:blue') axs[1].tick_params(axis='y', labelcolor='tab:blue') # axs[1].title('Moonlight SI on 2018-12-14 19:00 UTC, ROLO-model + SSI') plt.tight_layout() plt.show() cgain = c.gain * c.aperture_area * c.emp_coef fgain = f.gain * f.exposure queff_coefs = tuple( c.qeff_coefs if qeff_coefs is None else qeff_coefs[self.cam_i]) electrons, _ = Camera.electron_flux_in_sensed_spectrum_fn( queff_coefs, spectrum_fn, c.lambda_min, c.lambda_max) du = pre_sat_gain * RAW_IMG_MAX_VALUE * post_sat_gain * fgain * cgain * electrons self.c_expected_du = du return du
def expected_du(self, pre_sat_gain=1, post_sat_gain=1, qeff_coefs=None, psf_coef=(1, 1, 1)): cam = self.frame.cam[self.cam_i] cgain = cam.gain * cam.aperture_area * cam.emp_coef fgain = self.frame.gain * self.frame.exposure queff_coefs = tuple( cam.qeff_coefs if qeff_coefs is None else qeff_coefs[self.cam_i]) if 0: p_elec, _ = Camera.electron_flux_in_sensed_spectrum( queff_coefs, self.t_eff, self.fe_h, self.log_g, self.mag_v, cam.lambda_min, cam.lambda_max) if 1: gomos_mag_v = self.mag_v # if self.bayer == 'alp_ori' else None electrons = sensed_electron_flux_star_spectrum( STAR_SPECTRA_PATH, self.bayer, self.mag_v, self.t_eff, self.log_g, self.fe_h, cam.lambda_min, cam.lambda_max, queff_coefs, gomos_mag_v) if 0: #self.bayer == 'alp_ori': spectrum_fn0 = Stars.synthetic_radiation_fn(self.t_eff, self.fe_h, self.log_g, mag_v=self.mag_v) spectrum_fn0b = Stars.synthetic_radiation_fn( self.t_eff, self.fe_h, self.log_g, mag_v=self.mag_v, model='ck04models', lam_min=cam.lambda_min - 10e-9, lam_max=cam.lambda_max + 10e-9) spectrum_fn1 = get_star_spectrum(STAR_SPECTRA_PATH, self.bayer, self.mag_v, self.t_eff, self.log_g, self.fe_h, cam.lambda_min, cam.lambda_max, gomos_mag_v) lams = np.linspace(cam.lambda_min, cam.lambda_max, 3000) plt.plot(lams, spectrum_fn0(lams)) plt.plot(lams, spectrum_fn0b(lams)) plt.plot(lams, spectrum_fn1(lams)) plt.title(self.bayer) plt.show() du = pre_sat_gain * RAW_IMG_MAX_VALUE * fgain * cgain * electrons self.c_unsat_du = du if StarFrame.STAR_SATURATION_MODELING == StarFrame.STAR_SATURATION_MODEL_MOTION: psf_coef = tuple(psf_coef) if StarFrame.STAR_SATURATION_MULTI_KERNEL else \ ((psf_coef[self.cam_i],) if len(psf_coef) == 3 else tuple(psf_coef)) du, self.c_px_du_sat = self._motion_kernel_psf_saturation( du, psf_coef, True) elif StarFrame.STAR_SATURATION_MODELING == StarFrame.STAR_SATURATION_MODEL_ANALYTICAL: du = self._analytical_psf_saturation(du, psf_coef[self.cam_i]) else: assert StarFrame.STAR_SATURATION_MODELING == StarFrame.STAR_SATURATION_MODEL_IDEAL # do nothing du *= post_sat_gain self.c_expected_du = du return du
def _match_stars(self, stars, max_dist=0.05, max_mag_diff=2.0, mag_cutoff=3.0, plot=False): """ match stars based on proximity """ merge_lim = 4 all_stars, cols = Stars.flux_density(self.q, self.cam[0], array=True, undistorted=True, mag_cutoff=mag_cutoff + merge_lim, order_by='mag_v') if self.debug: db_img = np.sqrt( Stars.flux_density(self.q, self.cam[0], mag_cutoff=10.0)) # override some star data, change None => nan for i, st in enumerate(all_stars): for j in range(len(st)): st[j] = np.nan if st[j] is None else st[j] if st[cols['id']] in self.override_star_data: for f in ('mag_v', 'mag_b', 't_eff', 'log_g', 'fe_h'): od = self.override_star_data[st[cols['id']]] if f in od: all_stars[i][cols[f]] = od[f] # merge close stars all_stars = np.array(all_stars) points = np.array([(s[cols['ix']], s[cols['iy']]) for s in all_stars]) D = tools.distance_mx(points, points) radius = 10 if self.cam[0].width > 300 else 2 db_stars = [] added = set() for i, s in enumerate(all_stars): if i in added: continue I = tuple( set( np.where( np.logical_and( D[i, :] < radius, all_stars[:, cols['mag_v']] - merge_lim < s[cols['mag_v']]))[0]) - added) cluster = [None] * (max(cols.values()) + 1) cluster[cols['id']] = tuple(all_stars[I, cols['id']].astype(np.int)) amag_v = 10**(-all_stars[I, cols['mag_v']] / 2.5) amag_b = 10**(-all_stars[I, cols['mag_b']] / 2.5) cluster[cols['mag_v']] = -2.5 * np.log10(np.sum(amag_v)) cluster[cols['mag_b']] = -2.5 * np.log10(np.sum(amag_b)) for c in ('ix', 'iy', 'dec', 'ra', 't_eff', 'fe_h', 'log_g'): E = np.where(all_stars[I, cols[c]] != None)[0] cluster[cols[c]] = np.sum(amag_v[E] * all_stars[I, cols[c]][E]) / np.sum( amag_v[E]) if len(E) else None if cluster[cols['mag_v']] < mag_cutoff: added.update(I) db_stars.append(cluster) img_st = np.array([(s['x'], s['y'], s['mag'], s['size']) for s in stars]) db_st = np.array([(s[cols['ix']], s[cols['iy']], s[cols['mag_v']]) for s in db_stars]) # adjust mags to match, not easy to make match directly as unknown variable black level removed in image sensor #b0, b1 = np.min(img_st[:, 2]), np.min(db_st[:, 2]) #d0, d1 = np.max(img_st[:, 2]), np.max(db_st[:, 2]) #img_st[:, 2] = (img_st[:, 2] - b0) * (d1-b1)/(d0-b0) + b1 #img_st[:, 2] = np.log10((10**img_st[:, 2] - 10**b0) * (10**d1-10**b1)/(10**d0-10**b0) + 10**b1) img_st[:, 2] = img_st[:, 2] - np.median(img_st[:, 2]) + np.median( db_st[:, 2]) if self.cam[0].dist_coefs is not None: db_st[:, :2] = Camera.distort( db_st[:, :2], self.cam[0].dist_coefs, self.cam[0].intrinsic_camera_mx(legacy=False), self.cam[0].inv_intrinsic_camera_mx(legacy=False)) M = (np.abs( np.repeat( np.expand_dims(img_st[:, 2:3], axis=0), len(db_st), axis=0) - np.repeat( np.expand_dims(db_st[:, 2:3], axis=1), len(img_st), axis=1)) ).squeeze() D = np.repeat(np.expand_dims(img_st[:, :2], axis=0), len(db_st), axis=0) \ - np.repeat(np.expand_dims(db_st[:, :2], axis=1), len(img_st), axis=1) D = np.sum(D**2, axis=2) D = D.flatten() D[M.flatten() > max_mag_diff] = np.inf D = D.reshape(M.shape) idxs = np.argmin(D, axis=0) max_dist = (self.image.shape[1] * max_dist)**2 m_idxs = {} for i1, j in enumerate(idxs): dist = D[j, i1] if dist > max_dist or j in m_idxs and dist > D[j, m_idxs[j]]: # discard match if distance too high or better match for same db star available continue m_idxs[j] = i1 matches = [None] * len(idxs) for j, i in m_idxs.items(): matches[i] = db_stars[j] if self.debug and DEBUG_MATCHING or plot: if plot: norm = ImageNormalize(stretch=SqrtStretch()) size = np.median(img_st[:, 3].astype('int')) data = np.mean(self.image.astype(np.float64) / (2**self.bits - 1), axis=2) ud_I = set(range(len(db_st))) - set(m_idxs.keys()) d_I = set(range(len(img_st))) - set(m_idxs.values()) # detected_pos = img_st[tuple(d_I), :2].astype('int') # matched_pos = img_st[tuple(m_idxs.values()), :2].astype('int') # undetected_pos = db_st[tuple(ud_I), :2].astype('int') plt.imshow(data, cmap='Greys', norm=norm) for i in d_I: # detected CircularAperture(img_st[i, :2].astype('int'), r=img_st[i, 3].astype('int')).plot( color='blue', lw=1.5, alpha=0.5) for i in m_idxs.values(): # matched CircularAperture(img_st[i, :2].astype('int'), r=img_st[i, 3].astype('int')).plot( color='green', lw=1.5, alpha=0.5) for i in ud_I: # undetected CircularAperture(db_st[i, :2].astype('int'), r=size).plot(color='red', lw=1.5, alpha=0.5) plt.show() else: dec, ra, pa = map(math.degrees, tools.q_to_ypr(self.q)) print('ra: %.1f, dec: %.1f, pa: %.1f' % (ra, dec, pa)) sc, isc = 1, (1024 if 0 else 2800) / (self.image.shape[1] * 2) img = np.sqrt(self.image) img = ((img / np.max(img)) * 255).astype('uint8') img = cv2.resize(img, None, fx=isc, fy=isc, interpolation=cv2.INTER_AREA) cv2.drawKeypoints(img, [ cv2.KeyPoint(x * isc, y * isc, 60 * sc) for x, y in db_st[:, :2] ], img, [0, 255, 0], cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) cv2.drawKeypoints(img, [ cv2.KeyPoint(x * isc, y * isc, 60 * sc) for x, y in img_st[:, :2] ], img, [255, 0, 0], cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) for j, i in m_idxs.items(): cv2.line( img, tuple(np.round(img_st[i, :2] * isc).astype('int')), tuple(np.round(db_st[j, :2] * isc).astype('int')), [0, 255, 0]) if DEBUG_MATCHING != 3: for j, pt in enumerate(db_st[:, :2]): cv2.putText(img, str(j), tuple(np.round(pt * isc).astype('int')), cv2.FONT_HERSHEY_SIMPLEX, 3 * sc, [0, 255, 0]) else: for i, pt in enumerate(img_st[:, :2]): text = '%.2f' % img_st[i, 2] #text = str(i) cv2.putText(img, text, tuple(np.round(pt * isc).astype('int')), cv2.FONT_HERSHEY_SIMPLEX, 1.6 * sc, [0, 0, 255]) db_img = np.repeat(np.expand_dims(db_img, axis=2), self.image.shape[2], axis=2) db_img = ((db_img / np.max(db_img)) * 255).astype('uint8') db_img = cv2.resize(db_img, None, fx=isc, fy=isc, interpolation=cv2.INTER_AREA) for j, pt in enumerate(db_st[:, :2]): if DEBUG_MATCHING == 1: text = '&'.join([ Stars.get_catalog_id(id) for id in db_stars[j][cols['id']] ]) elif DEBUG_MATCHING == 2: t_eff = db_stars[j][cols['t_eff']] t_eff2 = Stars.effective_temp( db_stars[j][cols['mag_b']] - db_stars[j][cols['mag_v']]) #if 1: # print('%s Teff: %s (%.1f)' % (Stars.get_catalog_id(db_stars[j][cols['id']]), t_eff, t_eff2)) text = ('%dK' % t_eff) if t_eff else ('(%dK)' % t_eff2) elif DEBUG_MATCHING == 3: text = '%.2f' % db_stars[j][cols['mag_v']] cv2.putText( db_img, text, tuple( np.round(pt * isc + np.array([5, -5])).astype('int')), cv2.FONT_HERSHEY_SIMPLEX, 1.6 * sc, [255, 0, 0]) cv2.drawKeypoints(db_img, [ cv2.KeyPoint(x * isc, y * isc, 60 * sc) for x, y in db_st[:, :2] ], db_img, [0, 255, 0], cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) img = np.hstack( (db_img, np.ones( (img.shape[0], 1, img.shape[2]), dtype='uint8') * 255, img)) cv2.imshow('test', img) cv2.waitKey() # plt.figure(1, (16, 12)) # plt.imshow(np.flip(img, axis=2)) # plt.tight_layout() # plt.show() return matches, cols
def analyze_aurora_img(img_file, get_bgr_cam): debug = 1 n = 0 Frame.MISSING_BG_REMOVE_STRIPES = 0 bgr_cam = get_bgr_cam(thumbnail=False, estimated=1, final=1) f = Frame.from_file(bgr_cam, img_file, img_file[:-4]+'.lbl', bg_offset=False, debug=debug) if 0: f.show_image(processed=True, save_as='C:/projects/s100imgs/processed-aurora.png') img = f.image.astype('float') if 0: img = img - np.percentile(img, 5, axis=1).reshape((-1, 1, 3)) bg1 = (830, 1070), (1180, 1400) # bg1 = (0, 900), (660, 1280) # bg2 = (1560, 1050), (2048, 1350) mean_bg = np.mean(img[bg1[0][1]:bg1[1][1], bg1[0][0]:bg1[1][0], :].reshape((-1, 3)), axis=0) # mean_bg = np.mean(np.vstack((img[bg1[0][1]:bg1[1][1], bg1[0][0]:bg1[1][0], :].reshape((-1, 3)), # img[bg2[0][1]:bg2[1][1], bg2[0][0]:bg2[1][0], :].reshape((-1, 3)))), axis=0) img = img - mean_bg # img = ImageProc.apply_point_spread_fn(img - mean_bg, 0.01) # img = np.clip(img, 0, 1023).astype('uint16') # img = cv2.medianBlur(img, 31) # img = np.clip(img, 0, RAW_IMG_MAX_VALUE) / RAW_IMG_MAX_VALUE n += 1 plt.figure(n) imsh = (np.clip(img * 2 + RAW_IMG_MAX_VALUE * 0.3, 0, RAW_IMG_MAX_VALUE) / RAW_IMG_MAX_VALUE * 255).astype('uint8') rd_y = (720, 700), (890, 720) rd_r1 = (720, 830), (890, 870) rd_r2 = (1080, 820), (1250, 860) gr_r = (1280, 770), (1450, 795) cv2.rectangle(imsh, bg1[0], bg1[1], (255, 0, 0), 2) # bg1 # cv2.rectangle(imsh, bg2[0], bg2[1], (255, 0, 0), 2) # bg2 cv2.rectangle(imsh, rd_y[0], rd_y[1], (0, 200, 200), 2) # yellow cv2.rectangle(imsh, rd_r1[0], rd_r1[1], (0, 0, 255), 2) # red1 cv2.rectangle(imsh, rd_r2[0], rd_r2[1], (0, 0, 255), 2) # red2 cv2.rectangle(imsh, gr_r[0], gr_r[1], (0, 255, 0), 2) # green plt.imshow(np.flip(imsh, axis=2)) plt.show() def electrons(lam): h = 6.626e-34 # planck constant (m2kg/s) c = 3e8 # speed of light return h * c / lam # energy per photon blue = 427.8e-9 green = 557.7e-9 yellow = 589.3e-9 red = 630.0e-9 colors = (blue, green, yellow, red) dus_per_rad = dict(zip(colors, ([], [], [], []))) # DUs per 1 W/m2/sr of radiance coef = f.exposure * f.gain * RAW_IMG_MAX_VALUE for wl in dus_per_rad.keys(): for cam in bgr_cam: cgain = cam.gain * cam.emp_coef * cam.aperture_area fn, _ = Camera.qeff_fn(tuple(cam.qeff_coefs), 350e-9, 1000e-9) # W/m2/sr => phot/s/m2/sr => elec/s/m2/sr => DUs/sr dus_per_rad[wl].append(1/electrons(wl) * fn(wl) * coef * cgain) for wl in dus_per_rad.keys(): dus_per_rad[wl] = np.array(dus_per_rad[wl]) class Patch: def __init__(self, name, rect, bands, mean=None, rad=None): self.name, self.rect, self.bands, self.mean, self.rad = name, rect, bands, mean, rad nt = lambda n, r, b: Patch(name=n, rect=r, bands=b) patches = [ nt('Clean Red', rd_r1, (blue, green, red)), nt('Strong Red', rd_r2, (blue, green, red)), nt('Green', gr_r, (blue, green, red)), nt('Sodium', rd_y, (blue, green, yellow)), ] # pseudo inverse for p in patches: p.mean = np.mean(img[p.rect[0][1]:p.rect[1][1], p.rect[0][0]:p.rect[1][0], :].reshape((-1, 3)), axis=0) px_sr = cam.pixel_solid_angle((p.rect[0][0]+p.rect[1][0])//2, (p.rect[0][1]+p.rect[1][1])//2) E = np.hstack((dus_per_rad[p.bands[0]], dus_per_rad[p.bands[1]], dus_per_rad[p.bands[2]])) * px_sr invE = np.linalg.inv(E.T.dot(E)).dot(E.T) rad = invE.dot(p.mean) # radiance in W/m2/sr # e = E.dot(rad) # diff = (p.mean - e) * 100 / np.linalg.norm(p.mean) p.rad = [''] * len(colors) for i, b in enumerate(p.bands): idx = colors.index(b) p.rad[idx] = rad[i] sep = '\t' if 1 else ' & ' le = '\n' if 1 else ' \\\\\n' if 0: print(sep.join(('Patch', 'Emission at', '', '', 'Red', 'Green', 'Blue')), end=le) for name, irr, mean, model, diff in patches: print(sep.join((name, '428 nm', ('%.3e' % irr[0]) if irr[0] else 'n/a', 'Mean', *('%.1f' % m for m in np.flip(mean.flatten())))), end=le) print(sep.join(('', '557.7 nm', ('%.3e' % irr[1]) if irr[1] else 'n/a', 'Modeled', *('%.1f' % m for m in np.flip(model.flatten())))), end=le) print(sep.join(('', '589 nm', ('%.3e' % irr[2]) if irr[2] else 'n/a', 'Diff. [%]', *('%.1f' % m for m in np.flip(diff.flatten())))), end=le) print(sep.join(('', '630 nm', ('%.3e' % irr[3]) if irr[3] else 'n/a', *(['']*4))), end=le) else: print(sep.join(('Patch', 'Red', 'Green', 'Blue', '428 nm', '557.7 nm', '589 nm', '630 nm')), end=le) for p in patches: # in kilo Rayleigh (kR) == 6330*1e9*lambda * W/m2/sr or 4*pi*10^(-10)*10^(-3) * photon flux print(sep.join((p.name, *('%.1f' % m for m in np.flip(p.mean.flatten())), *(tools.fixed_precision(r*4*np.pi*1e-13/electrons(colors[i]), 3, True) if r else '' # r*1e-13*4*np.pi for i, r in enumerate(p.rad)))), end=le) quit() aurora = np.zeros_like(img) for i, color in enumerate(colors): # d/dx[(r-aw)'*(r-aw)] == 0 # => w == (r'*a)/(a'*a) a = emission[color] w = np.sum(img.reshape((-1, 3)) * a.T, axis=1) / sum(a**2) e = (w*a).T r = img.reshape((-1, 3)) - e x = w / np.linalg.norm(r, axis=1) # plt.figure(2) # plt.imshow(w.reshape(img.shape[:2])/np.max(w)) # plt.title('weight (max=%f)' % np.max(w)) # plt.figure(3) # plt.imshow(x.reshape(img.shape[:2])/np.max(x)) # plt.title('x (max=%f)' % np.max(x)) n += 1 plt.figure(n) x[x < {red: 10, green: 6, yellow: 16}[color]] = 0 x[w < 100] = 0 xf = ImageProc.apply_point_spread_fn(x.reshape(img.shape[:2]), 0.03) xf = cv2.medianBlur(xf.astype('uint16'), 11) plt.imshow(xf / np.max(xf)) plt.title('Emission detection @ %.1fnm' % (color * 1e9)) e[xf.flatten() == 0, :] = (0, 0, 0) aurora += e.reshape(img.shape) # plt.figure(6) # plt.imshow(np.flip(e.reshape(img.shape) / np.max(e), axis=2)) # plt.title('modeled aurora') # plt.figure(7) # plt.imshow(np.flip(r.reshape(img.shape)/np.max(r), axis=2)) # plt.title('residual') # plt.show() plt.figure(8) plt.imshow(np.flip(aurora / np.max(aurora), axis=2)) plt.title('modeled aurora') plt.show() # TODO: translate rgb values to aurora (ir)radiance # - following uses W/m2/sr for "in-band radiance" # - https://www.osapublishing.org/DirectPDFAccess/A2F3D832-975A-1850-088634AAFCF21258_186134/ETOP-2009-ESB4.pdf?da=1&id=186134&uri=ETOP-2009-ESB4&seq=0&mobile=no # - use pixel sr? print('done')
def cost_fun(x, measures, prior_x, return_details=False, plot=False): c_qeff_coefs, f_gains, gain_adj, psf_coef = decode(x) band = [] obj_ids = [] measured_du = [] expected_du = [] weights = [] for m in measures: if FRAME_GAINS == FRAME_GAIN_SAME: pre_sat_gain = f_gains[0] elif FRAME_GAINS == FRAME_GAIN_INDIVIDUAL: pre_sat_gain = f_gains[m.frame.id] elif FRAME_GAINS == FRAME_GAIN_STATIC: if m.obj_id[0] == 'moon': pre_sat_gain = MOON_GAIN_ADJ else: pre_sat_gain = STAR_GAIN_ADJUSTMENT if m.frame.cam[ 0].emp_coef >= 1 else STAR_GAIN_ADJUSTMENT_TN else: pre_sat_gain = 1 edu = m.expected_du(pre_sat_gain=pre_sat_gain, post_sat_gain=gain_adj, qeff_coefs=c_qeff_coefs, psf_coef=psf_coef) if return_details or (m.obj_id[0], m.cam_i) not in IGNORE_MEASURES: expected_du.append(edu) measured_du.append(m.du_count) weights.append(m.weight) band.append(m.cam_i) obj_ids.append(m.obj_id) measured_du, expected_du, band = map( np.array, (measured_du, expected_du, band)) if plot: plt.rcParams.update({'font.size': 16}) fig, ax = plt.subplots(1, 1, figsize=[6.4, 4.8]) sb, = ax.plot(expected_du[band == 0] * 1e-3, measured_du[band == 0] * 1e-3, 'bx') sg, = ax.plot(expected_du[band == 1] * 1e-3, measured_du[band == 1] * 1e-3, 'gx') sr, = ax.plot(expected_du[band == 2] * 1e-3, measured_du[band == 2] * 1e-3, 'rx') line = np.linspace(0, np.max(expected_du)) ax.plot(line * 1e-3, line * 1e-3, 'k--', linewidth=0.5) ax.set_xlabel('Expected [1000 DNs]') ax.set_ylabel('Measured [1000 DNs]') names = Stars.get_catalog_id( np.unique(list(s[0] for s in obj_ids if s[0] != 'moon')), 'simbad') names['moon'] = 'Moon' labels = np.array([names[id[0]] for id in obj_ids]) tools.hover_annotate(fig, ax, sb, labels[band == 0]) tools.hover_annotate(fig, ax, sg, labels[band == 1]) tools.hover_annotate(fig, ax, sr, labels[band == 2]) plt.tight_layout() plt.show() _, _, gain_adj0, _ = decode(prior_x) # err = tuple(tools.pseudo_huber_loss(STAR_CALIB_HUBER_COEF, (measured_du - expected_du) * 2 / (expected_du + measured_du)) * np.array(weights)) err = tuple( tools.pseudo_huber_loss( STAR_CALIB_HUBER_COEF, np.log10(expected_du) - np.log10(measured_du)) * np.array(weights)) n = 3 * len(c_qeff_coefs[0]) lab_dp = tuple() if STAR_LAB_DATAPOINT_WEIGHT > 0: c, lam = m.frame.cam, 557.7e-9 g = Camera.sample_qeff(c_qeff_coefs[1], c[1].lambda_min, c[1].lambda_max, lam) eps = 1e-10 r_g = (Camera.sample_qeff(c_qeff_coefs[2], c[2].lambda_min, c[2].lambda_max, lam) + eps) / (g + eps) b_g = (Camera.sample_qeff(c_qeff_coefs[0], c[0].lambda_min, c[0].lambda_max, lam) + eps) / (g + eps) lab_dp = tuple(STAR_LAB_DATAPOINT_WEIGHT * (np.log10(r_g) - np.log10(np.array((0.26, 0.25, 0.24, 0.24))))**2) \ +tuple(STAR_LAB_DATAPOINT_WEIGHT * (np.log10(b_g) - np.log10(np.array((0.23, 0.24, 0.21, 0.22))))**2) prior = tuple(STAR_CALIB_PRIOR_WEIGHT ** 2 * (np.array(x[:n]) - np.array(prior_x[:n])) ** 2) \ if STAR_CALIB_PRIOR_WEIGHT > 0 else tuple() err_tuple = lab_dp + err + prior return (err_tuple, measured_du, expected_du) if return_details else \ err_tuple if opt_method == 'leastsq' else \ np.sum(err_tuple)
def __init__(self, target_primary=True, hi_res_shape_model=False, use_narrow_cam=True, res_mult=1.0): # gives some unnecessary warning about "dubious year" even when trying to ignore it with warnings.catch_warnings(): warnings.simplefilter("ignore") min_time = Time('2023-01-01 00:00:00', scale='utc', format='iso') common_kwargs_worst = { 'sensor_size': (2048 * 0.0022, 1944 * 0.0022), # diagonal: 6.2mm 'quantum_eff': 0.30, 'px_saturation_e': 2200, # snr_max = 20*log10(sqrt(sat_e)) dB 'lambda_min': 350e-9, 'lambda_eff': 580e-9, 'lambda_max': 800e-9, 'dark_noise_mu': 40, 'dark_noise_sd': 6.32, 'readout_noise_sd': 15, # dark_noise_sd should be sqrt(dark_noise_mu) 'emp_coef': 1, # dynamic range = 20*log10(sat_e/readout_noise)) 'exclusion_angle_x': 55, 'exclusion_angle_y': 90, } common_kwargs_best = dict(common_kwargs_worst) common_kwargs_best.update({ 'quantum_eff': 0.4, 'px_saturation_e': 3500, 'dark_noise_mu': 25, 'dark_noise_sd': 5, 'readout_noise_sd': 5, }) common_kwargs = common_kwargs_best narrow_cam = Camera( 2048 * res_mult, # width in pixels 1944 * res_mult, # height in pixels 7.7, # x fov in degrees (could be 6 & 5.695, 5.15 & 4.89, 7.7 & 7.309) 7.309, # y fov in degrees f_stop=5, # TODO: put better value here point_spread_fn=0.50, # ratio of brightness in center pixel scattering_coef= 2e-10, # affects strength of haze/veil when sun shines on the lens **common_kwargs) wide_cam = Camera( 2048 * res_mult, # width in pixels 1944 * res_mult, # height in pixels 61.5, # x fov in degrees 58.38, # y fov in degrees f_stop=5, # TODO: put better value here point_spread_fn=0.35, # ratio of brightness in center pixel scattering_coef= 2e-10, # affects strength of haze/veil when sun shines on the lens **common_kwargs) if target_primary: if use_narrow_cam: mission_id = 'didy1n' limits = ( 3.5, # min_distance in km 6.5, # min_med_distance in km 10.5, # max_med_distance in km 10.5, # max_distance in km 45, # min_elong in deg min_time, # min time instant ) else: mission_id = 'didy1w' limits = ( 1.0, # min_distance in km 1.1, # min_med_distance in km 6.0, # max_med_distance in km 10.5, # max_distance in km 45, # min_elong in deg min_time, # min time instant ) else: if use_narrow_cam: mission_id = 'didy2n' limits = ( 1.0, # min_distance in km 1.65, # min_med_distance in km 5.3, # max_med_distance in km 7.0, # max_distance in km 45, # min_elong in deg min_time, # min time instant ) else: mission_id = 'didy2w' limits = ( 0.135, # min_distance in km 0.28, # min_med_distance in km 1.3, # max_med_distance in km 1.3, # max_distance in km 45, # min_elong in deg min_time, # min time instant ) super(DidymosSystemModel, self).__init__( asteroid=DidymosPrimary(hi_res_shape_model=hi_res_shape_model) if target_primary else DidymosSecondary( hi_res_shape_model=hi_res_shape_model), camera=narrow_cam if use_narrow_cam else wide_cam, limits=limits, ) self.mission_id = mission_id self.sc_model_file = os.path.join(DATA_DIR, 'apex-x1-2019-05-28.obj')
def replay_keyframes(cam: Camera, keyframes: List[Frame] = None, map3d: List[Keypoint] = None, file: str = 'results.pickle'): import cv2 if keyframes is None: with open(file, 'rb') as fh: keyframes, map3d, frame_names, meta_names, ground_truth, *ba_errs = pickle.load( fh) ba_errs = ba_errs[0] if len(ba_errs) else None if isinstance(keyframes[0], Frame): keyframes = [ dict(pose=kf.pose, meas=kf.measure, time=kf.time, id=kf.id, kps_uv=kf.kps_uv, image=kf.image) for kf in keyframes ] kp_size, kp_color = 5, (200, 0, 0) kp_ids = set(kf.id for kf in map3d if not kf.bad_qlt and kf.inlier_count > 2 and kf.inlier_count / kf.total_count > 0.2) map3d = {kf.id: kf for kf in map3d} img_scale = 0.5 for kf in keyframes: if kf['pose'] is None or kf['pose'].post is None: continue obs_ids = list(kp_ids.intersection(kf['kps_uv'].keys())) if len(obs_ids) == 0: continue image = kf['image'].copy() if 'image' in kf else np.zeros( (int(cam.height * img_scale), int(cam.width * img_scale), 3), dtype=np.uint8) if len(image.shape) == 2 or image.shape[2] == 1: image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) p_pts2d = (np.array([kf['kps_uv'][id] for id in obs_ids]) + 0.5).astype(int).squeeze() p_pts3d = np.array([map3d[id].pt3d for id in obs_ids]) pts3d_cf = tools.q_times_mx(kf['pose'].post.quat, p_pts3d) + kf['pose'].post.loc pts2d_proj = (cam.project(pts3d_cf.astype(np.float32)) * img_scale + 0.5).astype(int) for (x, y), (xp, yp) in zip(p_pts2d, pts2d_proj): image = cv2.circle(image, (x, y), kp_size, kp_color, 1) # negative thickness => filled circle image = cv2.rectangle(image, (xp - 2, yp - 2), (xp + 2, yp + 2), kp_color, 1) image = cv2.line(image, (xp, yp), (x, y), kp_color, 1) cv2.imshow('keypoint reprojection', image) cv2.waitKey()