001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.apriltag;
006
007import edu.wpi.first.apriltag.jni.AprilTagJNI;
008import edu.wpi.first.math.geometry.Transform3d;
009
010/** Pose estimators for AprilTag tags. */
011public class AprilTagPoseEstimator {
012  /** Configuration for the pose estimator. */
013  @SuppressWarnings("MemberName")
014  public static class Config {
015    /**
016     * Creates a pose estimator configuration.
017     *
018     * @param tagSize tag size, in meters
019     * @param fx camera horizontal focal length, in pixels
020     * @param fy camera vertical focal length, in pixels
021     * @param cx camera horizontal focal center, in pixels
022     * @param cy camera vertical focal center, in pixels
023     */
024    public Config(double tagSize, double fx, double fy, double cx, double cy) {
025      this.tagSize = tagSize;
026      this.fx = fx;
027      this.fy = fy;
028      this.cx = cx;
029      this.cy = cy;
030    }
031
032    public double tagSize;
033    public double fx;
034    public double fy;
035    public double cx;
036    public double cy;
037
038    @Override
039    public int hashCode() {
040      return Double.hashCode(tagSize)
041          + Double.hashCode(fx)
042          + Double.hashCode(fy)
043          + Double.hashCode(cx)
044          + Double.hashCode(cy);
045    }
046
047    @Override
048    public boolean equals(Object obj) {
049      if (!(obj instanceof Config)) {
050        return false;
051      }
052
053      Config other = (Config) obj;
054      return tagSize == other.tagSize
055          && fx == other.fx
056          && fy == other.fy
057          && cx == other.cx
058          && cy == other.cy;
059    }
060  }
061
062  /**
063   * Creates estimator.
064   *
065   * @param config Configuration
066   */
067  public AprilTagPoseEstimator(Config config) {
068    m_config = new Config(config.tagSize, config.fx, config.fy, config.cx, config.cy);
069  }
070
071  /**
072   * Sets estimator configuration.
073   *
074   * @param config Configuration
075   */
076  public void setConfig(Config config) {
077    m_config.tagSize = config.tagSize;
078    m_config.fx = config.fx;
079    m_config.fy = config.fy;
080    m_config.cx = config.cx;
081    m_config.cy = config.cy;
082  }
083
084  /**
085   * Gets estimator configuration.
086   *
087   * @return Configuration
088   */
089  public Config getConfig() {
090    return new Config(m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
091  }
092
093  /**
094   * Estimates the pose of the tag using the homography method described in [1].
095   *
096   * @param detection Tag detection
097   * @return Pose estimate
098   */
099  public Transform3d estimateHomography(AprilTagDetection detection) {
100    return estimateHomography(detection.getHomography());
101  }
102
103  /**
104   * Estimates the pose of the tag using the homography method described in [1].
105   *
106   * @param homography Homography 3x3 matrix data
107   * @return Pose estimate
108   */
109  public Transform3d estimateHomography(double[] homography) {
110    return AprilTagJNI.estimatePoseHomography(
111        homography, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
112  }
113
114  /**
115   * Estimates the pose of the tag. This returns one or two possible poses for the tag, along with
116   * the object-space error of each.
117   *
118   * <p>This uses the homography method described in [1] for the initial estimate. Then Orthogonal
119   * Iteration [2] is used to refine this estimate. Then [3] is used to find a potential second
120   * local minima and Orthogonal Iteration is used to refine this second estimate.
121   *
122   * <p>[1]: E. Olson, “Apriltag: A robust and flexible visual fiducial system,” in 2011 IEEE
123   * International Conference on Robotics and Automation, May 2011, pp. 3400–3407.
124   *
125   * <p>[2]: Lu, G. D. Hager and E. Mjolsness, "Fast and globally convergent pose estimation from
126   * video images," in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 22, no.
127   * 6, pp. 610-622, June 2000. doi: 10.1109/34.862199
128   *
129   * <p>[3]: Schweighofer and A. Pinz, "Robust Pose Estimation from a Planar Target," in IEEE
130   * Transactions on Pattern Analysis and Machine Intelligence, vol. 28, no. 12, pp. 2024-2030, Dec.
131   * 2006. doi: 10.1109/TPAMI.2006.252
132   *
133   * @param detection Tag detection
134   * @param nIters Number of iterations
135   * @return Initial and (possibly) second pose estimates
136   */
137  public AprilTagPoseEstimate estimateOrthogonalIteration(AprilTagDetection detection, int nIters) {
138    return estimateOrthogonalIteration(detection.getHomography(), detection.getCorners(), nIters);
139  }
140
141  /**
142   * Estimates the pose of the tag. This returns one or two possible poses for the tag, along with
143   * the object-space error of each.
144   *
145   * @param homography Homography 3x3 matrix data
146   * @param corners Corner point array (X and Y for each corner in order)
147   * @param nIters Number of iterations
148   * @return Initial and (possibly) second pose estimates
149   */
150  public AprilTagPoseEstimate estimateOrthogonalIteration(
151      double[] homography, double[] corners, int nIters) {
152    return AprilTagJNI.estimatePoseOrthogonalIteration(
153        homography,
154        corners,
155        m_config.tagSize,
156        m_config.fx,
157        m_config.fy,
158        m_config.cx,
159        m_config.cy,
160        nIters);
161  }
162
163  /**
164   * Estimates tag pose. This method is an easier to use interface to
165   * EstimatePoseOrthogonalIteration(), running 50 iterations and returning the pose with the lower
166   * object-space error.
167   *
168   * @param detection Tag detection
169   * @return Pose estimate
170   */
171  public Transform3d estimate(AprilTagDetection detection) {
172    return estimate(detection.getHomography(), detection.getCorners());
173  }
174
175  /**
176   * Estimates tag pose. This method is an easier to use interface to
177   * EstimatePoseOrthogonalIteration(), running 50 iterations and returning the pose with the lower
178   * object-space error.
179   *
180   * @param homography Homography 3x3 matrix data
181   * @param corners Corner point array (X and Y for each corner in order)
182   * @return Pose estimate
183   */
184  public Transform3d estimate(double[] homography, double[] corners) {
185    return AprilTagJNI.estimatePose(
186        homography, corners, m_config.tagSize, m_config.fx, m_config.fy, m_config.cx, m_config.cy);
187  }
188
189  private final Config m_config;
190}