def compute_hand_eye_calibration_RANSAC(dq_B_H_vec, dq_W_E_vec, config):
  """
  Runs various RANSAC-based hand-eye calibration algorithms.
   - RANSAC using the position/orientation error to determine model inliers
   - RANSAC using the scalar part equality constraint to determine model inliers
   For evaluation purposes:
   - Exhaustive search versions of both algorithms above.

   Outputs a tuple containing:
    - success
    - dq_H_E
    - (best_rmse_position, best_rmse_orientation)
    - best_num_inliers
    - num_poses_after_filtering
    - runtime
    - singular_values
    - bad_singular_values
  """
  assert len(dq_W_E_vec) == len(dq_B_H_vec)

  start_time = timeit.default_timer()

  num_poses = len(dq_W_E_vec)
  assert config.ransac_sample_size < num_poses, (
      "The RANSAC sample size ({}) is bigger than the number "
      "of poses ({})!".format(config.ransac_sample_size, num_poses))

  # Reject pairs whose motion is not informative,
  # i.e. their screw axis dot product is large
  if config.prefilter_poses_enabled:
    dq_B_H_vec_filtered, dq_W_E_vec_filtered = prefilter_using_screw_axis(
        dq_B_H_vec, dq_W_E_vec, config.prefilter_dot_product_threshold)
    assert len(dq_W_E_vec_filtered) == len(dq_B_H_vec_filtered)
    assert len(dq_B_H_vec) == num_poses
    assert len(dq_W_E_vec) == num_poses
    num_poses_after_filtering = len(dq_W_E_vec_filtered)
  else:
    dq_B_H_vec_filtered = dq_B_H_vec
    dq_W_E_vec_filtered = dq_W_E_vec
    num_poses_after_filtering = num_poses

  print("Ignore {} poses based on the screw axis".format(
      num_poses - num_poses_after_filtering))
  print("Drawing samples from remaining {} poses".format(
      num_poses_after_filtering))

  indices_set = set(range(0, num_poses_after_filtering))

  # Result variables:
  best_inlier_idx_set = None
  best_num_inliers = 0
  best_rmse_position = np.inf
  best_rmse_orientation = np.inf
  best_estimated_dq_H_E = None
  best_singular_values = None
  best_singular_value_status = True

  all_sample_combinations = []
  max_number_samples = np.inf
  if not config.enable_exhaustive_search:
    print("Running RANSAC...")
    print("Inlier classification method: {}".format(
        config.ransac_inlier_classification))
  else:
    all_sample_combinations = list(
        itertools.combinations(indices_set, config.ransac_sample_size))
    max_number_samples = len(all_sample_combinations)
    print("Running exhaustive search, exploring {} "
          "sample combinations...".format(max_number_samples))

  sample_number = 0
  full_iterations = 0
  prerejected_samples = 0
  while ((not config.enable_exhaustive_search and
          full_iterations < config.ransac_max_number_iterations) or
         (config.enable_exhaustive_search and
          sample_number < max_number_samples)):

    # Get sample, either at:
    #  - random (RANSAC)
    #  - from the list of all possible samples (exhaustive search)
    if config.enable_exhaustive_search:
      sample_indices = list(all_sample_combinations[sample_number])
    else:
      sample_indices = random.sample(indices_set,
                                     config.ransac_sample_size)
    sample_number += 1

    # Extract sampled poses.
    samples_dq_W_E = [dq_W_E_vec_filtered[idx] for idx in sample_indices]
    samples_dq_B_H = [dq_B_H_vec_filtered[idx] for idx in sample_indices]
    assert len(samples_dq_W_E) == len(samples_dq_B_H)
    assert len(samples_dq_W_E) == config.ransac_sample_size

    if config.ransac_sample_size > 1:
      # Transform all sample poses such that the first pose becomes the origin.
      aligned_samples_dq_B_H = align_paths_at_index(samples_dq_B_H,
                                                    align_index=0)
      aligned_samples_dq_W_E = align_paths_at_index(samples_dq_W_E,
                                                    align_index=0)
      assert len(aligned_samples_dq_B_H) == len(aligned_samples_dq_W_E)

      # Reject the sample early if not even the samples have a
      # similar scalar part. This should speed up RANSAC and is required, as
      # the hand eye calibration does not accept outliers.
      good_sample = True
      for i in range(0, config.ransac_sample_size):
        scalar_parts_W_E = aligned_samples_dq_W_E[i].scalar()
        scalar_parts_B_H = aligned_samples_dq_B_H[i].scalar()
        if not np.allclose(
                scalar_parts_W_E.dq, scalar_parts_B_H.dq,
                atol=config.ransac_sample_rejection_scalar_part_equality_tolerance):
          good_sample = False
          prerejected_samples += 1
          break

      if not good_sample:
        continue

    # Transform all poses based on the first sample pose and rearange poses,
    # such that the first sample pose is the first pose.
    aligned_dq_B_H = align_paths_at_index(dq_B_H_vec, sample_indices[0])
    aligned_dq_W_E = align_paths_at_index(dq_W_E_vec, sample_indices[0])
    assert len(aligned_dq_B_H) == num_poses
    assert len(aligned_dq_W_E) == num_poses

    # Compute model and determine inliers
    dq_H_E_initial = None
    num_inliers = 0
    inlier_dq_B_H = []
    inlier_dq_W_E = []
    if config.ransac_inlier_classification == "rmse_threshold":
      assert config.ransac_sample_size >= 2, (
          "Cannot compute the hand eye calibration with a "
          "sample size of less than 2!")
      try:
        # Compute initial hand-eye calibration on SAMPLES only.
        (dq_H_E_initial,
         singular_values,
         bad_singular_values) = compute_hand_eye_calibration(
            aligned_samples_dq_B_H, aligned_samples_dq_W_E,
            config.hand_eye_calibration_scalar_part_equality_tolerance)
        dq_H_E_initial.normalize()
      except:
        print("\n\n Hand-eye calibration FAILED! "
              "algorithm_name: {} exception: \n\n".format(
                  config.algorithm_name, sys.exc_info()[0]))
        continue

      # Inliers are determined by evaluating the hand-eye calibration computed
      # based on the samples on all the poses and thresholding the RMSE of the
      # position/orientation.
      (poses_B_H, poses_W_H) = get_aligned_poses(aligned_dq_B_H,
                                                 aligned_dq_W_E,
                                                 dq_H_E_initial)
      (rmse_position,
       rmse_orientation,
       inlier_flags) = evaluate_alignment(poses_B_H, poses_W_H, config)
      assert inlier_flags[0], ("The sample idx used for alignment should be "
                               "part of the inlier set!")

      num_inlier_removed_due_to_scalar_part_inequality = 0
      for i in range(0, num_poses):
        if inlier_flags[i]:
          scalar_parts_W_E = aligned_dq_W_E[i].scalar()
          scalar_parts_B_H = aligned_dq_B_H[i].scalar()
          if not np.allclose(
                  scalar_parts_W_E.dq, scalar_parts_B_H.dq,
                  atol=config.ransac_sample_rejection_scalar_part_equality_tolerance):
            num_inlier_removed_due_to_scalar_part_inequality += 1
            inlier_flags[i] = False

      if num_inlier_removed_due_to_scalar_part_inequality > 0:
        print("WARNING: At least one inlier selected based on the "
              "position/orientation error did not pass the scalar part "
              "equality test! Use tighter values for "
              "ransac_position_error_threshold_m and "
              "ransac_orientation_error_threshold_deg. "
              "Inliers removed: {}".format(
                  num_inlier_removed_due_to_scalar_part_inequality))
        # TODO(mfehr): Find a good way to tune the parameters.
        continue

    elif config.ransac_inlier_classification == "scalar_part_equality":
      # Inliers are determined without computing an initial model but by simply
      # selecting all poses that have a matching scalar part.
      inlier_flags = [False] * num_poses
      for i in range(0, num_poses):
        scalar_parts_B_H = aligned_dq_B_H[i].scalar()
        scalar_parts_W_E = aligned_dq_W_E[i].scalar()
        if np.allclose(scalar_parts_W_E.dq, scalar_parts_B_H.dq,
                       atol=config.ransac_sample_rejection_scalar_part_equality_tolerance):
          inlier_flags[i] = True

    else:
      assert False, "Unkown ransac inlier classification."

    # Filter poses based on inlier flags.
    inlier_dq_B_H = list(compress(aligned_dq_B_H, inlier_flags))
    inlier_dq_W_E = list(compress(aligned_dq_W_E, inlier_flags))
    assert len(inlier_dq_B_H) == len(inlier_dq_W_E)
    num_inliers = len(inlier_dq_B_H)

    # Reject sample if not enough inliers.
    if num_inliers < config.min_num_inliers:
      print("==> Not enough inliers ({})".format(num_inliers))
      continue

    if (config.ransac_model_refinement or dq_H_E_initial is None):
      try:
        # Refine hand-calibration using all inliers.
        (dq_H_E_refined,
         singular_values,
         bad_singular_values) = compute_hand_eye_calibration(
            inlier_dq_B_H, inlier_dq_W_E,
            config.hand_eye_calibration_scalar_part_equality_tolerance)
        dq_H_E_refined.normalize()
      except:
        print("\n\n Hand-eye calibration FAILED! "
              "algorithm_name: {} exception: \n\n".format(
                  config.algorithm_name, sys.exc_info()[0]))
        continue
    else:
      assert dq_H_E_initial is not None
      dq_H_E_refined = dq_H_E_initial

    # Rerun evaluation to determine the RMSE for the refined
    # hand-eye calibration.
    if config.ransac_evaluate_refined_model_on_inliers_only:
      (poses_B_H, poses_W_H) = get_aligned_poses(inlier_dq_B_H,
                                                 inlier_dq_W_E,
                                                 dq_H_E_refined)
    else:
      (poses_B_H, poses_W_H) = get_aligned_poses(aligned_dq_B_H,
                                                 aligned_dq_W_E,
                                                 dq_H_E_refined)

    (rmse_position_refined,
     rmse_orientation_refined,
     inlier_flags) = evaluate_alignment(poses_B_H, poses_W_H, config)

    if (rmse_position_refined < best_rmse_position and
            rmse_orientation_refined < best_rmse_orientation):
      best_estimated_dq_H_E = dq_H_E_refined
      best_rmse_position = rmse_position_refined
      best_rmse_orientation = rmse_orientation_refined
      best_inlier_idx_set = sample_indices
      best_num_inliers = num_inliers
      best_singular_values = singular_values
      best_singular_value_status = bad_singular_values

      print("Found a new best sample: {}\n"
            "\t\tNumber of inliers: {}\n"
            "\t\tRMSE position:     {:10.4f}\n"
            "\t\tRMSE orientation:  {:10.4f}\n"
            "\t\tdq_H_E_initial:    {}\n"
            "\t\tdq_H_E_refined:    {}".format(
                sample_indices, num_inliers, rmse_position_refined,
                rmse_orientation_refined, dq_H_E_initial, dq_H_E_refined))
    else:
      print("Rejected sample: {}\n"
            "\t\tNumber of inliers: {}\n"
            "\t\tRMSE position:     {:10.4f}\n"
            "\t\tRMSE orientation:  {:10.4f}".format(
                sample_indices, num_inliers, rmse_position_refined,
                rmse_orientation_refined))

    full_iterations += 1

    # Abort RANSAC early based on prior about the outlier probability.
    if (not config.enable_exhaustive_search and config.ransac_enable_early_abort):
      s = config.ransac_sample_size
      w = (1.0 - config.ransac_outlier_probability)  # Inlier probability
      w_pow_s = w ** s
      required_iterations = (math.log(1. - config.ransac_success_probability_threshold) /
                             math.log(1. - w_pow_s))
      if (full_iterations > required_iterations):
        print("Reached a {}% probability that RANSAC succeeded in finding a sample "
              "containing only inliers, aborting ...".format(
                  config.ransac_success_probability_threshold * 100.))
        break
  if not config.enable_exhaustive_search:
    print("Finished RANSAC.")
    print("RANSAC iterations: {}".format(full_iterations))
    print("RANSAC early rejected samples: {}".format(prerejected_samples))
  else:
    print("Finished exhaustive search!")

  if best_estimated_dq_H_E is None:
    print("!!! RANSAC couldn't find a solution !!!")
    end_time = timeit.default_timer()
    runtime = end_time - start_time
    return (False, None, (best_rmse_position, best_rmse_orientation),
            best_num_inliers, num_poses_after_filtering, runtime,
            best_singular_values, best_singular_value_status)

  # Visualize best alignment.
  if config.visualize:
    aligned_dq_W_E = align_paths_at_index(dq_W_E_vec)
    aligned_dq_B_H = align_paths_at_index(dq_B_H_vec)

    (poses_B_H, poses_W_H) = get_aligned_poses(aligned_dq_B_H,
                                               aligned_dq_W_E,
                                               best_estimated_dq_H_E)
    (rmse_position_all,
     rmse_orientation_all,
     inlier_flags) = evaluate_alignment(poses_B_H, poses_W_H, config, config.visualize)

    every_nth_element = config.visualize_plot_every_nth_pose
    plot_poses([poses_B_H[:: every_nth_element],
                poses_W_H[:: every_nth_element]],
               True, title="3D Poses After Alignment")

  pose_vec = best_estimated_dq_H_E.to_pose()
  print("Solution found with sample: {}\n"
        "\t\tNumber of inliers: {}\n"
        "\t\tRMSE position:     {:10.4f}\n"
        "\t\tRMSE orientation:  {:10.4f}\n"
        "\t\tdq_H_E_refined:    {}\n"
        "\t\tpose_H_E_refined:  {}\n"
        "\t\tTranslation norm:  {:10.4f}".format(
            best_inlier_idx_set, best_num_inliers, best_rmse_position,
            best_rmse_orientation, best_estimated_dq_H_E,
            pose_vec, np.linalg.norm(pose_vec[0:3])))

  if best_singular_values is not None:
    if best_singular_value_status:
      print("The singular values of this solution are bad. "
            "Either the smallest two are too big or the first 6 "
            "are too small! singular values: {}".format(best_singular_values))

  end_time = timeit.default_timer()
  runtime = end_time - start_time

  return (True, best_estimated_dq_H_E, (best_rmse_position, best_rmse_orientation),
          best_num_inliers, num_poses_after_filtering, runtime, best_singular_values, best_singular_value_status)

  Runs various RANSAC-based hand-eye calibration algorithms.
   - RANSAC using the position/orientation error to determine model inliers
   - RANSAC using the scalar part equality constraint to determine model inliers
   For evaluation purposes:
   - Exhaustive search versions of both algorithms above.

   Outputs a tuple containing:
    - success
    - dq_H_E
    - (best_rmse_position, best_rmse_orientation)
    - best_num_inliers
    - num_poses_after_filtering
    - runtime
    - singular_values
    - bad_singular_values
  
hand_eye_calibration/python/hand_eye_calibration/dual_quaternion_hand_eye_calibration.py