def plotoptions_measurement_boundaries(**optimization_inputs): r'''Return the 'set' plot options for gnuplotlib to show the measurement boundaries SYNOPSIS import numpy as np import gnuplotlib as gp import mrcal model = mrcal.cameramodel('xxx.cameramodel') optimization_inputs = model.optimization_inputs() x = mrcal.optimizer_callback(**optimization_inputs)[1] gp.plot( np.abs(x), _set = mrcal.plotoptions_measurement_boundaries(**optimization_inputs) ) # a plot pops up showing the magnitude of each measurement, with boundaries # between the different measurements denoted When plotting the measurement vector (or anything relating to it, such as columns of the Jacobian), it is usually very useful to infer at a glance the meaning of each part of the plot. This function returns a list of 'set' directives passable to gnuplotlib that show the boundaries inside the measurement vector. ARGUMENTS **optimization_inputs: a dict() of arguments passable to mrcal.optimize() and mrcal.optimizer_callback(). These define the full optimization problem, and can be obtained from the optimization_inputs() method of mrcal.cameramodel RETURNED VALUE A list of 'set' directives passable as plot options to gnuplotlib ''' imeas0 = [] try: imeas0.append(mrcal.measurement_index_boards(0, **optimization_inputs)) except: pass try: imeas0.append(mrcal.measurement_index_points(0, **optimization_inputs)) except: pass try: imeas0.append( mrcal.measurement_index_regularization(0, **optimization_inputs)) except: pass return [f"arrow nohead from {x},graph 0 to {x},graph 1" for x in imeas0]
def plotoptions_state_boundaries(**optimization_inputs): r'''Return the 'set' plot options for gnuplotlib to show the state boundaries SYNOPSIS import numpy as np import gnuplotlib as gp import mrcal model = mrcal.cameramodel('xxx.cameramodel') optimization_inputs = model.optimization_inputs() J = mrcal.optimizer_callback(**optimization_inputs)[2] gp.plot( np.sum(np.abs(J.toarray()), axis=-2), _set = mrcal.plotoptions_state_boundaries(**optimization_inputs) ) # a plot pops up showing the magnitude of the effects of each element of the # packed state (as seen by the optimizer), with boundaries between the # different state variables denoted When plotting the state vector (or anything relating to it, such as rows of the Jacobian), it is usually very useful to infer at a glance the meaning of each part of the plot. This function returns a list of 'set' directives passable to gnuplotlib that show the boundaries inside the state vector. ARGUMENTS **optimization_inputs: a dict() of arguments passable to mrcal.optimize() and mrcal.optimizer_callback(). These define the full optimization problem, and can be obtained from the optimization_inputs() method of mrcal.cameramodel RETURNED VALUE A list of 'set' directives passable as plot options to gnuplotlib ''' istate0 = [] try: istate0.append(mrcal.state_index_intrinsics (0, **optimization_inputs)) except: pass try: istate0.append(mrcal.state_index_extrinsics (0, **optimization_inputs)) except: pass try: istate0.append(mrcal.state_index_frames (0, **optimization_inputs)) except: pass try: istate0.append(mrcal.state_index_points (0, **optimization_inputs)) except: pass try: istate0.append(mrcal.state_index_calobject_warp( **optimization_inputs)) except: pass return [f"arrow nohead from {x},graph 0 to {x},graph 1" for x in istate0]
def residuals_chessboard(optimization_inputs, i_cam=None, residuals=None): r'''Compute and return the chessboard residuals SYNOPSIS model = mrcal.cameramodel(model_filename) gp.plot( mrcal.residuals_chessboard( optimization_inputs = model.optimization_inputs(), i_cam = i_cam ).ravel(), histogram = True, binwidth = 0.02 ) ... A plot pops up showing the empirical distribution of chessboard fit ... errors in this solve. For ALL the cameras Given a calibration solve, returns the residuals of chessboard observations, throwing out outliers and, optionally, selecting the residuals from a specific camera. ARGUMENTS - optimization_inputs: the optimization inputs dict passed into and returned from mrcal.optimize(). This describes the solved optimization problem that we're visualizing - i_cam: optional integer to select the camera whose residuals we're visualizing If omitted or None, we report the residuals for ALL the cameras together. - residuals: optional numpy array of shape (Nmeasurements,) containing the optimization residuals. If omitted or None, this will be recomputed. To use a cached value, pass the result of mrcal.optimize(**optimization_inputs)['x'] or mrcal.optimizer_callback(**optimization_inputs)[1] RETURNED VALUES Numpy array of shape (N,2) of all the residuals. N is the number of pixel observations remaining after outliers and other cameras are thrown out ''' if residuals is None: # Flattened residuals. The board measurements are at the start of the # array residuals = \ mrcal.optimizer_callback(**optimization_inputs, no_jacobian = True, no_factorization = True)[1] # shape (Nobservations, object_height_n, object_width_n, 3) observations = optimization_inputs['observations_board'] residuals_shape = observations.shape[:-1] + (2, ) # shape (Nobservations, object_height_n, object_width_n, 2) residuals = residuals[:np.product(residuals_shape)].reshape( *residuals_shape) # shape (Nobservations, object_height_n, object_width_n, 3) indices_frame_camera = optimization_inputs[ 'indices_frame_camintrinsics_camextrinsics'][..., :2] # shape (Nobservations, object_height_n, object_width_n) idx = np.ones(observations.shape[:-1], dtype=bool) if i_cam is not None: # select residuals from THIS camera idx[indices_frame_camera[:, 1] != i_cam, ...] = False # select non-outliers idx[observations[..., 2] <= 0.0] = False # shape (N,2) return residuals[idx, ...]
def ingest_packed_state(p_packed, **optimization_inputs): r'''Read a given packed state into optimization_inputs SYNOPSIS # A simple gradient check model = mrcal.cameramodel('xxx.cameramodel') optimization_inputs = model.optimization_inputs() p0,x0,J = mrcal.optimizer_callback(no_factorization = True, **optimization_inputs)[:3] dp = np.random.randn(len(p0)) * 1e-9 mrcal.ingest_packed_state(p0 + dp, **optimization_inputs) x1 = mrcal.optimizer_callback(no_factorization = True, no_jacobian = True, **optimization_inputs)[1] dx_observed = x1 - x0 dx_predicted = nps.inner(J, dp_packed) This is the converse of mrcal.optimizer_callback(). One thing mrcal.optimizer_callback() does is to convert the expanded (intrinsics, extrinsics, ...) arrays into a 1-dimensional scaled optimization vector p_packed. mrcal.ingest_packed_state() allows updates to p_packed to be absorbed back into the (intrinsics, extrinsics, ...) arrays for further evaluation with mrcal.optimizer_callback() and others. ARGUMENTS - p_packed: a numpy array of shape (Nstate,) containing the input packed state - **optimization_inputs: a dict() of arguments passable to mrcal.optimize() and mrcal.optimizer_callback(). The arrays in this dict are updated RETURNED VALUE None ''' intrinsics = optimization_inputs.get("intrinsics") extrinsics = optimization_inputs.get("extrinsics_rt_fromref") frames = optimization_inputs.get("frames_rt_toref") points = optimization_inputs.get("points") calobject_warp = optimization_inputs.get("calobject_warp") Npoints_fixed = optimization_inputs.get('Npoints_fixed', 0) Nvars_intrinsics = mrcal.num_states_intrinsics(**optimization_inputs) Nvars_extrinsics = mrcal.num_states_extrinsics(**optimization_inputs) Nvars_frames = mrcal.num_states_frames(**optimization_inputs) Nvars_points = mrcal.num_states_points(**optimization_inputs) Nvars_calobject_warp = mrcal.num_states_calobject_warp( **optimization_inputs) Nvars_expected = \ Nvars_intrinsics + \ Nvars_extrinsics + \ Nvars_frames + \ Nvars_points + \ Nvars_calobject_warp # Defaults MUST match those in OPTIMIZER_ARGUMENTS_OPTIONAL in # mrcal-pywrap.c. Or better yet, this whole function should # come from the C code instead of being reimplemented here in Python do_optimize_intrinsics_core = optimization_inputs.get( 'do_optimize_intrinsics_core', True) do_optimize_intrinsics_distortions = optimization_inputs.get( 'do_optimize_intrinsics_distortions', True) do_optimize_extrinsics = optimization_inputs.get('do_optimize_extrinsics', True) do_optimize_frames = optimization_inputs.get('do_optimize_frames', True) do_optimize_calobject_warp = optimization_inputs.get( 'do_optimize_calobject_warp', True) if p_packed.ravel().size != Nvars_expected: raise Exception( f"Mismatched array size: p_packed.size={p_packed.ravel().size} while the optimization problem expects {Nvars_expected}" ) p = p_packed.copy() mrcal.unpack_state(p, **optimization_inputs) if do_optimize_intrinsics_core or \ do_optimize_intrinsics_distortions: ivar0 = mrcal.state_index_intrinsics(0, **optimization_inputs) if ivar0 is not None: iunpacked0, iunpacked1 = None, None # everything by default lensmodel = optimization_inputs['lensmodel'] has_core = mrcal.lensmodel_metadata_and_config( lensmodel)['has_core'] Ncore = 4 if has_core else 0 Ndistortions = mrcal.lensmodel_num_params(lensmodel) - Ncore if not do_optimize_intrinsics_core: iunpacked0 = Ncore if not do_optimize_intrinsics_distortions: iunpacked1 = -Ndistortions intrinsics[:, iunpacked0:iunpacked1].ravel()[:] = \ p[ ivar0:Nvars_intrinsics ] if do_optimize_extrinsics: ivar0 = mrcal.state_index_extrinsics(0, **optimization_inputs) if ivar0 is not None: extrinsics.ravel()[:] = p[ivar0:ivar0 + Nvars_extrinsics] if do_optimize_frames: ivar0 = mrcal.state_index_frames(0, **optimization_inputs) if ivar0 is not None: frames.ravel()[:] = p[ivar0:ivar0 + Nvars_frames] if do_optimize_frames: ivar0 = mrcal.state_index_points(0, **optimization_inputs) if ivar0 is not None: points.ravel()[:-Npoints_fixed * 3] = p[ivar0:ivar0 + Nvars_points] if do_optimize_calobject_warp: ivar0 = mrcal.state_index_calobject_warp(**optimization_inputs) if ivar0 is not None: calobject_warp.ravel()[:] = p[ivar0:ivar0 + Nvars_calobject_warp]
calibration_object_spacing = object_spacing, do_apply_regularization = True) mrcal.optimize(**baseline, do_apply_outlier_rejection=False) # Done setting up. I'll be looking at tiny motions off the baseline Nframes = len(frames_ref) Ncameras = len(intrinsics_ref) lensmodel = baseline['lensmodel'] Nintrinsics = mrcal.lensmodel_num_params(lensmodel) Nmeasurements_boards = mrcal.num_measurements_boards(**baseline) Nmeasurements_regularization = mrcal.num_measurements_regularization( **baseline) p0, x0, J0 = mrcal.optimizer_callback(no_factorization=True, **baseline)[:3] J0 = J0.toarray() ########################################################################### # First a very basic gradient check. Looking at an arbitrary camera's # intrinsics. The test-gradients tool does this much more thoroughly optimization_inputs = copy.deepcopy(baseline) dp_packed = np.random.randn(len(p0)) * 1e-9 mrcal.ingest_packed_state(p0 + dp_packed, **optimization_inputs) x1 = mrcal.optimizer_callback(no_factorization=True, no_jacobian=True, **optimization_inputs)[1] dx_observed = x1 - x0
frames_rt_toref = frames_rt_toref, points = points, observations_board = observations_copy, indices_frame_camintrinsics_camextrinsics = indices_frame_camintrinsics_camextrinsics, observations_point = observations_point, indices_point_camintrinsics_camextrinsics = indices_point_camintrinsics_camextrinsics, lensmodel = lensmodel, calobject_warp = np.array((1e-3, 2e-3)), imagersizes = imagersizes, calibration_object_spacing = 0.1, point_min_range = 1.0, point_max_range = 1000.0, verbose = False, **kwargs ) x, J = mrcal.optimizer_callback(**optimization_inputs)[1:3] J = J.toarray() # let's make sure that pack and unpack work correctly J2 = J.copy() mrcal.pack_state(J2, **optimization_inputs) mrcal.unpack_state(J2, **optimization_inputs) testutils.confirm_equal(J2, J, "unpack(pack(J)) = J") J2 = J.copy() mrcal.unpack_state(J2, **optimization_inputs) mrcal.pack_state(J2, **optimization_inputs) testutils.confirm_equal(J2, J, "pack(unpack(J)) = J") # I compare full-state J so that I can change SCALE_... without breaking the # test mrcal.pack_state(J, **optimization_inputs)
optimization_inputs_baseline['frames_rt_toref'], frames_true, q_true, lensmodel, stabilize_coords = args.stabilize_coords) testutils.confirm_equal(p_triangulated, p_triangulated_true, worstcase = True, eps = 1.0, msg = "Re-optimized triangulation should be close to the reference. This checks the regularization bias") # I have q0,i0 -> v0 # q1,i1 -> vlocal1 # vlocal1,r0r,r1r -> v1 # r0r,r1r,t0r,t1r -> t01 # v0,v1,t01 -> p_triangulated ppacked,x,Jpacked,factorization = mrcal.optimizer_callback(**optimization_inputs_baseline) Nstate = len(ppacked) # I store dp_triangulated_dp initialy, without worrying about the "packed" part. # I'll scale the thing when done to pack it dp_triangulated_dpstate = np.zeros((Npoints,3,Nstate), dtype=float) istate_i0 = mrcal.state_index_intrinsics(0, **optimization_inputs_baseline) istate_i1 = mrcal.state_index_intrinsics(1, **optimization_inputs_baseline) # I'm expecting the layout of a vanilla calibration problem, and I assume that # camera0 is at the reference below. Here I confirm that this assumption is # correct icam_extrinsics0 = mrcal.corresponding_icam_extrinsics(0, **optimization_inputs_baseline) icam_extrinsics1 = mrcal.corresponding_icam_extrinsics(1, **optimization_inputs_baseline) if not (icam_extrinsics0 < 0 and icam_extrinsics1 == 0):