22 #include <opencv2/core/core.hpp>
23 #include <opencv2/imgproc/imgproc.hpp>
24 #include <opencv2/features2d/features2d.hpp>
25 #include <opencv2/calib3d/calib3d.hpp>
49 MarkerDetector_Impl::MarkerDetector_Impl()
60 MarkerDetector_Impl::MarkerDetector_Impl(
int dict_type,
float error_correction_rate)
62 setDictionary(dict_type, error_correction_rate);
71 MarkerDetector_Impl::MarkerDetector_Impl(std::string dict_type,
float error_correction_rate)
73 setDictionary(dict_type, error_correction_rate);
83 MarkerDetector_Impl::~MarkerDetector_Impl()
90 setDictionary(_params.dictionary, _params.error_correction_rate);
99 void MarkerDetector_Impl::setDetectionMode(
DetectionMode dm,
float minMarkerSize)
101 _params.setDetectionMode(dm, minMarkerSize);
106 return _params.detectMode;
118 std::vector<aruco::Marker> MarkerDetector_Impl::detect(
const cv::Mat &input)
120 std::vector<Marker> detectedMarkers;
121 detect(input, detectedMarkers);
122 return detectedMarkers;
125 std::vector<aruco::Marker> MarkerDetector_Impl::detect(
const cv::Mat &input,
127 float markerSizeMeters,
128 bool setYPerperdicular,
bool correctFisheye)
130 std::vector<Marker> detectedMarkers;
131 detect(input, detectedMarkers, camParams, markerSizeMeters, setYPerperdicular, correctFisheye);
132 return detectedMarkers;
141 void MarkerDetector_Impl::detect(
const cv::Mat &input, std::vector<Marker> &detectedMarkers,
143 bool setYPerpendicular,
bool correctFisheye)
145 if (camParams.
CamSize != input.size() && camParams.
isValid() && markerSizeMeters > 0)
149 cp_aux.
resize(input.size());
151 cp_aux.
ExtrinsicMatrix, markerSizeMeters, setYPerpendicular, correctFisheye);
156 camParams.
ExtrinsicMatrix, markerSizeMeters, setYPerpendicular, correctFisheye);
159 int MarkerDetector_Impl::getMarkerWarpSize()
161 auto bis = markerIdDetector->getBestInputSize();
165 int ndiv = markerIdDetector->getNSubdivisions();
168 return _params.markerWarpPixSize *
174 void MarkerDetector_Impl::buildPyramid(vector<cv::Mat> &ImagePyramid,
const cv::Mat &grey,
179 cv::Size imgpsize = grey.size();
180 while (imgpsize.width > minSize)
182 imgpsize = cv::Size(imgpsize.width / _params.pyrfactor, imgpsize.height / _params.pyrfactor);
186 ImagePyramid.resize(npyrimg);
187 imagePyramid[0] = grey;
189 imgpsize = grey.size();
190 for (
int i = 1; i < npyrimg; i++)
192 cv::Size nsize(ImagePyramid[i - 1].cols / _params.pyrfactor,
193 ImagePyramid[i - 1].rows / _params.pyrfactor);
194 cv::resize(ImagePyramid[i - 1], ImagePyramid[i], nsize);
199 bool norm,
int wsize)
201 if (im.type() != CV_8UC1)
202 throw std::runtime_error(
"assignClass_fast Input image must be 8UC1");
203 int wsizeFull = wsize * 2 + 1;
205 cv::Mat labels = cv::Mat::zeros(wsize * 2 + 1, wsize * 2 + 1, CV_8UC1);
206 cv::Mat thresIm = cv::Mat(wsize * 2 + 1, wsize * 2 + 1, CV_8UC1);
208 for (
auto &kp : kpoints)
210 int x = kp.pt.x + 0.5f;
211 int y = kp.pt.y + 0.5f;
213 cv::Rect r = cv::Rect(x - wsize, y - wsize, wsize * 2 + 1, wsize * 2 + 1);
215 if (r.x < 0 || r.x + r.width > im.cols || r.y < 0 || r.y + r.height > im.rows)
219 int endX = r.x + r.width;
220 int endY = r.y + r.height;
221 uchar minV = 255, maxV = 0;
222 for (
int y = r.y; y < endY; y++)
224 const uchar *ptr = im.ptr<uchar>(y);
225 for (
int x = r.x; x < endX; x++)
234 if ((maxV - minV) < 25)
240 double thres = (maxV + minV) / 2.0;
244 for (
int y = 0; y < wsizeFull; y++)
246 const uchar *ptr = im.ptr<uchar>(r.y + y) + r.x;
247 uchar *thresPtr = thresIm.ptr<uchar>(y);
248 for (
int x = 0; x < wsizeFull; x++)
260 for (
int y = 0; y < thresIm.rows; y++)
262 uchar *labelsPtr = labels.ptr<uchar>(y);
263 for (
int x = 0; x < thresIm.cols; x++)
268 std::map<uchar, uchar> unions;
269 for (
int y = 0; y < thresIm.rows; y++)
271 uchar *thresPtr = thresIm.ptr<uchar>(y);
272 uchar *labelsPtr = labels.ptr<uchar>(y);
273 for (
int x = 0; x < thresIm.cols; x++)
275 uchar reg = thresPtr[x];
281 if (reg == thresPtr[x - 1])
282 lleft_px = labelsPtr[x - 1];
287 if (reg == thresIm.ptr<uchar>(y - 1)[x])
288 ltop_px = labels.at<uchar>(y - 1, x);
291 if (lleft_px == 0 && ltop_px == 0)
292 labelsPtr[x] = newLab++;
294 else if (lleft_px != 0 && ltop_px != 0)
296 if (lleft_px < ltop_px)
298 labelsPtr[x] = lleft_px;
299 unions[ltop_px] = lleft_px;
301 else if (lleft_px > ltop_px)
303 labelsPtr[x] = ltop_px;
304 unions[lleft_px] = ltop_px;
308 labelsPtr[x] = ltop_px;
314 labelsPtr[x] = lleft_px;
316 labelsPtr[x] = ltop_px;
321 int nc = newLab - 1 - unions.size();
324 if (nZ > thresIm.total() - nZ)
422 vector<MarkerDetector_Impl::MarkerCandidate> MarkerDetector_Impl::thresholdAndDetectRectangles(
423 const cv::Mat &input,
int thres_param1,
int thres_param2,
bool enclosed, cv::Mat &auxThresImage)
427 if (thres_param1 < 3)
429 else if (((
int)thres_param1) % 2 != 1)
430 thres_param1 = (int)(thres_param1 + 1);
432 int enclosedOffset = -1;
435 auxImage = auxThresImage;
436 if (_params.thresMethod == MarkerDetector::THRES_AUTO_FIXED)
438 cv::threshold(input, auxImage,
static_cast<int>(thres_param2), 255, THRESH_BINARY_INV);
442 enclosedOffset = int(std::max(3.0, 3. / 1920. *
float(auxImage.cols)));
443 if (enclosedOffset % 2 == 0)
445 cv::erode(auxImage, aux1,
446 getStructuringElement(MORPH_CROSS, cv::Size(enclosedOffset, enclosedOffset),
447 cv::Point(enclosedOffset / 2, enclosedOffset / 2)));
448 cv::bitwise_xor(aux1, auxImage, auxImage);
455 cv::adaptiveThreshold(input, auxImage, 255., ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV,
456 static_cast<int>(thres_param1),
static_cast<int>(thres_param2));
457 if (_params.closingSize > 0)
459 int p = _params.closingSize * 2 + 1;
461 cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(p, p));
462 cv::morphologyEx(auxImage, im2, cv::MORPH_OPEN, ker);
465 enclosedOffset = thres_param1;
473 vector<MarkerCandidate> MarkerCanditates;
475 int thisImageMinSize = int(3.5 *
float(_params.lowResMarkerSize));
477 std::vector<cv::Vec4i> hierarchy;
478 std::vector<std::vector<cv::Point>> contours;
479 cv::findContours(auxThresImage, contours, cv::noArray(), cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
481 vector<Point> approxCurve;
483 #ifdef _aruco_debug_detectrectangles
485 cv::cvtColor(input, simage, CV_GRAY2BGR);
489 for (
unsigned int i = 0; i < contours.size(); i++)
491 #ifdef _aruco_debug_detectrectangles
492 drawContour(simage, contours[i], Scalar(125, 125, 255));
495 if (thisImageMinSize <
int(contours[i].size()))
498 cv::approxPolyDP(contours[i], approxCurve,
double(contours[i].size()) * 0.05,
true);
500 if (approxCurve.size() == 4 && cv::isContourConvex(approxCurve))
502 #ifdef _aruco_debug_detectrectangles
503 drawApproxCurve(simage, approxCurve, Scalar(255, 0, 255), 1);
506 float minDist = std::numeric_limits<float>::max();
507 for (
int j = 0; j < 4; j++)
509 float d = cv::norm(approxCurve[j] - approxCurve[(j + 1) % 4]);
516 for (
int j = 0; j < 4; j++)
517 MarkerCanditates.back().push_back(Point2f(
static_cast<float>(approxCurve[j].x),
518 static_cast<float>(approxCurve[j].y)));
524 enlargeMarkerCandidate(MarkerCanditates.back(),
float(enclosedOffset) / 2.);
526 #ifdef _aruco_debug_detectrectangles
527 MarkerCanditates.back().draw(simage, Scalar(255, 255, 0), 1,
false);
529 MarkerCanditates.back().contourPoints = contours[i];
533 #ifdef _aruco_debug_detectrectangles
534 cv::imshow(
"contours", simage);
536 return MarkerCanditates;
540 void MarkerDetector_Impl::thresholdAndDetectRectangles_thread()
544 bool enclosed =
false;
545 auto tad = _tasks.pop();
546 if (tad.task == EXIT_TASK)
548 else if (tad.task == ENCLOSE_TASK)
550 _vcandidates[tad.outIdx] =
551 thresholdAndDetectRectangles(_thres_Images[tad.inIdx], tad.param1, tad.param2,
552 enclosed, _thres_Images[tad.outIdx]);
556 vector<aruco::MarkerDetector_Impl::MarkerCandidate> MarkerDetector_Impl::thresholdAndDetectRectangles(
557 const cv::Mat &image)
561 int adaptiveWindowSize = _params.AdaptiveThresWindowSize;
562 if (_params.AdaptiveThresWindowSize == -1)
563 adaptiveWindowSize = max(
int(3),
int(15 *
float(image.cols) / 1920.));
564 if (adaptiveWindowSize % 2 == 0)
565 adaptiveWindowSize++;
569 vector<int> p1_values;
570 for (
int i =
static_cast<int>(
571 std::max(3., adaptiveWindowSize - 2. * _params.AdaptiveThresWindowSize_range));
572 i <= adaptiveWindowSize + 2 * _params.AdaptiveThresWindowSize_range; i += 2)
573 p1_values.push_back(i);
575 size_t nimages = p1_values.size();
576 _tooNearDistance = p1_values.back();
577 _vcandidates.resize(nimages);
578 _thres_Images.resize(nimages + 1);
579 _thres_Images.back() = image;
582 vector<ThresAndDetectRectTASK> vtad;
586 if (_params.enclosedMarker)
588 for (
size_t i = 0; i < p1_values.size(); i++)
590 tad.
inIdx = int(_thres_Images.size() - 1);
591 tad.
param1 = p1_values[i];
592 tad.
param2 = _params.ThresHold;
601 for (
size_t i = 0; i < nimages; i++)
602 _thres_Images[i].create(image.size(), CV_8UC1);
607 if (_params.maxThreads <= 0)
609 nthreads = std::thread::hardware_concurrency() - 1;
611 nthreads = max(1, _params.maxThreads - 1);
613 tad.
task = EXIT_TASK;
614 for (
int i = 0; i < nthreads; i++)
620 thresholdAndDetectRectangles_thread();
628 vector<std::thread> threads;
629 for (
int i = 0; i < nthreads; i++)
631 std::thread(&MarkerDetector_Impl::thresholdAndDetectRectangles_thread,
this));
633 for (
auto &th : threads)
636 vector<MarkerCandidate> joined;
637 joinVectors(_vcandidates, joined);
641 vector<MarkerDetector_Impl::MarkerCandidate> MarkerDetector_Impl::prefilterCandidates(
642 vector<aruco::MarkerDetector_Impl::MarkerCandidate> &MarkerCanditates, cv::Size imgSize)
648 valarray<bool> swapped(
false, MarkerCanditates.size());
649 for (
unsigned int i = 0; i < MarkerCanditates.size(); i++)
653 double dx1 = MarkerCanditates[i][1].x - MarkerCanditates[i][0].x;
654 double dy1 = MarkerCanditates[i][1].y - MarkerCanditates[i][0].y;
655 double dx2 = MarkerCanditates[i][2].x - MarkerCanditates[i][0].x;
656 double dy2 = MarkerCanditates[i][2].y - MarkerCanditates[i][0].y;
657 double o = (dx1 * dy2) - (dy1 * dx2);
661 swap(MarkerCanditates[i][1], MarkerCanditates[i][3]);
670 vector<pair<int, int>> TooNearCandidates;
671 for (
unsigned int i = 0; i < MarkerCanditates.size(); i++)
674 for (
unsigned int j = i + 1; j < MarkerCanditates.size(); j++)
676 valarray<float> vdist(4);
677 for (
int c = 0; c < 4; c++)
678 vdist[c] = cv::norm(MarkerCanditates[i][c] - MarkerCanditates[j][c]);
680 if (vdist[0] < _tooNearDistance && vdist[1] < _tooNearDistance &&
681 vdist[2] < _tooNearDistance && vdist[3] < _tooNearDistance)
682 TooNearCandidates.push_back(pair<int, int>(i, j));
687 vector<bool> toRemove(MarkerCanditates.size(),
false);
688 for (
unsigned int i = 0; i < TooNearCandidates.size(); i++)
690 if (perimeter(MarkerCanditates[TooNearCandidates[i].first]) >
691 perimeter(MarkerCanditates[TooNearCandidates[i].second]))
692 toRemove[TooNearCandidates[i].second] =
true;
694 toRemove[TooNearCandidates[i].first] =
true;
699 int borderDistThresX =
static_cast<int>(_params.borderDistThres * float(imgSize.width));
700 int borderDistThresY =
static_cast<int>(_params.borderDistThres * float(imgSize.height));
701 for (
size_t i = 0; i < MarkerCanditates.size(); i++)
704 for (
size_t c = 0; c < MarkerCanditates[i].size(); c++)
706 if (MarkerCanditates[i][c].x < borderDistThresX ||
707 MarkerCanditates[i][c].y < borderDistThresY ||
708 MarkerCanditates[i][c].x > imgSize.width - borderDistThresX ||
709 MarkerCanditates[i][c].y > imgSize.height - borderDistThresY)
717 vector<MarkerCandidate> finalCandidates;
718 finalCandidates.reserve(MarkerCanditates.size());
719 for (
size_t i = 0; i < MarkerCanditates.size(); i++)
721 finalCandidates.push_back(MarkerCanditates[i]);
722 return finalCandidates;
725 return MarkerCanditates;
729 void MarkerDetector_Impl::addToImageHist(cv::Mat &im, std::vector<float> &hist)
731 for (
int y = 0; y < im.rows; y++)
733 uchar *ptr = im.ptr<uchar>(y);
734 for (
int x = 0; x < im.cols; x++)
739 int MarkerDetector_Impl::Otsu(std::vector<float> &hist)
741 float sum = 0, invsum;
750 for (
int t = 1; t < 256; t++)
752 float w0 = 0, w1 = 0, mean0 = 0, mean1 = 0;
753 for (
int v = 0; v < t; v++)
756 mean0 += float(v) * hist[v];
758 for (
int v = t; v < 256; v++)
761 mean1 += hist[v] * float(v);
763 if (w0 > 1e-4 && w1 > 1e-4)
767 float var = w0 * w1 * (mean0 - mean1) * (mean0 - mean1);
781 void MarkerDetector_Impl::detect(
const cv::Mat &input, std::vector<Marker> &detectedMarkers,
782 cv::Mat camMatrix, cv::Mat distCoeff, cv::Mat extrinsics,
783 float markerSizeMeters,
bool setYPerpendicular,
787 detectedMarkers.clear();
788 _vcandidates.clear();
789 _candidates_wcontour.clear();
794 if (input.type() == CV_8UC3)
795 cv::cvtColor(input, grey, CV_BGR2GRAY);
804 float ResizeFactor = 1;
806 cv::Mat imgToBeThresHolded;
807 cv::Size maxImageSize = grey.size();
809 getMinMarkerSizePix(input.size());
810 if (_params.lowResMarkerSize < minpixsize)
812 ResizeFactor = float(_params.lowResMarkerSize) / float(minpixsize);
813 if (ResizeFactor < 0.9)
815 _debug_msg(
"Scale factor=" << ResizeFactor, 1);
816 maxImageSize.width = float(grey.cols) * ResizeFactor + 0.5;
817 maxImageSize.height = float(grey.rows) * ResizeFactor + 0.5;
818 if (maxImageSize.width % 2 != 0)
819 maxImageSize.width++;
820 if (maxImageSize.height % 2 != 0)
821 maxImageSize.height++;
822 cv::resize(grey, imgToBeThresHolded, maxImageSize, 0, 0, cv::INTER_NEAREST);
827 if (imgToBeThresHolded.empty())
828 imgToBeThresHolded = grey;
833 std::thread buildPyramidThread;
836 if (_params.maxThreads > 1)
838 std::thread([&] { buildPyramid(imagePyramid, grey, 2 * getMarkerWarpSize()); });
840 buildPyramid(imagePyramid, grey, 2 * getMarkerWarpSize());
845 imagePyramid.resize(1);
846 imagePyramid[0] = grey;
850 int nAttemptsAutoFix = 0;
851 bool keepLookingFor =
false;
852 std::vector<float> hist(256, 0);
858 vector<MarkerCandidate> MarkerCanditates;
859 MarkerCanditates = thresholdAndDetectRectangles(imgToBeThresHolded);
860 thres = _thres_Images[0];
883 MarkerCanditates = prefilterCandidates(MarkerCanditates, imgToBeThresHolded.size());
896 if (buildPyramidThread.joinable())
897 buildPyramidThread.join();
905 auto markerWarpSize = getMarkerWarpSize();
907 detectedMarkers.clear();
908 _candidates_wcontour.clear();
911 float desiredarea = std::pow(
static_cast<float>(markerWarpSize), 2.
f);
912 for (
size_t i = 0; i < MarkerCanditates.size(); i++)
915 cv::Mat canonicalMarker, canonicalMarkerAux;
917 cv::Mat inToWarp = imgToBeThresHolded;
925 size_t imgPyrIdx = 0;
926 for (
size_t p = 1; p < imagePyramid.size(); p++)
928 if (MarkerCanditates[i].getArea() / pow(4, p) >= desiredarea)
933 inToWarp = imagePyramid[imgPyrIdx];
935 float ratio = float(inToWarp.cols) / float(imgToBeThresHolded.cols);
936 for (
auto &p : points2d_pyr)
939 warp(inToWarp, canonicalMarker, Size(markerWarpSize, markerWarpSize), points2d_pyr);
942 cv::minMaxIdx(canonicalMarker, &min, &Max);
943 canonicalMarker.copyTo(canonicalMarkerAux);
944 string additionalInfo;
954 if (markerIdDetector->detect(canonicalMarkerAux,
id, nRotations, additionalInfo))
956 detectedMarkers.push_back(MarkerCanditates[i]);
957 detectedMarkers.back().id = id;
958 detectedMarkers.back().dict_info = additionalInfo;
959 detectedMarkers.back().contourPoints = MarkerCanditates[i].contourPoints;
961 std::rotate(detectedMarkers.back().begin(), detectedMarkers.back().begin() + 4 - nRotations,
962 detectedMarkers.back().end());
972 if (_params.thresMethod == MarkerDetector::THRES_AUTO_FIXED)
973 addToImageHist(canonicalMarker, hist);
977 _candidates_wcontour.push_back(MarkerCanditates[i]);
981 if (detectedMarkers.size() == 0 && _params.thresMethod == MarkerDetector::THRES_AUTO_FIXED &&
982 ++nAttemptsAutoFix < _params.NAttemptsAutoThresFix)
984 _params.ThresHold = 10 + rand() % 230;
985 keepLookingFor =
true;
988 keepLookingFor =
false;
989 }
while (keepLookingFor);
994 if (_params.thresMethod == MarkerDetector::THRES_AUTO_FIXED)
996 int newThres = Otsu(hist);
999 _params.ThresHold = float(newThres);
1003 cv::imshow(
"image-lines", image);
1007 if (input.cols != imgToBeThresHolded.cols)
1009 cornerUpsample(detectedMarkers, imgToBeThresHolded.size());
1020 if (_params.trackingMinDetections > 0)
1024 for (
auto &md : marker_ndetections)
1026 if (std::find(detectedMarkers.begin(), detectedMarkers.end(),
Marker(md.first)) ==
1027 detectedMarkers.end())
1028 md.second = std::max(md.second - 1, 0);
1032 std::vector<Marker> needsTrack;
1033 for (
auto m : _prevMarkers)
1035 if (std::find(detectedMarkers.begin(), detectedMarkers.end(),
Marker(m.id)) ==
1036 detectedMarkers.end())
1038 if (marker_ndetections.count(m.id) != 0)
1040 if (marker_ndetections.at(m.id) >= _params.trackingMinDetections)
1041 needsTrack.push_back(m);
1047 if (needsTrack.size() > 0)
1051 trackInfo(
const Marker &m)
1057 double dist = std::numeric_limits<double>::max();
1059 std::map<int, trackInfo> marker_trackMatches;
1060 for (
auto &mnt : needsTrack)
1061 marker_trackMatches.insert({ mnt.id, trackInfo(mnt) });
1063 for (
size_t cand = 0; cand < _candidates_wcontour.size(); cand++)
1065 auto &mcnd = _candidates_wcontour[cand];
1068 for (
auto &mtm : marker_trackMatches)
1070 if (mtm.second.ma.isInto(can_ma.
getCenter()))
1072 auto dist = cv::norm(mtm.second.ma.getCenter() - can_ma.
getCenter());
1074 fabs(mtm.second.ma.getArea() - can_ma.
getArea()) / mtm.second.ma.getArea();
1075 if (sizedif < 0.3
f && dist < mtm.second.dist)
1077 mtm.second.candidate =
static_cast<int>(cand);
1078 mtm.second.dist = dist;
1084 for (
auto &mtm : marker_trackMatches)
1086 if (mtm.second.candidate == -1)
1088 auto it = find(_prevMarkers.begin(), _prevMarkers.end(),
Marker(mtm.first));
1089 assert(it != _prevMarkers.end());
1090 detectedMarkers.push_back(_candidates_wcontour[mtm.second.candidate]);
1091 detectedMarkers.back().id = mtm.first;
1092 detectedMarkers.back().dict_info = it->dict_info;
1093 detectedMarkers.back().contourPoints =
1094 _candidates_wcontour[mtm.second.candidate].contourPoints;
1097 auto rotError = [](
const vector<cv::Point2f> &c1,
const vector<cv::Point2f> &c2)
1099 cv::Point2f direction1 = c1[1] - c1[0];
1100 direction1 *= 1. / cv::norm(direction1);
1101 cv::Point2f direction2 = c2[1] - c2[0];
1102 direction2 *= 1. / cv::norm(direction2);
1103 return direction1.dot(direction2);
1105 auto prev = find(_prevMarkers.begin(), _prevMarkers.end(),
Marker(mtm.first));
1106 pair<int, double> rot_error(-1, -1);
1107 for (
int r = 0; r < 4; r++)
1109 vector<cv::Point2f> aux = detectedMarkers.back();
1110 std::rotate(aux.begin(), aux.begin() + r, aux.end());
1111 auto err = rotError(*prev, aux);
1112 if (err > rot_error.second)
1114 rot_error = { r, err };
1118 detectedMarkers.back().begin() + rot_error.first,
1119 detectedMarkers.back().end());
1121 _candidates_wcontour[mtm.second.candidate].clear();
1124 _candidates_wcontour.erase(
1125 std::remove_if(_candidates_wcontour.begin(), _candidates_wcontour.end(),
1126 [](
const vector<cv::Point2f> &m) { return m.size() == 0; }),
1127 _candidates_wcontour.end());
1130 for (
auto m : detectedMarkers)
1132 if (marker_ndetections.count(m.id) == 0)
1133 marker_ndetections[m.id] = 1;
1135 marker_ndetections[m.id]++;
1147 std::sort(detectedMarkers.begin(), detectedMarkers.end());
1151 vector<bool> toRemove(detectedMarkers.size(),
false);
1153 for (
int i = 0; i < int(detectedMarkers.size()) - 1; i++)
1155 for (
int j = i + 1; j < int(detectedMarkers.size()) && !toRemove[i]; j++)
1157 if (detectedMarkers[i].
id == detectedMarkers[j].
id &&
1158 detectedMarkers[i].dict_info == detectedMarkers[j].dict_info)
1161 if (perimeter(detectedMarkers[i]) < perimeter(detectedMarkers[j]))
1169 removeElements(detectedMarkers, toRemove);
1177 if (detectedMarkers.size() > 0 && input.size() == imgToBeThresHolded.size())
1181 int halfwsize = 4 * float(input.cols) / float(imgToBeThresHolded.cols) + 0.5;
1183 vector<Point2f> Corners;
1184 for (
unsigned int i = 0; i < detectedMarkers.size(); i++)
1185 for (
int c = 0; c < 4; c++)
1186 Corners.push_back(detectedMarkers[i][c]);
1187 cornerSubPix(grey, Corners, cv::Size(halfwsize, halfwsize), cv::Size(-1, -1),
1188 cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12, 0.005));
1190 for (
unsigned int i = 0; i < detectedMarkers.size(); i++)
1191 for (
int c = 0; c < 4; c++)
1192 detectedMarkers[i][c] = Corners[i * 4 + c];
1197 for (
unsigned int i = 0; i < detectedMarkers.size(); i++)
1198 refineCornerWithContourLines(detectedMarkers[i]);
1211 if (camMatrix.rows != 0 && markerSizeMeters > 0)
1213 for (
unsigned int i = 0; i < detectedMarkers.size(); i++)
1214 detectedMarkers[i].calculateExtrinsics(markerSizeMeters, camMatrix, distCoeff,
1215 extrinsics, setYPerpendicular, correctFisheye);
1220 float mlength = std::numeric_limits<float>::max();
1221 for (
const auto &marker : detectedMarkers)
1224 for (
int c = 0; c < 4; c++)
1225 l += cv::norm(marker[c] - marker[(c + 1) % 4]);
1229 float markerMinSize;
1230 if (mlength != std::numeric_limits<float>::max())
1231 markerMinSize = mlength / (4 * std::max(input.cols, input.rows));
1234 if (_params.autoSize)
1236 _params.minSize = markerMinSize * (1 - _params.ts);
1241 _prevMarkers = detectedMarkers;
1244 cv::Mat camMatrix, cv::Mat distCoeff)
1249 vector<int> cornerIndex(4, -1);
1250 vector<float> dist(4, std::numeric_limits<float>::max());
1251 for (
unsigned int j = 0; j < contour.size(); j++)
1253 for (
unsigned int k = 0; k < 4; k++)
1255 float d = (contour[j].x - marker[k].x) * (contour[j].x - marker[k].x) +
1256 (contour[j].y - marker[k].y) * (contour[j].y - marker[k].y);
1270 if ((cornerIndex[1] > cornerIndex[0]) &&
1271 (cornerIndex[2] > cornerIndex[1] || cornerIndex[2] < cornerIndex[0]))
1273 else if (cornerIndex[2] > cornerIndex[1] && cornerIndex[2] < cornerIndex[0])
1285 vector<Point2f> contour2f;
1286 if (!camMatrix.empty() && !distCoeff.empty())
1288 for (
unsigned int i = 0; i < contour.size(); i++)
1289 contour2f.push_back(cv::Point2f(contour[i].x, contour[i].y));
1290 if (!camMatrix.empty() && !distCoeff.empty())
1291 cv::undistortPoints(contour2f, contour2f, camMatrix, distCoeff, cv::Mat(), camMatrix);
1295 contour2f.reserve(contour.size());
1296 for (
auto p : contour)
1297 contour2f.push_back(cv::Point2f(p.x, p.y));
1300 vector<std::vector<cv::Point2f>> contourLines;
1301 contourLines.resize(4);
1302 for (
unsigned int l = 0; l < 4; l++)
1304 for (
int j = (
int)cornerIndex[l]; j != (int)cornerIndex[(l + 1) % 4]; j += inc)
1306 if (j == (
int)contour.size() && !inverse)
1308 else if (j == 0 && inverse)
1309 j = contour.size() - 1;
1310 contourLines[l].push_back(contour2f[j]);
1311 if (j == (
int)cornerIndex[(l + 1) % 4])
1317 vector<Point3f> lines;
1319 for (
unsigned int j = 0; j < lines.size(); j++)
1320 interpolate2Dline(contourLines[j], lines[j]);
1323 vector<Point2f> crossPoints;
1324 crossPoints.resize(4);
1325 for (
unsigned int i = 0; i < 4; i++)
1326 crossPoints[i] = getCrossPoint(lines[(i - 1) % 4], lines[i]);
1329 if (!camMatrix.empty() && !distCoeff.empty())
1330 distortPoints(crossPoints, crossPoints, camMatrix, distCoeff);
1333 for (
unsigned int j = 0; j < 4; j++)
1336 marker[j] = crossPoints[j];
1346 for (
int j = 0; j < 2; j++)
1349 auto endp = (j + 2) % 4;
1351 if (cand[startp].x > cand[endp].x)
1355 const float _180 = 3.14159f;
1357 const float _22 = 3.14159 / 8.f;
1358 const float _3_22 = 3. * 3.14159f / 8.f;
1359 const float _5_22 = 5.f * 3.14159f / 8.f;
1360 const float _7_22 = 7.f * 3.14159f / 8.f;
1362 int incx = 0, incy = 0;
1364 auto v1 = cand[endp] - cand[startp];
1365 float angle = atan2(v1.y, v1.x);
1366 if (_22 < angle && angle < 3 * _22)
1370 else if (-_22 < angle && angle < _22)
1375 else if (-_3_22 < angle && angle < -_22)
1380 else if (-_5_22 < angle && angle < -_3_22)
1385 else if (-_7_22 < angle && angle < -_5_22)
1390 else if ((-_180 < angle && angle < -_7_22) || (_7_22 < angle && angle < _180))
1395 else if ((_5_22 < angle && angle < _7_22))
1400 else if ((_3_22 < angle && angle < _5_22))
1405 cand[endp].x += incx;
1406 cand[endp].y += incy;
1407 cand[startp].x -= incx;
1408 cand[startp].y -= incy;
1414 int MarkerDetector_Impl::getMinMarkerSizePix(cv::Size orginput_imageSize)
const
1416 if (_params.minSize == -1 && _params.minSize_pix == -1)
1419 int maxDim = std::max(orginput_imageSize.width, orginput_imageSize.height);
1421 if (_params.minSize != -1)
1422 minSize =
static_cast<float>(_params.minSize) *
static_cast<float>(maxDim);
1423 if (_params.minSize_pix != -1)
1424 minSize = std::min(_params.minSize_pix, minSize);
1433 bool MarkerDetector_Impl::warp(Mat &in, Mat &out, Size size, vector<Point2f> points)
1435 if (points.size() != 4)
1436 throw cv::Exception(9001,
"point.size()!=4",
"MarkerDetector_Impl::warp", __FILE__, __LINE__);
1438 Point2f pointsRes[4], pointsIn[4];
1439 for (
int i = 0; i < 4; i++)
1440 pointsIn[i] = points[i];
1441 pointsRes[0] = (Point2f(0, 0));
1442 pointsRes[1] = Point2f(
static_cast<float>(size.width - 1), 0.f);
1444 Point2f(
static_cast<float>(size.width - 1),
static_cast<float>(size.height - 1));
1445 pointsRes[3] = Point2f(0.
f,
static_cast<float>(size.height - 1));
1446 Mat M = getPerspectiveTransform(pointsIn, pointsRes);
1447 cv::warpPerspective(in, out, M, size, cv::INTER_LINEAR);
1459 int MarkerDetector_Impl::perimeter(
const vector<Point2f> &a)
1462 for (
unsigned int i = 0; i < a.size(); i++)
1464 int i2 = (i + 1) % a.size();
1465 sum +=
static_cast<int>(sqrt((a[i].x - a[i2].x) * (a[i].x - a[i2].x) +
1466 (a[i].y - a[i2].y) * (a[i].y - a[i2].y)));
1474 void MarkerDetector_Impl::interpolate2Dline(
const std::vector<cv::Point2f> &inPoints,
1475 cv::Point3f &outLine)
1477 float minX, maxX, minY, maxY;
1478 minX = maxX = inPoints[0].x;
1479 minY = maxY = inPoints[0].y;
1480 for (
unsigned int i = 1; i < inPoints.size(); i++)
1482 if (inPoints[i].x < minX)
1483 minX = inPoints[i].x;
1484 if (inPoints[i].x > maxX)
1485 maxX = inPoints[i].x;
1486 if (inPoints[i].y < minY)
1487 minY = inPoints[i].y;
1488 if (inPoints[i].y > maxY)
1489 maxY = inPoints[i].y;
1493 const int pointsCount =
static_cast<int>(inPoints.size());
1494 Mat A(pointsCount, 2, CV_32FC1, Scalar(0));
1495 Mat B(pointsCount, 1, CV_32FC1, Scalar(0));
1498 if (maxX - minX > maxY - minY)
1501 for (
int i = 0; i < pointsCount; i++)
1503 A.at<
float>(i, 0) = inPoints[i].x;
1504 A.at<
float>(i, 1) = 1.;
1505 B.at<
float>(i, 0) = inPoints[i].y;
1509 solve(A, B, X, DECOMP_SVD);
1511 outLine = Point3f(X.at<
float>(0, 0), -1., X.at<
float>(1, 0));
1516 for (
int i = 0; i < pointsCount; i++)
1518 A.at<
float>(i, 0) = inPoints[i].y;
1519 A.at<
float>(i, 1) = 1.;
1520 B.at<
float>(i, 0) = inPoints[i].x;
1524 solve(A, B, X, DECOMP_SVD);
1526 outLine = Point3f(-1., X.at<
float>(0, 0), X.at<
float>(1, 0));
1532 Point2f MarkerDetector_Impl::getCrossPoint(
const cv::Point3f &line1,
const cv::Point3f &line2)
1535 Mat A(2, 2, CV_32FC1, Scalar(0));
1536 Mat B(2, 1, CV_32FC1, Scalar(0));
1539 A.at<
float>(0, 0) = line1.x;
1540 A.at<
float>(0, 1) = line1.y;
1541 B.at<
float>(0, 0) = -line1.z;
1543 A.at<
float>(1, 0) = line2.x;
1544 A.at<
float>(1, 1) = line2.y;
1545 B.at<
float>(1, 0) = -line2.z;
1548 solve(A, B, X, DECOMP_SVD);
1549 return Point2f(X.at<
float>(0, 0), X.at<
float>(1, 0));
1599 void MarkerDetector_Impl::drawAllContours(Mat input, std::vector<std::vector<cv::Point>> &contours)
1601 drawContours(input, contours, -1, Scalar(255, 0, 255));
1610 void MarkerDetector_Impl::drawContour(Mat &in, vector<Point> &contour, Scalar color)
1612 for (
unsigned int i = 0; i < contour.size(); i++)
1614 cv::rectangle(in, contour[i], contour[i], color);
1618 void MarkerDetector_Impl::drawApproxCurve(Mat &in, vector<Point> &contour, Scalar color,
int thickness)
1620 for (
unsigned int i = 0; i < contour.size(); i++)
1622 cv::line(in, contour[i], contour[(i + 1) % contour.size()], color, thickness);
1632 void MarkerDetector_Impl::draw(Mat out,
const vector<Marker> &markers)
1634 for (
unsigned int i = 0; i < markers.size(); i++)
1636 cv::line(out, markers[i][0], markers[i][1], cv::Scalar(255, 0, 0), 2);
1637 cv::line(out, markers[i][1], markers[i][2], cv::Scalar(255, 0, 0), 2);
1638 cv::line(out, markers[i][2], markers[i][3], cv::Scalar(255, 0, 0), 2);
1639 cv::line(out, markers[i][3], markers[i][0], cv::Scalar(255, 0, 0), 2);
1644 void MarkerDetector_Impl::setMarkerLabeler(cv::Ptr<MarkerLabeler> detector)
1646 markerIdDetector = detector;
1649 void MarkerDetector_Impl::setDictionary(
int dict_type,
float error_correction_rate)
1653 _params.error_correction_rate = error_correction_rate;
1659 void MarkerDetector_Impl::setDictionary(
string dict_type,
float error_correction_rate)
1661 auto _to_string = [](
float i)
1663 std::stringstream str;
1667 _params.dictionary = dict_type;
1668 markerIdDetector = MarkerLabeler::create(dict_type, _to_string(error_correction_rate));
1669 _params.error_correction_rate = error_correction_rate;
1673 cv::Mat MarkerDetector_Impl::getThresholdedImage(uint32_t idx)
1675 if (_thres_Images.size() == 0)
1677 if (idx >= _thres_Images.size())
1678 idx = _thres_Images.size() - 1;
1679 return _thres_Images[idx];
1686 void MarkerDetector_Impl::distortPoints(vector<cv::Point2f> in, vector<cv::Point2f> &out,
1687 const Mat &camMatrix,
const Mat &distCoeff)
1690 cv::Mat Rvec = cv::Mat(3, 1, CV_32FC1, cv::Scalar::all(0));
1691 cv::Mat Tvec = Rvec.clone();
1693 vector<cv::Point3f> cornersPoints3d;
1694 for (
unsigned int i = 0; i < in.size(); i++)
1695 cornersPoints3d.push_back(
1696 cv::Point3f((in[i].x - camMatrix.at<
float>(0, 2)) / camMatrix.at<
float>(0, 0),
1697 (in[i].y - camMatrix.at<
float>(1, 2)) / camMatrix.at<
float>(1, 1),
1699 cv::projectPoints(cornersPoints3d, Rvec, Tvec, camMatrix, distCoeff, out);
1705 void MarkerDetector_Impl::saveParamsToFile(
const std::string &path)
const
1707 cv::FileStorage fs(path, cv::FileStorage::WRITE);
1709 throw std::runtime_error(
"Could not open " + path);
1715 void MarkerDetector_Impl::loadParamsFromFile(
const std::string &path)
1717 cv::FileStorage fs(path, cv::FileStorage::READ);
1719 throw std::runtime_error(
"Could not open " + path);
1721 setDictionary(_params.dictionary, _params.error_correction_rate);
1724 void MarkerDetector_Impl::toStream(std::ostream &str)
const
1726 uint64_t sig = 13213;
1727 str.write((
char *)&sig,
sizeof(sig));
1728 _params.toStream(str);
1731 void MarkerDetector_Impl::fromStream(std::istream &str)
1733 uint64_t sig = 13213;
1734 str.read((
char *)&sig,
sizeof(sig));
1736 throw std::runtime_error(
"MarkerDetector_Impl::fromStream invalid signature");
1737 _params.fromStream(str);
1738 setDictionary(_params.dictionary, _params.error_correction_rate);
1743 void MarkerDetector_Impl::filter_ambiguous_query(std::vector<cv::DMatch> &matches)
1745 if (matches.size() == 0)
1749 for (
auto m : matches)
1750 maxT = std::max(maxT, m.queryIdx);
1753 vector<int> used(maxT + 1, -1);
1754 vector<cv::DMatch> best_matches(maxT);
1756 bool needRemove =
false;
1758 for (
auto &match : matches)
1760 if (used[match.queryIdx] == -1)
1762 used[match.queryIdx] = idx;
1766 if (matches[used[match.queryIdx]].distance > match.distance)
1768 matches[used[match.queryIdx]].queryIdx = -1;
1769 used[match.queryIdx] = idx;
1774 match.queryIdx = -1;
1783 matches.erase(std::remove_if(matches.begin(), matches.end(),
1784 [](
const cv::DMatch &m)
1785 { return m.trainIdx == -1 || m.queryIdx == -1; }),
1789 void MarkerDetector_Impl::cornerUpsample(std::vector<Marker> &MarkerCanditates,
1790 cv::Size lowResImageSize)
1792 if (MarkerCanditates.size() == 0)
1795 int startPyrImg = 0;
1797 for (
size_t i = 0; i < imagePyramid.size(); i++)
1799 if (lowResImageSize.width < imagePyramid[i].cols)
1806 cv::Size prevLowResSize = lowResImageSize;
1807 for (
int curpyr = startPyrImg; curpyr >= 0; curpyr--)
1809 float factor = float(imagePyramid[curpyr].cols) / float(prevLowResSize.width);
1811 for (
auto &m : MarkerCanditates)
1812 for (
auto &point : m)
1816 int halfwsize = 0.5 + 2.5 * factor;
1817 std::vector<cv::Point2f> p2d;
1818 for (
auto &m : MarkerCanditates)
1819 for (
auto &point : m)
1821 p2d.push_back(point);
1823 cv::cornerSubPix(imagePyramid[curpyr], p2d, cv::Size(halfwsize, halfwsize),
1824 cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::MAX_ITER, 4, 0.5));
1826 for (
auto &m : MarkerCanditates)
1827 for (
auto &point : m)
1829 point = p2d[cidx++];
1832 prevLowResSize = imagePyramid[curpyr].size();
1837 void MarkerDetector_Impl::cornerUpsample(std::vector<std::vector<cv::Point2f>> &MarkerCanditates,
1838 cv::Size lowResImageSize)
1840 std::vector<Marker> markers;
1841 markers.reserve(MarkerCanditates.size());
1842 std::copy(MarkerCanditates.begin(), MarkerCanditates.end(), std::back_inserter(markers));
1844 cornerUpsample(markers, lowResImageSize);
1846 for (std::size_t i = 0; i < markers.size(); i++)
1848 MarkerCanditates[i] = markers[i];