Skip to content

Commit c1eb8d1

Browse files
committed
ChArUco unit tests adapted to support legacy switch.
1 parent 45d7695 commit c1eb8d1

File tree

3 files changed

+160
-140
lines changed

3 files changed

+160
-140
lines changed

modules/aruco/include/opencv2/aruco/charuco.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ class CV_EXPORTS_W CharucoBoard : public Board {
116116
*/
117117
CV_WRAP float getMarkerLength() const { return _markerLength; }
118118

119+
/**
120+
*
121+
*/
122+
CV_WRAP bool getLegacyFlag() const { return _legacy; }
123+
119124
private:
120125
void _getNearestMarkerCorners();
121126

modules/aruco/test/test_aruco_utils.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ namespace opencv_test {
66
namespace {
77

88
static inline vector<Point2f> getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec,
9-
InputArray _tvec, float length, const float offset = 0.f)
9+
InputArray _tvec, float length, const Point2f offset = Point2f(0, 0))
1010
{
1111
vector<Point3f> axis;
12-
axis.push_back(Point3f(offset, offset, 0.f));
13-
axis.push_back(Point3f(length+offset, offset, 0.f));
14-
axis.push_back(Point3f(offset, length+offset, 0.f));
15-
axis.push_back(Point3f(offset, offset, length));
12+
axis.push_back(Point3f(offset.x, offset.y, 0.f));
13+
axis.push_back(Point3f(length+offset.x, offset.y, 0.f));
14+
axis.push_back(Point3f(offset.x, length+offset.y, 0.f));
15+
axis.push_back(Point3f(offset.x, offset.y, length));
1616
vector<Point2f> axis_to_img;
1717
projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img);
1818
return axis_to_img;

modules/aruco/test/test_charucodetection.cpp

Lines changed: 150 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,19 @@ namespace opencv_test { namespace {
4646
* @brief Get a synthetic image of Chessboard in perspective
4747
*/
4848
static Mat projectChessboard(int squaresX, int squaresY, float squareSize, Size imageSize,
49-
Mat cameraMatrix, Mat rvec, Mat tvec) {
49+
Mat cameraMatrix, Mat rvec, Mat tvec, bool legacy) {
5050

5151
Mat img(imageSize, CV_8UC1, Scalar::all(255));
5252
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
5353

5454
for(int y = 0; y < squaresY; y++) {
5555
float startY = float(y) * squareSize;
5656
for(int x = 0; x < squaresX; x++) {
57-
if(y % 2 != x % 2) continue;
57+
if(legacy) {
58+
if((y + ((squaresY + 1) % 2)) % 2 != x % 2) continue;
59+
} else {
60+
if(y % 2 != x % 2) continue;
61+
}
5862
float startX = float(x) * squareSize;
5963

6064
vector< Point3f > squareCorners;
@@ -101,7 +105,7 @@ static Mat projectCharucoBoard(Ptr<aruco::CharucoBoard> &board, Mat cameraMatrix
101105
// project chessboard
102106
Mat chessboard =
103107
projectChessboard(board->getChessboardSize().width, board->getChessboardSize().height,
104-
board->getSquareLength(), imageSize, cameraMatrix, rvec, tvec);
108+
board->getSquareLength(), imageSize, cameraMatrix, rvec, tvec, board->getLegacyFlag());
105109

106110
for(unsigned int i = 0; i < chessboard.total(); i++) {
107111
if(chessboard.ptr< unsigned char >()[i] == 0) {
@@ -130,84 +134,85 @@ CV_CharucoDetection::CV_CharucoDetection() {}
130134
void CV_CharucoDetection::run(int) {
131135

132136
int iter = 0;
133-
Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
134137
Size imgSize(500, 500);
135-
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
136-
Ptr<aruco::CharucoBoard> board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, dictionary);
137-
138+
Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
138139
cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 600;
139140
cameraMatrix.at< double >(0, 2) = imgSize.width / 2;
140141
cameraMatrix.at< double >(1, 2) = imgSize.height / 2;
141-
142142
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
143143

144-
// for different perspectives
145-
for(double distance = 0.2; distance <= 0.4; distance += 0.2) {
146-
for(int yaw = -55; yaw <= 50; yaw += 25) {
147-
for(int pitch = -55; pitch <= 50; pitch += 25) {
148-
149-
int markerBorder = iter % 2 + 1;
150-
iter++;
151-
152-
// create synthetic image
153-
Mat rvec, tvec;
154-
Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
155-
distance, imgSize, markerBorder, rvec, tvec);
156-
157-
// detect markers
158-
vector< vector< Point2f > > corners;
159-
vector< int > ids;
160-
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
161-
params->minDistanceToBorder = 3;
162-
params->markerBorderBits = markerBorder;
163-
aruco::detectMarkers(img, dictionary, corners, ids, params);
164-
165-
if(ids.size() == 0) {
166-
ts->printf(cvtest::TS::LOG, "Marker detection failed");
167-
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
168-
return;
169-
}
144+
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
145+
for(bool legacy : {true, false}) {
146+
Ptr<aruco::CharucoBoard> board = aruco::CharucoBoard::create(4, 4, 0.04f, 0.03f, dictionary, legacy);
147+
148+
// for different perspectives
149+
for(double distance = 0.2; distance <= 0.4; distance += 0.2) {
150+
for(int yaw = -55; yaw <= 50; yaw += 25) {
151+
for(int pitch = -55; pitch <= 50; pitch += 25) {
152+
153+
int markerBorder = iter % 2 + 1;
154+
iter++;
155+
156+
// create synthetic image
157+
Mat rvec, tvec;
158+
Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
159+
distance, imgSize, markerBorder, rvec, tvec);
160+
161+
// detect markers
162+
vector< vector< Point2f > > corners;
163+
vector< int > ids;
164+
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
165+
params->minDistanceToBorder = 3;
166+
params->markerBorderBits = markerBorder;
167+
aruco::detectMarkers(img, dictionary, corners, ids, params);
168+
169+
if(ids.size() == 0) {
170+
ts->printf(cvtest::TS::LOG, "Marker detection failed");
171+
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
172+
return;
173+
}
170174

171-
// interpolate charuco corners
172-
vector< Point2f > charucoCorners;
173-
vector< int > charucoIds;
175+
// interpolate charuco corners
176+
vector< Point2f > charucoCorners;
177+
vector< int > charucoIds;
174178

175-
if(iter % 2 == 0) {
176-
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
177-
charucoIds);
178-
} else {
179-
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
180-
charucoIds, cameraMatrix, distCoeffs);
181-
}
179+
if(iter % 2 == 0) {
180+
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
181+
charucoIds);
182+
} else {
183+
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
184+
charucoIds, cameraMatrix, distCoeffs);
185+
}
182186

183-
// check results
184-
vector< Point2f > projectedCharucoCorners;
187+
// check results
188+
vector< Point2f > projectedCharucoCorners;
185189

186-
// copy chessboardCorners
187-
vector<Point3f> copyChessboardCorners = board->chessboardCorners;
188-
// move copyChessboardCorners points
189-
for (size_t i = 0; i < copyChessboardCorners.size(); i++)
190-
copyChessboardCorners[i] -= board->rightBottomBorder / 2.f;
191-
projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs,
192-
projectedCharucoCorners);
190+
// copy chessboardCorners
191+
vector<Point3f> copyChessboardCorners = board->chessboardCorners;
192+
// move copyChessboardCorners points
193+
for (size_t i = 0; i < copyChessboardCorners.size(); i++)
194+
copyChessboardCorners[i] -= board->rightBottomBorder / 2.f;
195+
projectPoints(copyChessboardCorners, rvec, tvec, cameraMatrix, distCoeffs,
196+
projectedCharucoCorners);
193197

194-
for(unsigned int i = 0; i < charucoIds.size(); i++) {
198+
for(unsigned int i = 0; i < charucoIds.size(); i++) {
195199

196-
int currentId = charucoIds[i];
200+
int currentId = charucoIds[i];
197201

198-
if(currentId >= (int)board->chessboardCorners.size()) {
199-
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
200-
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
201-
return;
202-
}
202+
if(currentId >= (int)board->chessboardCorners.size()) {
203+
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
204+
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
205+
return;
206+
}
203207

204-
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
208+
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
205209

206210

207-
if(repError > 5.) {
208-
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
209-
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
210-
return;
211+
if(repError > 5.) {
212+
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
213+
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
214+
return;
215+
}
211216
}
212217
}
213218
}
@@ -235,91 +240,101 @@ CV_CharucoPoseEstimation::CV_CharucoPoseEstimation() {}
235240
void CV_CharucoPoseEstimation::run(int) {
236241

237242
int iter = 0;
243+
Size imgSize(750, 750);
238244
Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
239-
Size imgSize(500, 500);
240-
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
241-
Ptr<aruco::CharucoBoard> board = aruco::CharucoBoard::create(4, 4, 0.03f, 0.015f, dictionary);
242-
243245
cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650;
244246
cameraMatrix.at< double >(0, 2) = imgSize.width / 2;
245247
cameraMatrix.at< double >(1, 2) = imgSize.height / 2;
246-
247248
Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
248-
// for different perspectives
249-
for(double distance = 0.2; distance <= 0.3; distance += 0.1) {
250-
for(int yaw = -55; yaw <= 50; yaw += 25) {
251-
for(int pitch = -55; pitch <= 50; pitch += 25) {
252-
253-
int markerBorder = iter % 2 + 1;
254-
iter++;
255-
256-
// get synthetic image
257-
Mat rvec, tvec;
258-
Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
259-
distance, imgSize, markerBorder, rvec, tvec);
260-
261-
// detect markers
262-
vector< vector< Point2f > > corners;
263-
vector< int > ids;
264-
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
265-
params->minDistanceToBorder = 3;
266-
params->markerBorderBits = markerBorder;
267-
aruco::detectMarkers(img, dictionary, corners, ids, params);
268-
269-
ASSERT_EQ(ids.size(), board->ids.size());
270-
271-
// interpolate charuco corners
272-
vector< Point2f > charucoCorners;
273-
vector< int > charucoIds;
274-
275-
if(iter % 2 == 0) {
276-
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
277-
charucoIds);
278-
} else {
279-
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
280-
charucoIds, cameraMatrix, distCoeffs);
281-
}
282-
283-
if(charucoIds.size() == 0) continue;
284-
285-
// estimate charuco pose
286-
aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix,
287-
distCoeffs, rvec, tvec);
288249

250+
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
251+
for(bool legacy : {true, false}) {
252+
Ptr<aruco::CharucoBoard> board = aruco::CharucoBoard::create(4, 4, 0.04f, 0.03f, dictionary, legacy);
253+
254+
// for different perspectives
255+
for(double distance = 0.2; distance <= 0.3; distance += 0.1) {
256+
for(int yaw = -55; yaw <= 50; yaw += 25) {
257+
for(int pitch = -55; pitch <= 50; pitch += 25) {
258+
259+
int markerBorder = iter % 2 + 1;
260+
iter++;
261+
262+
// get synthetic image
263+
Mat rvec, tvec;
264+
Mat img = projectCharucoBoard(board, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
265+
distance, imgSize, markerBorder, rvec, tvec);
266+
267+
// detect markers
268+
vector< vector< Point2f > > corners;
269+
vector< int > ids;
270+
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
271+
params->minDistanceToBorder = 3;
272+
params->markerBorderBits = markerBorder;
273+
aruco::detectMarkers(img, dictionary, corners, ids, params);
274+
275+
ASSERT_EQ(ids.size(), board->ids.size());
276+
277+
// interpolate charuco corners
278+
vector< Point2f > charucoCorners;
279+
vector< int > charucoIds;
280+
281+
if(iter % 2 == 0) {
282+
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
283+
charucoIds);
284+
} else {
285+
aruco::interpolateCornersCharuco(corners, ids, img, board, charucoCorners,
286+
charucoIds, cameraMatrix, distCoeffs);
287+
}
289288

290-
// check axes
291-
const float offset = (board->getSquareLength() - board->getMarkerLength()) / 2.f;
292-
vector<Point2f> axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, board->getSquareLength(), offset);
293-
vector<Point2f> topLeft = getMarkerById(board->ids[0], corners, ids);
294-
ASSERT_NEAR(topLeft[0].x, axes[1].x, 3.f);
295-
ASSERT_NEAR(topLeft[0].y, axes[1].y, 3.f);
296-
vector<Point2f> bottomLeft = getMarkerById(board->ids[2], corners, ids);
297-
ASSERT_NEAR(bottomLeft[0].x, axes[2].x, 3.f);
298-
ASSERT_NEAR(bottomLeft[0].y, axes[2].y, 3.f);
289+
if(charucoIds.size() == 0) continue;
290+
291+
// estimate charuco pose
292+
aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix,
293+
distCoeffs, rvec, tvec);
294+
295+
// check axes
296+
const float aruco_offset = (board->getSquareLength() - board->getMarkerLength()) / 2.f;
297+
Point2f offset;
298+
vector<Point2f> topLeft, bottomLeft;
299+
if(legacy && (board->getChessboardSize().height % 2 == 0)) { // legacy and even row count
300+
offset = Point2f(aruco_offset + board->getSquareLength(), aruco_offset);
301+
topLeft = getMarkerById(board->ids[1], corners, ids);
302+
bottomLeft = getMarkerById(board->ids[2], corners, ids);
303+
} else {
304+
offset = Point2f(aruco_offset, aruco_offset);
305+
topLeft = getMarkerById(board->ids[0], corners, ids);
306+
bottomLeft = getMarkerById(board->ids[2], corners, ids);
307+
}
308+
vector<Point2f> axes = getAxis(cameraMatrix, distCoeffs, rvec, tvec, board->getSquareLength(), offset);
309+
ASSERT_NEAR(topLeft[0].x, axes[1].x, 3.f);
310+
ASSERT_NEAR(topLeft[0].y, axes[1].y, 3.f);
311+
ASSERT_NEAR(bottomLeft[0].x, axes[2].x, 3.f);
312+
ASSERT_NEAR(bottomLeft[0].y, axes[2].y, 3.f);
299313

300-
// check estimate result
301-
vector< Point2f > projectedCharucoCorners;
314+
// check estimate result
315+
vector< Point2f > projectedCharucoCorners;
302316

303-
projectPoints(board->chessboardCorners, rvec, tvec, cameraMatrix, distCoeffs,
304-
projectedCharucoCorners);
317+
projectPoints(board->chessboardCorners, rvec, tvec, cameraMatrix, distCoeffs,
318+
projectedCharucoCorners);
305319

306-
for(unsigned int i = 0; i < charucoIds.size(); i++) {
320+
for(unsigned int i = 0; i < charucoIds.size(); i++) {
307321

308-
int currentId = charucoIds[i];
322+
int currentId = charucoIds[i];
309323

310-
if(currentId >= (int)board->chessboardCorners.size()) {
311-
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
312-
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
313-
return;
314-
}
324+
if(currentId >= (int)board->chessboardCorners.size()) {
325+
ts->printf(cvtest::TS::LOG, "Invalid Charuco corner id");
326+
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
327+
return;
328+
}
315329

316-
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
330+
double repError = cv::norm(charucoCorners[i] - projectedCharucoCorners[currentId]); // TODO cvtest
317331

318332

319-
if(repError > 5.) {
320-
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
321-
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
322-
return;
333+
if(repError > 5.) {
334+
ts->printf(cvtest::TS::LOG, "Charuco corner reprojection error too high");
335+
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
336+
return;
337+
}
323338
}
324339
}
325340
}

0 commit comments

Comments
 (0)