Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
arucotag Namespace Reference

Namespace containing functions related to ArUco operations on images. More...

Classes

struct  ArucoTag
 Represents a single ArUco tag. Stores all information about a specific tag detection. More...
 

Functions

cv::Point2f FindTagCenter (const ArucoTag &stTag)
 Given an ArucoTag struct find the center point of the corners.
 
void PreprocessFrame (const cv::Mat &cvInputFrame, cv::Mat &cvOutputFrame)
 Preprocess images for specifically for the Aruco Detect() method. This method creates a copy of the camera frame so as to not alter the original in case it's used after the call to this function completes.
 
std::vector< ArucoTagDetect (const cv::Mat &cvFrame, const cv::aruco::ArucoDetector &cvArucoDetector)
 Detect ArUco tags in the provided image.
 
void DrawDetections (cv::Mat &cvDetectionsFrame, const std::vector< ArucoTag > &vDetectedTags)
 Given a vector of ArucoTag structs draw each tag corner and ID onto the given image.
 
void EstimatePoseFromPointCloud (const cv::Mat &cvPointCloud, ArucoTag &stTag)
 Estimate the pose of a position with respect to the observer using a point cloud.
 
void EstimatePoseFromPNP (cv::Mat &cvCameraMatrix, cv::Mat &cvDistCoeffs, ArucoTag &stTag)
 Estimate the pose of a position with respect to the observer using an image.
 

Detailed Description

Namespace containing functions related to ArUco operations on images.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-07

Function Documentation

◆ FindTagCenter()

cv::Point2f arucotag::FindTagCenter ( const ArucoTag stTag)
inline

Given an ArucoTag struct find the center point of the corners.

Parameters
stTag- The tag to find the center of.
Returns
cv::Point2f - The resultant center point within the image.
Author
jspencerpittman (jspen.nosp@m.cerp.nosp@m.ittma.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-07
68 {
69 // Average of the four corners
70 cv::Point2f cvCenter(0, 0);
71
72 // Add each tag x, y to the center x, y.
73 cvCenter.x += stTag.CornerBL.x + stTag.CornerBR.x + stTag.CornerTL.x + stTag.CornerTR.x;
74 cvCenter.y += stTag.CornerBL.y + stTag.CornerBR.y + stTag.CornerTL.y + stTag.CornerTR.y;
75 // Divide by number of corners.
76 cvCenter.x /= 4;
77 cvCenter.y /= 4;
78
79 // Return a copy of the center point of the tag.
80 return cvCenter;
81 }
Here is the caller graph for this function:

◆ PreprocessFrame()

void arucotag::PreprocessFrame ( const cv::Mat cvInputFrame,
cv::Mat cvOutputFrame 
)
inline

Preprocess images for specifically for the Aruco Detect() method. This method creates a copy of the camera frame so as to not alter the original in case it's used after the call to this function completes.

Parameters
cvInputFrame- cv::Mat of the image to pre-process. This image should be in BGR format.
cvOutputFrame- cv::Mat to write the pre-processed image to.
Todo:
Determine optimal order for speed
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2023-10-10
97 {
98 // Check if the input frame is in BGR format.
99 if (cvInputFrame.channels() != 3)
100 {
101 // Submit logger message.
102 LOG_ERROR(logging::g_qSharedLogger, "PreprocessFrame() requires a BGR image.");
103 return;
104 }
105
106 // Grayscale.
107 cv::cvtColor(cvInputFrame, cvOutputFrame, cv::COLOR_BGR2GRAY);
108 cv::filter2D(cvOutputFrame, cvOutputFrame, -1, constants::ARUCO_SHARPEN_KERNEL_EXTRA);
109 // Reduce number of colors/gradients in the image.
110 imgops::ColorReduce(cvOutputFrame);
111 // Denoise (Looks like bilateral filter is req. for ArUco, check speed since docs say it's slow)
112 // cv::bilateralFilter(cvInputFrame, cvInputFrame, /*diameter =*/5, /*sigmaColor =*/0.2, /*sigmaSpace =*/3);
113 // imgops::CustomBilateralFilter(cvInputFrame, 3, 0.1, 3);
114 // Deblur? (Would require determining point spread function that caused the blur)
115
116 // Threshold mask (could use OTSU or TRIANGLE, just a const threshold for now)
117 // cv::threshold(cvInputFrame, cvInputFrame, constants::ARUCO_PIXEL_THRESHOLD, constants::ARUCO_PIXEL_THRESHOLD_MAX_VALUE, cv::THRESH_BINARY);
118
119 // Super-Resolution
120 // std::string szModelPath = "ESPCN_x3.pb";
121 // std::string szModelName = "espcn";
122 // dnn_superres::DnnSuperResImpl dnSuperResModel;
123 // dnSuperResModel.readModel(/*path*/);
124 // dnSuperResModel.setModel(/*path, scale*/);
125 // dnSuperResModel.upsample(cvInputFrame, cvOutputFrame);
126 }
int channels() const
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
COLOR_BGR2GRAY
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT)
void ColorReduce(cv::Mat &cvFrame, int nDiv=64)
Given an image and a divisor, divide the precision of the elements.
Definition ImageOperations.hpp:295
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Detect()

std::vector< ArucoTag > arucotag::Detect ( const cv::Mat cvFrame,
const cv::aruco::ArucoDetector cvArucoDetector 
)
inline

Detect ArUco tags in the provided image.

Parameters
cvFrame- The camera frame to run ArUco detection on. Should be BGR format or grayscale.
cvArucoDetector- The configured aruco detector to use for detection.
Returns
std::vector<ArucoTag> - The resultant vector containing the detected tags in the frame.
Note
The given cvFrame SHOULD BE IN BGR FORMAT.
Author
jspencerpittman (jspen.nosp@m.cerp.nosp@m.ittma.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om), clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-09-28

Create instance variables.

141 {
143 std::vector<int> vIDs;
144 std::vector<std::vector<cv::Point2f>> cvMarkerCorners, cvRejectedCandidates;
145
146 // Run Aruco detection algorithm.
147 cvArucoDetector.detectMarkers(cvFrame, cvMarkerCorners, vIDs, cvRejectedCandidates);
148
149 // Store all of the detected tags as ArucoTag.
150 std::vector<ArucoTag> vDetectedTags;
151 vDetectedTags.reserve(vIDs.size());
152
153 // Loop through each detection and build tag for it.
154 for (long unsigned int unIter = 0; unIter < vIDs.size(); unIter++)
155 {
156 // Create and initialize new tag.
157 ArucoTag stDetectedTag;
158 stDetectedTag.nID = vIDs[unIter];
159 // Copy corners.
160 stDetectedTag.CornerTL = cvMarkerCorners[unIter][0];
161 stDetectedTag.CornerTR = cvMarkerCorners[unIter][1];
162 stDetectedTag.CornerBR = cvMarkerCorners[unIter][2];
163 stDetectedTag.CornerBL = cvMarkerCorners[unIter][3];
164
165 // Add new tag to detected tags vector.
166 vDetectedTags.push_back(stDetectedTag);
167 }
168
169 // Return the detected tags.
170 return vDetectedTags;
171 }
void detectMarkers(InputArray image, OutputArrayOfArrays corners, OutputArray ids, OutputArrayOfArrays rejectedImgPoints=noArray()) const
Represents a single ArUco tag. Stores all information about a specific tag detection.
Definition ArucoDetection.hpp:44
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DrawDetections()

void arucotag::DrawDetections ( cv::Mat cvDetectionsFrame,
const std::vector< ArucoTag > &  vDetectedTags 
)
inline

Given a vector of ArucoTag structs draw each tag corner and ID onto the given image.

Parameters
cvDetectionsFrame- The frame to draw overlay onto.
vDetectedTags- The vector of ArucoTag struct used to draw tag corners and IDs onto image.
Note
Image must be a 1 or 3 channel image and image must match dimensions of image when used for detection of the given tags.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-19
186 {
187 // Create instance variables.
188 std::vector<int> vIDs;
189 std::vector<std::vector<cv::Point2f>> cvMarkers;
190
191 // Loop through each of the given AR tags and repackage them so that the draw function can read them.
192 for (long unsigned int nIter = 0; nIter < vDetectedTags.size(); ++nIter)
193 {
194 // Append tag ID.
195 vIDs.emplace_back(vDetectedTags[nIter].nID);
196
197 // Assemble vector of marker corners.
198 std::vector<cv::Point2f> cvMarkerCorners;
199 cvMarkerCorners.emplace_back(vDetectedTags[nIter].CornerTL);
200 cvMarkerCorners.emplace_back(vDetectedTags[nIter].CornerTR);
201 cvMarkerCorners.emplace_back(vDetectedTags[nIter].CornerBR);
202 cvMarkerCorners.emplace_back(vDetectedTags[nIter].CornerBL);
203 // Append vector of marker corners.
204 cvMarkers.emplace_back(cvMarkerCorners);
205 }
206
207 // Check if the given frame is a 1 or 3 channel image. (not BGRA)
208 if (!cvDetectionsFrame.empty() && (cvDetectionsFrame.channels() == 1 || cvDetectionsFrame.channels() == 3))
209 {
210 // Draw markers onto normal given image.
211 cv::aruco::drawDetectedMarkers(cvDetectionsFrame, cvMarkers, vIDs, cv::Scalar(0, 0, 0));
212 }
213 else
214 {
215 // Submit logger message.
216 LOG_ERROR(logging::g_qSharedLogger,
217 "ArucoDetect: Unable to draw markers on image because it is empty or because it has {} channels. (Should be 1 or 3)",
218 cvDetectionsFrame.channels());
219 }
220 }
bool empty() const
void drawDetectedMarkers(InputOutputArray image, InputArrayOfArrays corners, InputArray ids=noArray(), Scalar borderColor=Scalar(0, 255, 0))
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EstimatePoseFromPointCloud()

void arucotag::EstimatePoseFromPointCloud ( const cv::Mat cvPointCloud,
ArucoTag stTag 
)
inline

Estimate the pose of a position with respect to the observer using a point cloud.

Parameters
cvPointCloud- A point cloud of x,y,z coordinates.
stTag- The tag we are estimating the pose of and then storing the distance and angle calculations in.
Note
The angle only takes into account how far forward/backward and left/right the tag is with respect to the rover. This meaning I ignore the up/down position of the tag when calculating the angle.
Author
jspencerpittman (jspen.nosp@m.cerp.nosp@m.ittma.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-05
235 {
236 // Confirm correct coordinate system.
237 if (constants::ZED_COORD_SYSTEM != sl::COORDINATE_SYSTEM::LEFT_HANDED_Y_UP)
238 {
239 // Submit logger message.
240 LOG_CRITICAL(logging::g_qSharedLogger, "ArucoDetection: Calculations won't work for anything other than ZED coordinate system == LEFT_HANDED_Y_UP");
241 }
242
243 // Find the center point of the given tag.
244 cv::Point2f cvCenter = FindTagCenter(stTag);
245
246 // Ensure the detected center is inside the domain of the point cloud.
247 if (cvCenter.y > cvPointCloud.rows || cvCenter.x > cvPointCloud.cols || cvCenter.y < 0 || cvCenter.x < 0)
248 {
249 LOG_ERROR(logging::g_qSharedLogger,
250 "Detected tag center ({}, {}) out of point cloud's domain ({},{})",
251 cvCenter.y,
252 cvCenter.x,
253 cvPointCloud.rows,
254 cvPointCloud.cols);
255 return;
256 }
257
258 // Get tag center point location relative to the camera. Point cloud location stores float x, y, z, BGRA.
259 cv::Vec4f cvCoordinate = cvPointCloud.at<cv::Vec4f>(cvCenter.y, cvCenter.x);
260 float fForward = cvCoordinate[2]; // Z
261 float fRight = cvCoordinate[0]; // X
262 float fUp = cvCoordinate[1]; // Y
263
264 // Calculate euclidean distance from ZED camera left eye to the point of interest
265 stTag.dStraightLineDistance = sqrt(pow(fForward, 2) + pow(fRight, 2) + pow(fUp, 2));
266
267 // Calculate the angle on plane horizontal to the viewpoint
268 stTag.dYawAngle = atan2(fRight, fForward);
269 }
_Tp & at(int i0=0)
void sqrt(InputArray src, OutputArray dst)
void pow(InputArray src, double power, OutputArray dst)
__device__ __forceinline__ float1 atan2(const uchar1 &a, const uchar1 &b)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EstimatePoseFromPNP()

void arucotag::EstimatePoseFromPNP ( cv::Mat cvCameraMatrix,
cv::Mat cvDistCoeffs,
ArucoTag stTag 
)
inline

Estimate the pose of a position with respect to the observer using an image.

Parameters
cvCameraMatrix- Matrix of camera's parameters including focal length and optical center.
cvDistCoeffs- Matrix of the camera's distortion coefficients.
stTag- The tag we are estimating the pose of and then storing the distance and angle calculations in.
Author
jspencerpittman (jspen.nosp@m.cerp.nosp@m.ittma.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2023-10-06
282 {
283 // rotVec is how the tag is orientated with respect to the camera. It's 3 numbers defining an axis of rotation around which we rotate the angle which is the
284 // euclidean distance of the vector. transVec is the XYZ translation of the tag from the camera if you image the convergence of light as a pinhole sitting at
285 // (0,0,0) in space.
286 cv::Vec3d cvRotVec, cvTransVec;
287
288 // Set expected object coordinate system shape.
289 cv::Mat cvObjPoints(4, 1, CV_32FC3);
290 cvObjPoints.at<cv::Vec3f>(0) = cv::Vec3f{0, 0, 0}; // Top-left corner.
291 cvObjPoints.at<cv::Vec3f>(1) = cv::Vec3f{constants::ARUCO_TAG_SIDE_LENGTH, 0, 0}; // Bottom-left corner.
292 cvObjPoints.at<cv::Vec3f>(2) = cv::Vec3f{0, constants::ARUCO_TAG_SIDE_LENGTH, 0}; // Top-right corner.
293 cvObjPoints.at<cv::Vec3f>(3) = cv::Vec3f{constants::ARUCO_TAG_SIDE_LENGTH, constants::ARUCO_TAG_SIDE_LENGTH, 0}; // Bottom-right corner.
294
295 // Repackage tag image points into a mat.
296 cv::Mat cvImgPoints(4, 1, CV_32FC3);
297 cvImgPoints.at<cv::Vec3f>(0) = cv::Vec3f{stTag.CornerTL.x, stTag.CornerTL.y, 0}; // Top-left corner.
298 cvImgPoints.at<cv::Vec3f>(1) = cv::Vec3f{stTag.CornerBL.x, stTag.CornerBL.y, 0}; // Bottom-left corner.
299 cvImgPoints.at<cv::Vec3f>(2) = cv::Vec3f{stTag.CornerTR.x, stTag.CornerTR.y, 0}; // Top-right corner.
300 cvImgPoints.at<cv::Vec3f>(3) = cv::Vec3f{stTag.CornerBR.x, stTag.CornerBR.y, 0}; // Bottom-right corner.
301
302 // Use solve perspective n' point algorithm to estimate pose of the tag.
303 cv::solvePnP(cvObjPoints, cvImgPoints, cvCameraMatrix, cvDistCoeffs, cvRotVec, cvTransVec);
304
305 // Grab (x,y,z) coordinates from where the tag was detected
306 double dForward = cvTransVec[2];
307 double dRight = cvTransVec[0];
308 double dUp = cvTransVec[1];
309
310 // Calculate euclidean distance from ZED camera left eye to the point of interest
311 stTag.dStraightLineDistance = std::sqrt(std::pow(dForward, 2) + std::pow(dRight, 2) + std::pow(dUp, 2));
312
313 // Calculate the angle on plane horizontal to the viewpoint
314 stTag.dYawAngle = std::atan2(dRight, dForward);
315 }
bool solvePnP(InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false, int flags=SOLVEPNP_ITERATIVE)
Here is the call graph for this function: