Beispiel #1
0
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]
Beispiel #2
0
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]
Beispiel #3
0
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, ...]
Beispiel #4
0
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]
Beispiel #5
0
         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
Beispiel #6
0
              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)
Beispiel #7
0
                     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):