charuco.cpp
Go to the documentation of this file.
1 /*
2 By downloading, copying, installing or using the software you agree to this
3 license. If you do not agree to this license, do not download, install,
4 copy or use the software.
5 
6  License Agreement
7  For Open Source Computer Vision Library
8  (3-clause BSD License)
9 
10 Copyright (C) 2013, OpenCV Foundation, all rights reserved.
11 Third party copyrights are property of their respective owners.
12 
13 Redistribution and use in source and binary forms, with or without modification,
14 are permitted provided that the following conditions are met:
15 
16  * Redistributions of source code must retain the above copyright notice,
17  this list of conditions and the following disclaimer.
18 
19  * Redistributions in binary form must reproduce the above copyright notice,
20  this list of conditions and the following disclaimer in the documentation
21  and/or other materials provided with the distribution.
22 
23  * Neither the names of the copyright holders nor the names of the contributors
24  may be used to endorse or promote products derived from this software
25  without specific prior written permission.
26 
27 This software is provided by the copyright holders and contributors "as is" and
28 any express or implied warranties, including, but not limited to, the implied
29 warranties of merchantability and fitness for a particular purpose are
30 disclaimed. In no event shall copyright holders or contributors be liable for
31 any direct, indirect, incidental, special, exemplary, or consequential damages
32 (including, but not limited to, procurement of substitute goods or services;
33 loss of use, data, or profits; or business interruption) however caused
34 and on any theory of liability, whether in contract, strict liability,
35 or tort (including negligence or otherwise) arising in any way out of
36 the use of this software, even if advised of the possibility of such damage.
37 */
38 
39 #include "precomp.hpp"
41 #include <opencv2/core.hpp>
42 #include <opencv2/imgproc.hpp>
43 
44 
45 namespace cv {
46 namespace aruco {
47 
48 using namespace std;
49 
50 
51 
54 void CharucoBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) {
55 
56  /* NOTE: OpenCV v<3.3 does not have .empty() as a Size member */
57  CV_Assert(outSize.height > 0 && outSize.width > 0);
58  CV_Assert(marginSize >= 0);
59 
60  _img.create(outSize, CV_8UC1);
61  _img.setTo(255);
62  Mat out = _img.getMat();
63  Mat noMarginsImg =
64  out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize);
65 
66  double totalLengthX, totalLengthY;
67  totalLengthX = _squareLength * _squaresX;
68  totalLengthY = _squareLength * _squaresY;
69 
70  // proportional transformation
71  double xReduction = totalLengthX / double(noMarginsImg.cols);
72  double yReduction = totalLengthY / double(noMarginsImg.rows);
73 
74  // determine the zone where the chessboard is placed
75  Mat chessboardZoneImg;
76  if(xReduction > yReduction) {
77  int nRows = int(totalLengthY / xReduction);
78  int rowsMargins = (noMarginsImg.rows - nRows) / 2;
79  chessboardZoneImg = noMarginsImg.rowRange(rowsMargins, noMarginsImg.rows - rowsMargins);
80  } else {
81  int nCols = int(totalLengthX / yReduction);
82  int colsMargins = (noMarginsImg.cols - nCols) / 2;
83  chessboardZoneImg = noMarginsImg.colRange(colsMargins, noMarginsImg.cols - colsMargins);
84  }
85 
86  // determine the margins to draw only the markers
87  // take the minimum just to be sure
88  double squareSizePixels = min(double(chessboardZoneImg.cols) / double(_squaresX),
89  double(chessboardZoneImg.rows) / double(_squaresY));
90 
91  double diffSquareMarkerLength = (_squareLength - _markerLength) / 2;
92  int diffSquareMarkerLengthPixels =
93  int(diffSquareMarkerLength * squareSizePixels / _squareLength);
94 
95  // draw markers
96  Mat markersImg;
97  aruco::_drawPlanarBoardImpl(this, chessboardZoneImg.size(), markersImg,
98  diffSquareMarkerLengthPixels, borderBits);
99 
100  markersImg.copyTo(chessboardZoneImg);
101 
102  // now draw black squares
103  for(int y = 0; y < _squaresY; y++) {
104  for(int x = 0; x < _squaresX; x++) {
105 
106  if(y % 2 != x % 2) continue; // white corner, dont do anything
107 
108  double startX, startY;
109  startX = squareSizePixels * double(x);
110  startY = double(chessboardZoneImg.rows) - squareSizePixels * double(y + 1);
111 
112  Mat squareZone = chessboardZoneImg.rowRange(int(startY), int(startY + squareSizePixels))
113  .colRange(int(startX), int(startX + squareSizePixels));
114 
115  squareZone.setTo(0);
116  }
117  }
118 }
119 
120 
121 
124 Ptr<CharucoBoard> CharucoBoard::create(int squaresX, int squaresY, float squareLength,
125  float markerLength, const Ptr<Dictionary> &dictionary) {
126 
127  CV_Assert(squaresX > 1 && squaresY > 1 && markerLength > 0 && squareLength > markerLength);
128  Ptr<CharucoBoard> res = makePtr<CharucoBoard>();
129 
130  res->_squaresX = squaresX;
131  res->_squaresY = squaresY;
132  res->_squareLength = squareLength;
133  res->_markerLength = markerLength;
134  res->dictionary = dictionary;
135 
136  float diffSquareMarkerLength = (squareLength - markerLength) / 2;
137 
138  // calculate Board objPoints
139  for(int y = squaresY - 1; y >= 0; y--) {
140  for(int x = 0; x < squaresX; x++) {
141 
142  if(y % 2 == x % 2) continue; // black corner, no marker here
143 
144  vector< Point3f > corners;
145  corners.resize(4);
146  corners[0] = Point3f(x * squareLength + diffSquareMarkerLength,
147  y * squareLength + diffSquareMarkerLength + markerLength, 0);
148  corners[1] = corners[0] + Point3f(markerLength, 0, 0);
149  corners[2] = corners[0] + Point3f(markerLength, -markerLength, 0);
150  corners[3] = corners[0] + Point3f(0, -markerLength, 0);
151  res->objPoints.push_back(corners);
152  // first ids in dictionary
153  int nextId = (int)res->ids.size();
154  res->ids.push_back(nextId);
155  }
156  }
157 
158  // now fill chessboardCorners
159  for(int y = 0; y < squaresY - 1; y++) {
160  for(int x = 0; x < squaresX - 1; x++) {
161  Point3f corner;
162  corner.x = (x + 1) * squareLength;
163  corner.y = (y + 1) * squareLength;
164  corner.z = 0;
165  res->chessboardCorners.push_back(corner);
166  }
167  }
168 
169  res->_getNearestMarkerCorners();
170 
171  return res;
172 }
173 
174 
175 
180 
181  nearestMarkerIdx.resize(chessboardCorners.size());
182  nearestMarkerCorners.resize(chessboardCorners.size());
183 
184  unsigned int nMarkers = (unsigned int)ids.size();
185  unsigned int nCharucoCorners = (unsigned int)chessboardCorners.size();
186  for(unsigned int i = 0; i < nCharucoCorners; i++) {
187  double minDist = -1; // distance of closest markers
188  Point3f charucoCorner = chessboardCorners[i];
189  for(unsigned int j = 0; j < nMarkers; j++) {
190  // calculate distance from marker center to charuco corner
191  Point3f center = Point3f(0, 0, 0);
192  for(unsigned int k = 0; k < 4; k++)
193  center += objPoints[j][k];
194  center /= 4.;
195  double sqDistance;
196  Point3f distVector = charucoCorner - center;
197  sqDistance = distVector.x * distVector.x + distVector.y * distVector.y;
198  if(j == 0 || fabs(sqDistance - minDist) < cv::pow(0.01 * _squareLength, 2)) {
199  // if same minimum distance (or first iteration), add to nearestMarkerIdx vector
200  nearestMarkerIdx[i].push_back(j);
201  minDist = sqDistance;
202  } else if(sqDistance < minDist) {
203  // if finding a closest marker to the charuco corner
204  nearestMarkerIdx[i].clear(); // remove any previous added marker
205  nearestMarkerIdx[i].push_back(j); // add the new closest marker index
206  minDist = sqDistance;
207  }
208  }
209 
210  // for each of the closest markers, search the marker corner index closer
211  // to the charuco corner
212  for(unsigned int j = 0; j < nearestMarkerIdx[i].size(); j++) {
213  nearestMarkerCorners[i].resize(nearestMarkerIdx[i].size());
214  double minDistCorner = -1;
215  for(unsigned int k = 0; k < 4; k++) {
216  double sqDistance;
217  Point3f distVector = charucoCorner - objPoints[nearestMarkerIdx[i][j]][k];
218  sqDistance = distVector.x * distVector.x + distVector.y * distVector.y;
219  if(k == 0 || sqDistance < minDistCorner) {
220  // if this corner is closer to the charuco corner, assing its index
221  // to nearestMarkerCorners
222  minDistCorner = sqDistance;
223  nearestMarkerCorners[i][j] = k;
224  }
225  }
226  }
227  }
228 }
229 
230 
234 static int _filterCornersWithoutMinMarkers(const Ptr<CharucoBoard> &_board,
235  InputArray _allCharucoCorners,
236  InputArray _allCharucoIds,
237  InputArray _allArucoIds, int minMarkers,
238  OutputArray _filteredCharucoCorners,
239  OutputArray _filteredCharucoIds) {
240 
241  CV_Assert(minMarkers >= 0 && minMarkers <= 2);
242 
243  vector< Point2f > filteredCharucoCorners;
244  vector< int > filteredCharucoIds;
245  // for each charuco corner
246  for(unsigned int i = 0; i < _allCharucoIds.getMat().total(); i++) {
247  int currentCharucoId = _allCharucoIds.getMat().at< int >(i);
248  int totalMarkers = 0; // nomber of closest marker detected
249  // look for closest markers
250  for(unsigned int m = 0; m < _board->nearestMarkerIdx[currentCharucoId].size(); m++) {
251  int markerId = _board->ids[_board->nearestMarkerIdx[currentCharucoId][m]];
252  bool found = false;
253  for(unsigned int k = 0; k < _allArucoIds.getMat().total(); k++) {
254  if(_allArucoIds.getMat().at< int >(k) == markerId) {
255  found = true;
256  break;
257  }
258  }
259  if(found) totalMarkers++;
260  }
261  // if enough markers detected, add the charuco corner to the final list
262  if(totalMarkers >= minMarkers) {
263  filteredCharucoIds.push_back(currentCharucoId);
264  filteredCharucoCorners.push_back(_allCharucoCorners.getMat().at< Point2f >(i));
265  }
266  }
267 
268  // parse output
269  Mat(filteredCharucoCorners).copyTo(_filteredCharucoCorners);
270  Mat(filteredCharucoIds).copyTo(_filteredCharucoIds);
271  return (int)_filteredCharucoIds.total();
272 }
273 
274 
279 class CharucoSubpixelParallel : public ParallelLoopBody {
280  public:
281  CharucoSubpixelParallel(const Mat *_grey, vector< Point2f > *_filteredChessboardImgPoints,
282  vector< Size > *_filteredWinSizes, const Ptr<DetectorParameters> &_params)
283  : grey(_grey), filteredChessboardImgPoints(_filteredChessboardImgPoints),
284  filteredWinSizes(_filteredWinSizes), params(_params) {}
285 
286  void operator()(const Range &range) const CV_OVERRIDE {
287  const int begin = range.start;
288  const int end = range.end;
289 
290  for(int i = begin; i < end; i++) {
291  vector< Point2f > in;
292  in.push_back((*filteredChessboardImgPoints)[i]);
293  Size winSize = (*filteredWinSizes)[i];
294  if(winSize.height == -1 || winSize.width == -1)
295  winSize = Size(params->cornerRefinementWinSize, params->cornerRefinementWinSize);
296 
297  cornerSubPix(*grey, in, winSize, Size(),
298  TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS,
299  params->cornerRefinementMaxIterations,
300  params->cornerRefinementMinAccuracy));
301 
302  (*filteredChessboardImgPoints)[i] = in[0];
303  }
304  }
305 
306  private:
307  CharucoSubpixelParallel &operator=(const CharucoSubpixelParallel &); // to quiet MSVC
308 
309  const Mat *grey;
310  vector< Point2f > *filteredChessboardImgPoints;
311  vector< Size > *filteredWinSizes;
312  const Ptr<DetectorParameters> &params;
313 };
314 
315 
316 
317 
322 static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray _image,
323  OutputArray _selectedCorners,
324  OutputArray _selectedIds,
325  const vector< Size > &winSizes) {
326 
327  const int minDistToBorder = 2; // minimum distance of the corner to the image border
328  // remaining corners, ids and window refinement sizes after removing corners outside the image
329  vector< Point2f > filteredChessboardImgPoints;
330  vector< Size > filteredWinSizes;
331  vector< int > filteredIds;
332 
333  // filter corners outside the image
334  Rect innerRect(minDistToBorder, minDistToBorder, _image.getMat().cols - 2 * minDistToBorder,
335  _image.getMat().rows - 2 * minDistToBorder);
336  for(unsigned int i = 0; i < _allCorners.getMat().total(); i++) {
337  if(innerRect.contains(_allCorners.getMat().at< Point2f >(i))) {
338  filteredChessboardImgPoints.push_back(_allCorners.getMat().at< Point2f >(i));
339  filteredIds.push_back(i);
340  filteredWinSizes.push_back(winSizes[i]);
341  }
342  }
343 
344  // if none valid, return 0
345  if(filteredChessboardImgPoints.size() == 0) return 0;
346 
347  // corner refinement, first convert input image to grey
348  Mat grey;
349  if(_image.type() == CV_8UC3)
350  cvtColor(_image, grey, COLOR_BGR2GRAY);
351  else
352  _image.copyTo(grey);
353 
354  const Ptr<DetectorParameters> params = DetectorParameters::create(); // use default params for corner refinement
355 
357  // for(unsigned int i=0; i<filteredChessboardImgPoints.size(); i++) {
358  // vector<Point2f> in;
359  // in.push_back(filteredChessboardImgPoints[i]);
360  // Size winSize = filteredWinSizes[i];
361  // if(winSize.height == -1 || winSize.width == -1)
362  // winSize = Size(params.cornerRefinementWinSize, params.cornerRefinementWinSize);
363  // cornerSubPix(grey, in, winSize, Size(),
364  // TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS,
365  // params->cornerRefinementMaxIterations,
366  // params->cornerRefinementMinAccuracy));
367  // filteredChessboardImgPoints[i] = in[0];
368  //}
369 
370  // this is the parallel call for the previous commented loop (result is equivalent)
371  parallel_for_(
372  Range(0, (int)filteredChessboardImgPoints.size()),
373  CharucoSubpixelParallel(&grey, &filteredChessboardImgPoints, &filteredWinSizes, params));
374 
375  // parse output
376  Mat(filteredChessboardImgPoints).copyTo(_selectedCorners);
377  Mat(filteredIds).copyTo(_selectedIds);
378  return (int)filteredChessboardImgPoints.size();
379 }
380 
381 
386 static void _getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, InputArray markerIds,
387  InputArray charucoCorners, const Ptr<CharucoBoard> &board,
388  vector< Size > &sizes) {
389 
390  unsigned int nCharucoCorners = (unsigned int)charucoCorners.getMat().total();
391  sizes.resize(nCharucoCorners, Size(-1, -1));
392 
393  for(unsigned int i = 0; i < nCharucoCorners; i++) {
394  if(charucoCorners.getMat().at< Point2f >(i) == Point2f(-1, -1)) continue;
395  if(board->nearestMarkerIdx[i].size() == 0) continue;
396 
397  double minDist = -1;
398  int counter = 0;
399 
400  // calculate the distance to each of the closest corner of each closest marker
401  for(unsigned int j = 0; j < board->nearestMarkerIdx[i].size(); j++) {
402  // find marker
403  int markerId = board->ids[board->nearestMarkerIdx[i][j]];
404  int markerIdx = -1;
405  for(unsigned int k = 0; k < markerIds.getMat().total(); k++) {
406  if(markerIds.getMat().at< int >(k) == markerId) {
407  markerIdx = k;
408  break;
409  }
410  }
411  if(markerIdx == -1) continue;
412  Point2f markerCorner =
413  markerCorners.getMat(markerIdx).at< Point2f >(board->nearestMarkerCorners[i][j]);
414  Point2f charucoCorner = charucoCorners.getMat().at< Point2f >(i);
415  double dist = norm(markerCorner - charucoCorner);
416  if(minDist == -1) minDist = dist; // if first distance, just assign it
417  minDist = min(dist, minDist);
418  counter++;
419  }
420 
421  // if this is the first closest marker, dont do anything
422  if(counter == 0)
423  continue;
424  else {
425  // else, calculate the maximum window size
426  int winSizeInt = int(minDist - 2); // remove 2 pixels for safety
427  if(winSizeInt < 1) winSizeInt = 1; // minimum size is 1
428  if(winSizeInt > 10) winSizeInt = 10; // maximum size is 10
429  sizes[i] = Size(winSizeInt, winSizeInt);
430  }
431  }
432 }
433 
434 
435 
439 static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorners,
440  InputArray _markerIds, InputArray _image,
441  const Ptr<CharucoBoard> &_board,
442  InputArray _cameraMatrix, InputArray _distCoeffs,
443  OutputArray _charucoCorners,
444  OutputArray _charucoIds) {
445 
446  CV_Assert(_image.getMat().channels() == 1 || _image.getMat().channels() == 3);
447  CV_Assert(_markerCorners.total() == _markerIds.getMat().total() &&
448  _markerIds.getMat().total() > 0);
449 
450  // approximated pose estimation using marker corners
451  Mat approximatedRvec, approximatedTvec;
452  int detectedBoardMarkers;
453  Ptr<Board> _b = _board.staticCast<Board>();
454  detectedBoardMarkers =
455  aruco::estimatePoseBoard(_markerCorners, _markerIds, _b,
456  _cameraMatrix, _distCoeffs, approximatedRvec, approximatedTvec);
457 
458  if(detectedBoardMarkers == 0) return 0;
459 
460  // project chessboard corners
461  vector< Point2f > allChessboardImgPoints;
462 
463  projectPoints(_board->chessboardCorners, approximatedRvec, approximatedTvec, _cameraMatrix,
464  _distCoeffs, allChessboardImgPoints);
465 
466 
467  // calculate maximum window sizes for subpixel refinement. The size is limited by the distance
468  // to the closes marker corner to avoid erroneous displacements to marker corners
469  vector< Size > subPixWinSizes;
470  _getMaximumSubPixWindowSizes(_markerCorners, _markerIds, allChessboardImgPoints, _board,
471  subPixWinSizes);
472 
473  // filter corners outside the image and subpixel-refine charuco corners
474  return _selectAndRefineChessboardCorners(allChessboardImgPoints, _image, _charucoCorners,
475  _charucoIds, subPixWinSizes);
476 }
477 
478 
479 
483 static int _interpolateCornersCharucoLocalHom(InputArrayOfArrays _markerCorners,
484  InputArray _markerIds, InputArray _image,
485  const Ptr<CharucoBoard> &_board,
486  OutputArray _charucoCorners,
487  OutputArray _charucoIds) {
488 
489  CV_Assert(_image.getMat().channels() == 1 || _image.getMat().channels() == 3);
490  CV_Assert(_markerCorners.total() == _markerIds.getMat().total() &&
491  _markerIds.getMat().total() > 0);
492 
493  unsigned int nMarkers = (unsigned int)_markerIds.getMat().total();
494 
495  // calculate local homographies for each marker
496  vector< Mat > transformations;
497  transformations.resize(nMarkers);
498  for(unsigned int i = 0; i < nMarkers; i++) {
499  vector< Point2f > markerObjPoints2D;
500  int markerId = _markerIds.getMat().at< int >(i);
501  vector< int >::const_iterator it = find(_board->ids.begin(), _board->ids.end(), markerId);
502  if(it == _board->ids.end()) continue;
503  int boardIdx = (int)std::distance<std::vector<int>::const_iterator>(_board->ids.begin(), it);
504  markerObjPoints2D.resize(4);
505  for(unsigned int j = 0; j < 4; j++)
506  markerObjPoints2D[j] =
507  Point2f(_board->objPoints[boardIdx][j].x, _board->objPoints[boardIdx][j].y);
508 
509  transformations[i] = getPerspectiveTransform(markerObjPoints2D, _markerCorners.getMat(i));
510  }
511 
512  unsigned int nCharucoCorners = (unsigned int)_board->chessboardCorners.size();
513  vector< Point2f > allChessboardImgPoints(nCharucoCorners, Point2f(-1, -1));
514 
515  // for each charuco corner, calculate its interpolation position based on the closest markers
516  // homographies
517  for(unsigned int i = 0; i < nCharucoCorners; i++) {
518  Point2f objPoint2D = Point2f(_board->chessboardCorners[i].x, _board->chessboardCorners[i].y);
519 
520  vector< Point2f > interpolatedPositions;
521  for(unsigned int j = 0; j < _board->nearestMarkerIdx[i].size(); j++) {
522  int markerId = _board->ids[_board->nearestMarkerIdx[i][j]];
523  int markerIdx = -1;
524  for(unsigned int k = 0; k < _markerIds.getMat().total(); k++) {
525  if(_markerIds.getMat().at< int >(k) == markerId) {
526  markerIdx = k;
527  break;
528  }
529  }
530  if(markerIdx != -1) {
531  vector< Point2f > in, out;
532  in.push_back(objPoint2D);
533  perspectiveTransform(in, out, transformations[markerIdx]);
534  interpolatedPositions.push_back(out[0]);
535  }
536  }
537 
538  // none of the closest markers detected
539  if(interpolatedPositions.size() == 0) continue;
540 
541  // more than one closest marker detected, take middle point
542  if(interpolatedPositions.size() > 1) {
543  allChessboardImgPoints[i] = (interpolatedPositions[0] + interpolatedPositions[1]) / 2.;
544  }
545  // a single closest marker detected
546  else allChessboardImgPoints[i] = interpolatedPositions[0];
547  }
548 
549  // calculate maximum window sizes for subpixel refinement. The size is limited by the distance
550  // to the closes marker corner to avoid erroneous displacements to marker corners
551  vector< Size > subPixWinSizes;
552  _getMaximumSubPixWindowSizes(_markerCorners, _markerIds, allChessboardImgPoints, _board,
553  subPixWinSizes);
554 
555 
556  // filter corners outside the image and subpixel-refine charuco corners
557  return _selectAndRefineChessboardCorners(allChessboardImgPoints, _image, _charucoCorners,
558  _charucoIds, subPixWinSizes);
559 }
560 
561 
562 
565 int interpolateCornersCharuco(InputArrayOfArrays _markerCorners, InputArray _markerIds,
566  InputArray _image, const Ptr<CharucoBoard> &_board,
567  OutputArray _charucoCorners, OutputArray _charucoIds,
568  InputArray _cameraMatrix, InputArray _distCoeffs, int minMarkers) {
569 
570  // if camera parameters are avaible, use approximated calibration
571  if(_cameraMatrix.total() != 0) {
572  _interpolateCornersCharucoApproxCalib(_markerCorners, _markerIds, _image, _board,
573  _cameraMatrix, _distCoeffs, _charucoCorners,
574  _charucoIds);
575  }
576  // else use local homography
577  else {
578  _interpolateCornersCharucoLocalHom(_markerCorners, _markerIds, _image, _board,
579  _charucoCorners, _charucoIds);
580  }
581 
582  // to return a charuco corner, its closest aruco markers should have been detected
583  return _filterCornersWithoutMinMarkers(_board, _charucoCorners, _charucoIds, _markerIds,
584  minMarkers, _charucoCorners, _charucoIds);
585 }
586 
587 
588 
591 void drawDetectedCornersCharuco(InputOutputArray _image, InputArray _charucoCorners,
592  InputArray _charucoIds, Scalar cornerColor) {
593 
594  CV_Assert(_image.getMat().total() != 0 &&
595  (_image.getMat().channels() == 1 || _image.getMat().channels() == 3));
596  CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total()) ||
597  _charucoIds.getMat().total() == 0);
598 
599  unsigned int nCorners = (unsigned int)_charucoCorners.getMat().total();
600  for(unsigned int i = 0; i < nCorners; i++) {
601  Point2f corner = _charucoCorners.getMat().at< Point2f >(i);
602 
603  // draw first corner mark
604  rectangle(_image, corner - Point2f(3, 3), corner + Point2f(3, 3), cornerColor, 1, LINE_AA);
605 
606  // draw ID
607  if(_charucoIds.total() != 0) {
608  int id = _charucoIds.getMat().at< int >(i);
609  stringstream s;
610  s << "id=" << id;
611  putText(_image, s.str(), corner + Point2f(5, -5), FONT_HERSHEY_SIMPLEX, 0.5,
612  cornerColor, 2);
613  }
614  }
615 }
616 
617 
622 static bool _arePointsEnoughForPoseEstimation(const vector< Point3f > &points) {
623 
624  if(points.size() < 4) return false;
625 
626  vector< double > sameXValue; // different x values in points
627  vector< int > sameXCounter; // number of points with the x value in sameXValue
628  for(unsigned int i = 0; i < points.size(); i++) {
629  bool found = false;
630  for(unsigned int j = 0; j < sameXValue.size(); j++) {
631  if(sameXValue[j] == points[i].x) {
632  found = true;
633  sameXCounter[j]++;
634  }
635  }
636  if(!found) {
637  sameXValue.push_back(points[i].x);
638  sameXCounter.push_back(1);
639  }
640  }
641 
642  // count how many x values has more than 2 points
643  int moreThan2 = 0;
644  for(unsigned int i = 0; i < sameXCounter.size(); i++) {
645  if(sameXCounter[i] >= 2) moreThan2++;
646  }
647 
648  // if we have more than 1 two xvalues with more than 2 points, calibration is ok
649  if(moreThan2 > 1)
650  return true;
651  else
652  return false;
653 }
654 
655 
658 bool estimatePoseCharucoBoard(InputArray _charucoCorners, InputArray _charucoIds,
659  const Ptr<CharucoBoard> &_board, InputArray _cameraMatrix, InputArray _distCoeffs,
660  OutputArray _rvec, OutputArray _tvec, bool useExtrinsicGuess) {
661 
662  CV_Assert((_charucoCorners.getMat().total() == _charucoIds.getMat().total()));
663 
664  // need, at least, 4 corners
665  if(_charucoIds.getMat().total() < 4) return false;
666 
667  vector< Point3f > objPoints;
668  objPoints.reserve(_charucoIds.getMat().total());
669  for(unsigned int i = 0; i < _charucoIds.getMat().total(); i++) {
670  int currId = _charucoIds.getMat().at< int >(i);
671  CV_Assert(currId >= 0 && currId < (int)_board->chessboardCorners.size());
672  objPoints.push_back(_board->chessboardCorners[currId]);
673  }
674 
675  // points need to be in different lines, check if detected points are enough
676  if(!_arePointsEnoughForPoseEstimation(objPoints)) return false;
677 
678  solvePnP(objPoints, _charucoCorners, _cameraMatrix, _distCoeffs, _rvec, _tvec, useExtrinsicGuess);
679 
680  return true;
681 }
682 
683 
684 
685 
688 double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds,
689  const Ptr<CharucoBoard> &_board, Size imageSize,
690  InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs,
691  OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs,
692  OutputArray _stdDeviationsIntrinsics,
693  OutputArray _stdDeviationsExtrinsics,
694  OutputArray _perViewErrors,
695  int flags, TermCriteria criteria) {
696 
697  CV_Assert(_charucoIds.total() > 0 && (_charucoIds.total() == _charucoCorners.total()));
698 
699  // Join object points of charuco corners in a single vector for calibrateCamera() function
700  vector< vector< Point3f > > allObjPoints;
701  allObjPoints.resize(_charucoIds.total());
702  for(unsigned int i = 0; i < _charucoIds.total(); i++) {
703  unsigned int nCorners = (unsigned int)_charucoIds.getMat(i).total();
704  CV_Assert(nCorners > 0 && nCorners == _charucoCorners.getMat(i).total());
705  allObjPoints[i].reserve(nCorners);
706 
707  for(unsigned int j = 0; j < nCorners; j++) {
708  int pointId = _charucoIds.getMat(i).at< int >(j);
709  CV_Assert(pointId >= 0 && pointId < (int)_board->chessboardCorners.size());
710  allObjPoints[i].push_back(_board->chessboardCorners[pointId]);
711  }
712  }
713 
714  return calibrateCamera(allObjPoints, _charucoCorners, imageSize, _cameraMatrix, _distCoeffs,
715  _rvecs, _tvecs, _stdDeviationsIntrinsics, _stdDeviationsExtrinsics,
716  _perViewErrors, flags, criteria);
717 }
718 
719 
720 
723 double calibrateCameraCharuco(InputArrayOfArrays _charucoCorners, InputArrayOfArrays _charucoIds,
724  const Ptr<CharucoBoard> &_board, Size imageSize,
725  InputOutputArray _cameraMatrix, InputOutputArray _distCoeffs,
726  OutputArrayOfArrays _rvecs, OutputArrayOfArrays _tvecs, int flags,
727  TermCriteria criteria) {
728  return calibrateCameraCharuco(_charucoCorners, _charucoIds, _board, imageSize, _cameraMatrix, _distCoeffs, _rvecs,
729  _tvecs, noArray(), noArray(), noArray(), flags, criteria);
730 }
731 
732 
735 void detectCharucoDiamond(InputArray _image, InputArrayOfArrays _markerCorners,
736  InputArray _markerIds, float squareMarkerLengthRate,
737  OutputArrayOfArrays _diamondCorners, OutputArray _diamondIds,
738  InputArray _cameraMatrix, InputArray _distCoeffs) {
739 
740  CV_Assert(_markerIds.total() > 0 && _markerIds.total() == _markerCorners.total());
741 
742  const float minRepDistanceRate = 1.302455f;
743 
744  // create Charuco board layout for diamond (3x3 layout)
745  Ptr<Dictionary> dict = getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME(0));
746  Ptr<CharucoBoard> _charucoDiamondLayout = CharucoBoard::create(3, 3, squareMarkerLengthRate, 1., dict);
747 
748 
749  vector< vector< Point2f > > diamondCorners;
750  vector< Vec4i > diamondIds;
751 
752  // stores if the detected markers have been assigned or not to a diamond
753  vector< bool > assigned(_markerIds.total(), false);
754  if(_markerIds.total() < 4) return; // a diamond need at least 4 markers
755 
756  // convert input image to grey
757  Mat grey;
758  if(_image.type() == CV_8UC3)
759  cvtColor(_image, grey, COLOR_BGR2GRAY);
760  else
761  _image.copyTo(grey);
762 
763  // for each of the detected markers, try to find a diamond
764  for(unsigned int i = 0; i < _markerIds.total(); i++) {
765  if(assigned[i]) continue;
766 
767  // calculate marker perimeter
768  float perimeterSq = 0;
769  Mat corners = _markerCorners.getMat(i);
770  for(int c = 0; c < 4; c++) {
771  Point2f edge = corners.at< Point2f >(c) - corners.at< Point2f >((c + 1) % 4);
772  perimeterSq += edge.x*edge.x + edge.y*edge.y;
773  }
774  // maximum reprojection error relative to perimeter
775  float minRepDistance = sqrt(perimeterSq) * minRepDistanceRate;
776 
777  int currentId = _markerIds.getMat().at< int >(i);
778 
779  // prepare data to call refineDetectedMarkers()
780  // detected markers (only the current one)
781  vector< Mat > currentMarker;
782  vector< int > currentMarkerId;
783  currentMarker.push_back(_markerCorners.getMat(i));
784  currentMarkerId.push_back(currentId);
785 
786  // marker candidates (the rest of markers if they have not been assigned)
787  vector< Mat > candidates;
788  vector< int > candidatesIdxs;
789  for(unsigned int k = 0; k < assigned.size(); k++) {
790  if(k == i) continue;
791  if(!assigned[k]) {
792  candidates.push_back(_markerCorners.getMat(k));
793  candidatesIdxs.push_back(k);
794  }
795  }
796  if(candidates.size() < 3) break; // we need at least 3 free markers
797 
798  // modify charuco layout id to make sure all the ids are different than current id
799  for(int k = 1; k < 4; k++)
800  _charucoDiamondLayout->ids[k] = currentId + 1 + k;
801  // current id is assigned to [0], so it is the marker on the top
802  _charucoDiamondLayout->ids[0] = currentId;
803 
804  // try to find the rest of markers in the diamond
805  vector< int > acceptedIdxs;
806  Ptr<Board> _b = _charucoDiamondLayout.staticCast<Board>();
808  currentMarker, currentMarkerId,
809  candidates, noArray(), noArray(), minRepDistance, -1, false,
810  acceptedIdxs);
811 
812  // if found, we have a diamond
813  if(currentMarker.size() == 4) {
814 
815  assigned[i] = true;
816 
817  // calculate diamond id, acceptedIdxs array indicates the markers taken from candidates
818  // array
819  Vec4i markerId;
820  markerId[0] = currentId;
821  for(int k = 1; k < 4; k++) {
822  int currentMarkerIdx = candidatesIdxs[acceptedIdxs[k - 1]];
823  markerId[k] = _markerIds.getMat().at< int >(currentMarkerIdx);
824  assigned[currentMarkerIdx] = true;
825  }
826 
827  // interpolate the charuco corners of the diamond
828  vector< Point2f > currentMarkerCorners;
829  Mat aux;
830  interpolateCornersCharuco(currentMarker, currentMarkerId, grey, _charucoDiamondLayout,
831  currentMarkerCorners, aux, _cameraMatrix, _distCoeffs);
832 
833  // if everything is ok, save the diamond
834  if(currentMarkerCorners.size() > 0) {
835  // reorder corners
836  vector< Point2f > currentMarkerCornersReorder;
837  currentMarkerCornersReorder.resize(4);
838  currentMarkerCornersReorder[0] = currentMarkerCorners[2];
839  currentMarkerCornersReorder[1] = currentMarkerCorners[3];
840  currentMarkerCornersReorder[2] = currentMarkerCorners[1];
841  currentMarkerCornersReorder[3] = currentMarkerCorners[0];
842 
843  diamondCorners.push_back(currentMarkerCornersReorder);
844  diamondIds.push_back(markerId);
845  }
846  }
847  }
848 
849 
850  if(diamondIds.size() > 0) {
851  // parse output
852  Mat(diamondIds).copyTo(_diamondIds);
853 
854  _diamondCorners.create((int)diamondCorners.size(), 1, CV_32FC2);
855  for(unsigned int i = 0; i < diamondCorners.size(); i++) {
856  _diamondCorners.create(4, 1, CV_32FC2, i, true);
857  for(int j = 0; j < 4; j++) {
858  _diamondCorners.getMat(i).at< Point2f >(j) = diamondCorners[i][j];
859  }
860  }
861  }
862 }
863 
864 
865 
866 
869 void drawCharucoDiamond(const Ptr<Dictionary> &dictionary, Vec4i ids, int squareLength, int markerLength,
870  OutputArray _img, int marginSize, int borderBits) {
871 
872  CV_Assert(squareLength > 0 && markerLength > 0 && squareLength > markerLength);
873  CV_Assert(marginSize >= 0 && borderBits > 0);
874 
875  // create a charuco board similar to a charuco marker and print it
876  Ptr<CharucoBoard> board =
877  CharucoBoard::create(3, 3, (float)squareLength, (float)markerLength, dictionary);
878 
879  // assign the charuco marker ids
880  for(int i = 0; i < 4; i++)
881  board->ids[i] = ids[i];
882 
883  Size outSize(3 * squareLength + 2 * marginSize, 3 * squareLength + 2 * marginSize);
884  board->draw(outSize, _img, marginSize, borderBits);
885 }
886 
887 
890 void drawDetectedDiamonds(InputOutputArray _image, InputArrayOfArrays _corners,
891  InputArray _ids, Scalar borderColor) {
892 
893 
894  CV_Assert(_image.getMat().total() != 0 &&
895  (_image.getMat().channels() == 1 || _image.getMat().channels() == 3));
896  CV_Assert((_corners.total() == _ids.total()) || _ids.total() == 0);
897 
898  // calculate colors
899  Scalar textColor, cornerColor;
900  textColor = cornerColor = borderColor;
901  swap(textColor.val[0], textColor.val[1]); // text color just sawp G and R
902  swap(cornerColor.val[1], cornerColor.val[2]); // corner color just sawp G and B
903 
904  int nMarkers = (int)_corners.total();
905  for(int i = 0; i < nMarkers; i++) {
906  Mat currentMarker = _corners.getMat(i);
907  CV_Assert(currentMarker.total() == 4 && currentMarker.type() == CV_32FC2);
908 
909  // draw marker sides
910  for(int j = 0; j < 4; j++) {
911  Point2f p0, p1;
912  p0 = currentMarker.at< Point2f >(j);
913  p1 = currentMarker.at< Point2f >((j + 1) % 4);
914  line(_image, p0, p1, borderColor, 1);
915  }
916 
917  // draw first corner mark
918  rectangle(_image, currentMarker.at< Point2f >(0) - Point2f(3, 3),
919  currentMarker.at< Point2f >(0) + Point2f(3, 3), cornerColor, 1, LINE_AA);
920 
921  // draw id composed by four numbers
922  if(_ids.total() != 0) {
923  Point2f cent(0, 0);
924  for(int p = 0; p < 4; p++)
925  cent += currentMarker.at< Point2f >(p);
926  cent = cent / 4.;
927  stringstream s;
928  s << "id=" << _ids.getMat().at< Vec4i >(i);
929  putText(_image, s.str(), cent, FONT_HERSHEY_SIMPLEX, 0.5, textColor, 2);
930  }
931  }
932 }
933 }
934 }
void operator()(const Range &range) const CV_OVERRIDE
Definition: charuco.cpp:286
CV_EXPORTS_W void drawDetectedCornersCharuco(InputOutputArray image, InputArray charucoCorners, InputArray charucoIds=noArray(), Scalar cornerColor=Scalar(255, 0, 0))
Draws a set of Charuco corners.
Definition: charuco.cpp:591
InputArray InputArray counter
Definition: aruco.hpp:569
CV_EXPORTS_W void detectCharucoDiamond(InputArray image, InputArrayOfArrays markerCorners, InputArray markerIds, float squareMarkerLengthRate, OutputArrayOfArrays diamondCorners, OutputArray diamondIds, InputArray cameraMatrix=noArray(), InputArray distCoeffs=noArray())
Detect ChArUco Diamond markers.
Definition: charuco.cpp:735
CV_EXPORTS void drawCharucoDiamond(const Ptr< Dictionary > &dictionary, Vec4i ids, int squareLength, int markerLength, OutputArray img, int marginSize=0, int borderBits=1)
Draw a ChArUco Diamond marker.
Definition: charuco.cpp:869
CV_EXPORTS_W void drawDetectedDiamonds(InputOutputArray image, InputArrayOfArrays diamondCorners, InputArray diamondIds=noArray(), Scalar borderColor=Scalar(0, 0, 255))
Draw a set of detected ChArUco Diamond markers.
Definition: charuco.cpp:890
InputArrayOfArrays const Ptr< CharucoBoard > Size imageSize
Definition: charuco.hpp:245
static int _interpolateCornersCharucoLocalHom(InputArrayOfArrays _markerCorners, InputArray _markerIds, InputArray _image, const Ptr< CharucoBoard > &_board, OutputArray _charucoCorners, OutputArray _charucoIds)
Definition: charuco.cpp:483
XmlRpcServer s
const Ptr< DetectorParameters > & params
Definition: charuco.cpp:312
Definition: charuco.hpp:47
vector< Size > * filteredWinSizes
Definition: charuco.cpp:311
CV_EXPORTS Ptr< Dictionary > getPredefinedDictionary(PREDEFINED_DICTIONARY_NAME name)
Returns one of the predefined dictionaries defined in PREDEFINED_DICTIONARY_NAME. ...
Definition: dictionary.cpp:301
InputArrayOfArrays const Ptr< CharucoBoard > Size InputOutputArray InputOutputArray OutputArrayOfArrays OutputArrayOfArrays OutputArray OutputArray OutputArray int TermCriteria criteria
Definition: charuco.hpp:250
PREDEFINED_DICTIONARY_NAME
Predefined markers dictionaries/sets Each dictionary indicates the number of bits and the number of m...
Definition: dictionary.hpp:141
CvImagePtr cvtColor(const CvImageConstPtr &source, const std::string &encoding)
CharucoSubpixelParallel(const Mat *_grey, vector< Point2f > *_filteredChessboardImgPoints, vector< Size > *_filteredWinSizes, const Ptr< DetectorParameters > &_params)
Definition: charuco.cpp:281
static CV_WRAP Ptr< CharucoBoard > create(int squaresX, int squaresY, float squareLength, float markerLength, const Ptr< Dictionary > &dictionary)
Create a CharucoBoard object.
Definition: charuco.cpp:124
static bool _arePointsEnoughForPoseEstimation(const vector< Point3f > &points)
Definition: charuco.cpp:622
Board of markers.
Definition: aruco.hpp:272
static int _selectAndRefineChessboardCorners(InputArray _allCorners, InputArray _image, OutputArray _selectedCorners, OutputArray _selectedIds, const vector< Size > &winSizes)
From all projected chessboard corners, select those inside the image and apply subpixel refinement...
Definition: charuco.cpp:322
InputArrayOfArrays const Ptr< CharucoBoard > Size InputOutputArray InputOutputArray OutputArrayOfArrays OutputArrayOfArrays OutputArray OutputArray OutputArray int flags
Definition: charuco.hpp:249
double min(double a, double b)
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
CV_EXPORTS_W double calibrateCameraCharuco(InputArrayOfArrays charucoCorners, InputArrayOfArrays charucoIds, const Ptr< CharucoBoard > &board, Size imageSize, InputOutputArray cameraMatrix, InputOutputArray distCoeffs, OutputArrayOfArrays rvecs=noArray(), OutputArrayOfArrays tvecs=noArray(), int flags=0, TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON))
It&#39;s the same function as calibrateCameraCharuco but without calibration error estimation.
Definition: charuco.cpp:723
void _drawPlanarBoardImpl(Board *board, Size outSize, OutputArray img, int marginSize=0, int borderBits=1)
Implementation of drawPlanarBoard that accepts a raw Board pointer.
Definition: aruco.cpp:1764
CV_WRAP void draw(Size outSize, OutputArray img, int marginSize=0, int borderBits=1)
Draw a ChArUco board.
Definition: charuco.cpp:54
CV_EXPORTS_W void refineDetectedMarkers(InputArray image, const Ptr< Board > &board, InputOutputArrayOfArrays detectedCorners, InputOutputArray detectedIds, InputOutputArrayOfArrays rejectedCorners, InputArray cameraMatrix=noArray(), InputArray distCoeffs=noArray(), float minRepDistance=10.f, float errorCorrectionRate=3.f, bool checkAllOrders=true, OutputArray recoveredIdxs=noArray(), const Ptr< DetectorParameters > &parameters=DetectorParameters::create())
Refind not detected markers based on the already detected and the board layout.
Definition: aruco.cpp:1402
vector< Point2f > * filteredChessboardImgPoints
Definition: charuco.cpp:310
static int _filterCornersWithoutMinMarkers(const Ptr< CharucoBoard > &_board, InputArray _allCharucoCorners, InputArray _allCharucoIds, InputArray _allArucoIds, int minMarkers, OutputArray _filteredCharucoCorners, OutputArray _filteredCharucoIds)
Definition: charuco.cpp:234
static CV_WRAP Ptr< DetectorParameters > create()
Create a new set of DetectorParameters with default values.
Definition: aruco.cpp:96
CV_EXPORTS_W bool estimatePoseCharucoBoard(InputArray charucoCorners, InputArray charucoIds, const Ptr< CharucoBoard > &board, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false)
Pose estimation for a ChArUco board given some of their corners.
Definition: charuco.cpp:658
CV_EXPORTS_W int estimatePoseBoard(InputArrayOfArrays corners, InputArray ids, const Ptr< Board > &board, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, bool useExtrinsicGuess=false)
Pose estimation for a board of markers.
Definition: aruco.cpp:1596
InputArray ids
Definition: aruco.hpp:569
static int _interpolateCornersCharucoApproxCalib(InputArrayOfArrays _markerCorners, InputArray _markerIds, InputArray _image, const Ptr< CharucoBoard > &_board, InputArray _cameraMatrix, InputArray _distCoeffs, OutputArray _charucoCorners, OutputArray _charucoIds)
Definition: charuco.cpp:439
void _getNearestMarkerCorners()
Definition: charuco.cpp:179
void swap(scoped_ptr< T > &, scoped_ptr< T > &)
static void _getMaximumSubPixWindowSizes(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray charucoCorners, const Ptr< CharucoBoard > &board, vector< Size > &sizes)
Definition: charuco.cpp:386
CV_EXPORTS_W int interpolateCornersCharuco(InputArrayOfArrays markerCorners, InputArray markerIds, InputArray image, const Ptr< CharucoBoard > &board, OutputArray charucoCorners, OutputArray charucoIds, InputArray cameraMatrix=noArray(), InputArray distCoeffs=noArray(), int minMarkers=2)
Interpolate position of ChArUco board corners.
Definition: charuco.cpp:565
InputArrayOfArrays const Ptr< CharucoBoard > & board
Definition: charuco.hpp:245


aruco_pose
Author(s): Oleg Kalachev
autogenerated on Mon Feb 28 2022 22:08:24