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.math.MatBuilder;
008import edu.wpi.first.math.Matrix;
009import edu.wpi.first.math.Nat;
010import edu.wpi.first.math.numbers.N3;
011import java.util.Arrays;
012
013/** A detection of an AprilTag tag. */
014public class AprilTagDetection {
015  /**
016   * Gets the decoded tag's family name.
017   *
018   * @return Decoded family name
019   */
020  public String getFamily() {
021    return m_family;
022  }
023
024  /**
025   * Gets the decoded ID of the tag.
026   *
027   * @return Decoded ID
028   */
029  public int getId() {
030    return m_id;
031  }
032
033  /**
034   * Gets how many error bits were corrected. Note: accepting large numbers of corrected errors
035   * leads to greatly increased false positive rates. NOTE: As of this implementation, the detector
036   * cannot detect tags with a hamming distance greater than 2.
037   *
038   * @return Hamming distance (number of corrected error bits)
039   */
040  public int getHamming() {
041    return m_hamming;
042  }
043
044  /**
045   * Gets a measure of the quality of the binary decoding process: the average difference between
046   * the intensity of a data bit versus the decision threshold. Higher numbers roughly indicate
047   * better decodes. This is a reasonable measure of detection accuracy only for very small tags--
048   * not effective for larger tags (where we could have sampled anywhere within a bit cell and still
049   * gotten a good detection.)
050   *
051   * @return Decision margin
052   */
053  public float getDecisionMargin() {
054    return m_decisionMargin;
055  }
056
057  /**
058   * Gets the 3x3 homography matrix describing the projection from an "ideal" tag (with corners at
059   * (-1,1), (1,1), (1,-1), and (-1, -1)) to pixels in the image.
060   *
061   * @return Homography matrix data
062   */
063  @SuppressWarnings("PMD.MethodReturnsInternalArray")
064  public double[] getHomography() {
065    return m_homography;
066  }
067
068  /**
069   * Gets the 3x3 homography matrix describing the projection from an "ideal" tag (with corners at
070   * (-1,1), (1,1), (1,-1), and (-1, -1)) to pixels in the image.
071   *
072   * @return Homography matrix
073   */
074  public Matrix<N3, N3> getHomographyMatrix() {
075    return new MatBuilder<>(Nat.N3(), Nat.N3()).fill(m_homography);
076  }
077
078  /**
079   * Gets the center of the detection in image pixel coordinates.
080   *
081   * @return Center point X coordinate
082   */
083  public double getCenterX() {
084    return m_centerX;
085  }
086
087  /**
088   * Gets the center of the detection in image pixel coordinates.
089   *
090   * @return Center point Y coordinate
091   */
092  public double getCenterY() {
093    return m_centerY;
094  }
095
096  /**
097   * Gets a corner of the tag in image pixel coordinates. These always wrap counter-clock wise
098   * around the tag.
099   *
100   * @param ndx Corner index (range is 0-3, inclusive)
101   * @return Corner point X coordinate
102   */
103  public double getCornerX(int ndx) {
104    return m_corners[ndx * 2];
105  }
106
107  /**
108   * Gets a corner of the tag in image pixel coordinates. These always wrap counter-clock wise
109   * around the tag.
110   *
111   * @param ndx Corner index (range is 0-3, inclusive)
112   * @return Corner point Y coordinate
113   */
114  public double getCornerY(int ndx) {
115    return m_corners[ndx * 2 + 1];
116  }
117
118  /**
119   * Gets the corners of the tag in image pixel coordinates. These always wrap counter-clock wise
120   * around the tag.
121   *
122   * @return Corner point array (X and Y for each corner in order)
123   */
124  @SuppressWarnings("PMD.MethodReturnsInternalArray")
125  public double[] getCorners() {
126    return m_corners;
127  }
128
129  private final String m_family;
130  private final int m_id;
131  private final int m_hamming;
132  private final float m_decisionMargin;
133  private final double[] m_homography;
134  private final double m_centerX;
135  private final double m_centerY;
136  private final double[] m_corners;
137
138  /**
139   * Constructs a new detection result. Used from JNI.
140   *
141   * @param family family
142   * @param id id
143   * @param hamming hamming
144   * @param decisionMargin dm
145   * @param homography homography
146   * @param centerX centerX
147   * @param centerY centerY
148   * @param corners corners
149   */
150  @SuppressWarnings("PMD.ArrayIsStoredDirectly")
151  public AprilTagDetection(
152      String family,
153      int id,
154      int hamming,
155      float decisionMargin,
156      double[] homography,
157      double centerX,
158      double centerY,
159      double[] corners) {
160    m_family = family;
161    m_id = id;
162    m_hamming = hamming;
163    m_decisionMargin = decisionMargin;
164    m_homography = homography;
165    m_centerX = centerX;
166    m_centerY = centerY;
167    m_corners = corners;
168  }
169
170  @Override
171  public String toString() {
172    return "DetectionResult [centerX="
173        + m_centerX
174        + ", centerY="
175        + m_centerY
176        + ", corners="
177        + Arrays.toString(m_corners)
178        + ", decisionMargin="
179        + m_decisionMargin
180        + ", hamming="
181        + m_hamming
182        + ", homography="
183        + Arrays.toString(m_homography)
184        + ", family="
185        + m_family
186        + ", id="
187        + m_id
188        + "]";
189  }
190}