8 #include "opencv2/calib3d/calib3d.hpp"
17 void kcornerSubPix(
const cv::Mat image, std::vector<cv::KeyPoint> &kpoints)
19 std::vector<cv::Point2f> points;
20 cv::KeyPoint::convert(kpoints, points);
22 cv::Size winSize = cv::Size(4, 4);
23 cv::Size zeroZone = cv::Size(-1, -1);
24 cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12,
26 cornerSubPix(image, points, winSize, zeroZone, criteria);
30 for (
auto &k : kpoints)
40 void kfilter(std::vector<cv::KeyPoint> &kpoints)
42 float minResp = kpoints[0].response;
43 float maxResp = kpoints[0].response;
44 for (
auto &p : kpoints)
47 if (p.response < minResp)
49 if (p.response > maxResp)
52 float thresoldResp = (maxResp - minResp) * 0.20
f + minResp;
54 for (uint32_t xi = 0; xi < kpoints.size(); xi++)
57 if (kpoints[xi].response < thresoldResp)
59 kpoints[xi].size = -1;
64 for (uint32_t xj = xi + 1; xj < kpoints.size(); xj++)
66 if (pow(kpoints[xi].pt.x - kpoints[xj].pt.x, 2) +
67 pow(kpoints[xi].pt.y - kpoints[xj].pt.y, 2) <
70 if (kpoints[xj].response > kpoints[xi].response)
71 kpoints[xi] = kpoints[xj];
73 kpoints[xj].size = -1;
77 kpoints.erase(std::remove_if(kpoints.begin(), kpoints.end(),
78 [](
const cv::KeyPoint &kpt) { return kpt.size == -1; }),
82 void assignClass(
const cv::Mat &im, std::vector<cv::KeyPoint> &kpoints,
float sizeNorm,
int wsize)
84 if (im.type() != CV_8UC1)
85 throw std::runtime_error(
"assignClass Input image must be 8UC1");
86 int wsizeFull = wsize * 2 + 1;
88 cv::Mat labels = cv::Mat::zeros(wsize * 2 + 1, wsize * 2 + 1, CV_8UC1);
89 cv::Mat thresIm = cv::Mat(wsize * 2 + 1, wsize * 2 + 1, CV_8UC1);
91 for (
auto &kp : kpoints)
101 x = im.cols * (x / sizeNorm + 0.5f);
102 y = im.rows * (-y / sizeNorm + 0.5f);
108 cv::Rect r = cv::Rect(x - wsize, y - wsize, wsize * 2 + 1, wsize * 2 + 1);
110 if (r.x < 0 || r.x + r.width > im.cols || r.y < 0 || r.y + r.height > im.rows)
113 int endX = r.x + r.width;
114 int endY = r.y + r.height;
115 uchar minV = 255, maxV = 0;
116 for (
int y = r.y; y < endY; y++)
118 const uchar *ptr = im.ptr<uchar>(y);
119 for (
int x = r.x; x < endX; x++)
128 if ((maxV - minV) < 25)
134 double thres = (maxV + minV) / 2.0;
138 for (
int y = 0; y < wsizeFull; y++)
140 const uchar *ptr = im.ptr<uchar>(r.y + y) + r.x;
141 uchar *thresPtr = thresIm.ptr<uchar>(y);
142 for (
int x = 0; x < wsizeFull; x++)
154 for (
int y = 0; y < thresIm.rows; y++)
156 uchar *labelsPtr = labels.ptr<uchar>(y);
157 for (
int x = 0; x < thresIm.cols; x++)
162 std::map<uchar, uchar> unions;
163 for (
int y = 0; y < thresIm.rows; y++)
165 uchar *thresPtr = thresIm.ptr<uchar>(y);
166 uchar *labelsPtr = labels.ptr<uchar>(y);
167 for (
int x = 0; x < thresIm.cols; x++)
169 uchar reg = thresPtr[x];
175 if (reg == thresPtr[x - 1])
176 lleft_px = labelsPtr[x - 1];
181 if (reg == thresIm.ptr<uchar>(y - 1)[x])
182 ltop_px = labels.at<uchar>(y - 1, x);
185 if (lleft_px == 0 && ltop_px == 0)
186 labelsPtr[x] = newLab++;
188 else if (lleft_px != 0 && ltop_px != 0)
190 if (lleft_px < ltop_px)
192 labelsPtr[x] = lleft_px;
193 unions[ltop_px] = lleft_px;
195 else if (lleft_px > ltop_px)
197 labelsPtr[x] = ltop_px;
198 unions[lleft_px] = ltop_px;
202 labelsPtr[x] = ltop_px;
208 labelsPtr[x] = lleft_px;
210 labelsPtr[x] = ltop_px;
215 int nc = newLab - 1 - unions.size();
218 if (nZ > thresIm.total() - nZ)
241 throw cv::Exception(9001,
"Invalid camera parameters",
242 "FractalPoseTracker::setParams", __FILE__, __LINE__);
244 throw cv::Exception(9001,
"Invalid FractalMarker",
"FractalPoseTracker::setParams",
249 throw cv::Exception(9001,
"You should indicate the markersize since the Fractal Marker is in pixels or normalized",
250 "FractalPoseTracker::setParams", __FILE__, __LINE__);
257 int markerId = id_innerMarker.first;
265 _innerkpoints.push_back(cv::KeyPoint(cv::Point2f(pt.x, pt.y), 20, -1, 0, markerId));
269 float radiusF = 0.25;
283 #ifdef _fractal_debug_classification
293 std::vector<cv::Mat> imagePyramid = markerDetector->getImagePyramid();
294 std::vector<cv::Point3f> _ref_inner3d;
295 std::vector<cv::Point2f> _ref_inner2d;
298 _rvec.copyTo(_p_rvec);
300 _tvec.copyTo(_p_tvec);
303 cv::Rodrigues(
_rvec, rot);
308 std::vector<cv::Point3f> marker3d;
309 for (
auto pt : id_marker.second.points)
311 cv::Mat_<double> src(3, 1, rot.type());
316 cv::Mat cam_image_point = rot * src +
_tvec;
317 cam_image_point = cam_image_point / cv::norm(cam_image_point);
319 if (cam_image_point.at<
double>(2, 0) > 0.85)
320 marker3d.push_back(pt);
325 std::vector<cv::Point3f> _inners3d;
326 std::vector<cv::Point2f> _inners2d;
328 if (marker3d.size() < 4)
337 cv::Mat_<double> src(3, 1,
_rvec.type());
342 cv::Mat cam_image_point = rot * src +
_tvec;
343 cam_image_point = cam_image_point / cv::norm(cam_image_point);
345 if (cam_image_point.at<
double>(2, 0) > 0.85)
346 _inners3d.push_back(pt);
349 if (_inners3d.size() == 0)
357 std::vector<cv::Point2f> marker2d;
361 cv::Point2f v01 = marker2d[1] - marker2d[0];
362 cv::Point2f v03 = marker2d[3] - marker2d[0];
363 float area1 = fabs(v01.x * v03.y - v01.y * v03.x);
364 cv::Point2f v21 = marker2d[1] - marker2d[2];
365 cv::Point2f v23 = marker2d[3] - marker2d[2];
366 float area2 = fabs(v21.x * v23.y - v21.y * v23.x);
368 area = (area2 + area1) / 2.
f;
376 size_t imgPyrIdx = 0;
377 auto markerWarpSize = (sqrt(id_marker.second.nBits()) + 2) * markerWarpPix;
378 float desiredarea = std::pow(
static_cast<float>(markerWarpSize), 2.
f);
379 for (
size_t p = 1; p < imagePyramid.size(); p++)
381 if (area / pow(4, p) >= desiredarea)
387 float ratio = float(imagePyramid[imgPyrIdx].cols) / float(imagePyramid[0].cols);
391 std::vector<double> _inners2d_error;
392 if (ratio == 1 && area >= desiredarea)
395 4 * float(imagePyramid[imgPyrIdx].cols) / float(imagePyramid[imgPyrIdx].cols) + 0.5;
397 std::vector<cv::Point2f> _inners2d_copy;
398 for (
auto pt : _inners2d)
400 _inners2d_copy.push_back(pt);
403 imagePyramid[imgPyrIdx], _inners2d, cv::Size(halfwsize, halfwsize), cv::Size(-1, -1),
404 cv::TermCriteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS, 12, 0.005));
406 for (
auto pt : _inners2d)
408 _inners2d_error.push_back(sqrt(pow(_inners2d_copy[idx].x - pt.x, 2) +
409 pow(_inners2d_copy[idx].y - pt.y, 2)));
413 else if (ratio != 1 && area >= desiredarea)
415 std::vector<cv::Point2f> _inners2d_copy;
416 for (
auto &pt : _inners2d)
418 _inners2d_copy.push_back(pt);
422 std::vector<std::vector<cv::Point2f>> vpnts;
423 vpnts.push_back(_inners2d);
424 markerDetector->cornerUpsample(vpnts, imagePyramid[imgPyrIdx].size());
427 for (
auto pt : vpnts[0])
429 _inners2d_error.push_back(sqrt(pow(_inners2d_copy[idx].x - pt.x, 2) +
430 pow(_inners2d_copy[idx].y - pt.y, 2)));
431 _inners2d[idx++] = pt;
436 if (area >= desiredarea)
439 std::vector<double> _inners2d_error_copy;
440 for (
auto err : _inners2d_error)
441 _inners2d_error_copy.push_back(err);
442 sort(_inners2d_error_copy.begin(), _inners2d_error_copy.end());
443 int q1 = (_inners2d_error_copy.size() + 1) / 4;
444 int q3 = 3 * (_inners2d_error_copy.size() + 1) / 4;
446 double limit = _inners2d_error_copy[q3] +
447 3 * (_inners2d_error_copy[q3] - _inners2d_error_copy[q1]);
450 for (
int idx = 0; idx < _inners2d.size(); idx++)
452 if (_inners2d_error[idx] > limit)
455 float x = int(_inners2d[idx].x + 0.5
f);
456 float y = int(_inners2d[idx].y + 0.5
f);
458 cv::Rect r = cv::Rect(x - wsize, y - wsize, wsize * 2 + 1, wsize * 2 + 1);
460 if (r.x < 0 || r.x + r.width > imagePyramid[0].cols || r.y < 0 ||
461 r.y + r.height > imagePyramid[0].rows)
464 int endX = r.x + r.width;
465 int endY = r.y + r.height;
466 uchar minV = 255, maxV = 0;
467 for (
int y = r.y; y < endY; y++)
469 const uchar *ptr = imagePyramid[0].ptr<uchar>(y);
470 for (
int x = r.x; x < endX; x++)
479 if ((maxV - minV) < 25)
482 _ref_inner3d.push_back(_inners3d[idx]);
483 _ref_inner2d.push_back(_inners2d[idx]);
490 if (_ref_inner3d.size() > 4)
496 #ifdef _fractal_debug_inners
498 cv::cvtColor(imagePyramid[0], InImageCopy, CV_GRAY2RGB);
501 std::vector<cv::Point2f> preinnersPrj;
504 cv::projectPoints(
_id_innerp3d[id_marker.first], _p_rvec, _p_tvec,
506 for (
auto pt : preinnersPrj)
507 cv::circle(InImageCopy, pt, 5, cv::Scalar(0, 0, 255), CV_FILLED);
511 for (
auto p : _ref_inner2d)
512 cv::circle(InImageCopy, p, 5, cv::Scalar(255, 0, 0), CV_FILLED);
516 cv::Rodrigues(
_rvec, rot);
518 std::vector<cv::Point3f> _inners;
519 for (
auto pt : _inner_corners_3d)
521 cv::Mat_<float> src(3, 1, rot.type());
526 cv::Mat cam_image_point = rot * src +
_tvec;
527 cam_image_point = cam_image_point / cv::norm(cam_image_point);
529 if (cam_image_point.at<
float>(2, 0) > 0.85f)
530 _inners.push_back(pt);
532 std::vector<cv::Point2f> _inners_prj;
537 for (
auto pt : _inners_prj)
538 cv::circle(InImageCopy, pt, 5, cv::Scalar(0, 255, 0), CV_FILLED);
540 cv::namedWindow(
"AA", cv::WINDOW_NORMAL);
541 imshow(
"AA", InImageCopy);
553 const std::vector<aruco::Marker> &vmarkers,
556 if (vmarkers.size() > 0)
560 std::vector<cv::Point2f> p2d;
561 std::vector<cv::Point3f> p3d;
562 for (
auto marker : vmarkers)
567 for (
auto p : marker)
597 std::vector<cv::Point2f> innerPoints2d;
598 std::vector<cv::Point3f> innerPoints3d;
602 cv::Rodrigues(
_rvec, rot);
607 std::vector<cv::Point2f> marker2d;
608 std::vector<cv::Point3f> marker3d;
611 cv::Mat_<double> src(3, 1, rot.type());
615 cv::Mat cam_image_point = rot * src +
_tvec;
616 cam_image_point = cam_image_point / cv::norm(cam_image_point);
618 if (cam_image_point.at<
double>(2, 0) > 0.85)
619 marker3d.push_back(pt);
624 if (marker3d.size() == 4)
630 cv::Point2f v01 = marker2d[1] - marker2d[0];
631 cv::Point2f v03 = marker2d[3] - marker2d[0];
632 float area1 = fabs(v01.x * v03.y - v01.y * v03.x);
633 cv::Point2f v21 = marker2d[1] - marker2d[2];
634 cv::Point2f v23 = marker2d[3] - marker2d[2];
635 float area2 = fabs(v21.x * v23.y - v21.y * v23.x);
636 double area = (area2 + area1) / 2.
f;
638 auto markerWarpSize =
640 float desiredarea = std::pow(
static_cast<float>(markerWarpSize), 2.
f);
641 if (area >= desiredarea)
644 innerPoints3d.push_back(pt);
648 radius = sqrt(area) / (sqrt(id_marker.second.nBits()) + 2.f);
653 innerPoints3d.push_back(pt);
661 if (innerPoints3d.size() > 0 && radius > 0)
669 if (!
ROI(markerDetector->getImagePyramid(), region, innerPoints2d, offset, ratio))
671 radius = radius * ratio;
675 std::cout <<
"radius: " << radius << std::endl;
678 #ifdef _fractal_debug_region
680 cv::cvtColor(region, out, CV_GRAY2RGB);
681 for (uint32_t i = 0; i < innerPoints2d.size(); i++)
682 circle(out, innerPoints2d[i], radius, cv::Scalar(0, 0, 255), 2);
683 cv::imshow(
"REGION ", out);
688 std::vector<cv::KeyPoint> kpoints;
689 cv::Ptr<cv::FastFeatureDetector> fd = cv::FastFeatureDetector::create();
690 fd->detect(region, kpoints);
694 if (kpoints.size() > 0)
706 #ifdef _fractal_debug_classification
712 kdtreeImg.
build(kpoints);
713 std::vector<std::pair<uint, std::vector<uint>>> inner_candidates;
714 for (uint idx = 0; idx < innerPoints2d.size(); idx++)
716 std::vector<std::pair<uint32_t, double>> res =
717 kdtreeImg.
radiusSearch(kpoints, innerPoints2d[idx], radius);
719 std::vector<uint> candidates;
722 if (kpoints[r.first].class_id ==
_innerkpoints[idx].class_id)
723 candidates.push_back(r.first);
725 if (candidates.size() > 0)
726 inner_candidates.push_back(make_pair(idx, candidates));
734 if (!bestModel.empty())
736 std::vector<cv::Point3f> p3d;
737 std::vector<cv::Point2f> p2d;
739 std::vector<cv::Point2f> pnts, pntsDst;
740 cv::KeyPoint::convert(kpoints, pnts);
741 perspectiveTransform(pnts, pntsDst, bestModel);
742 for (uint32_t
id = 0;
id < pntsDst.size();
id++)
744 std::vector<std::pair<uint32_t, double>> res =
750 uint32_t innerId = r.first;
751 double dist = sqrt(r.second);
755 res.erase(res.begin() + i);
763 cv::Point2f(kpoints[
id].pt.x + offset.x, kpoints[
id].pt.y + offset.y) / ratio);
795 std::vector<cv::Point2f> &innerPoints2d, cv::Point2f &offset,
799 cv::Rodrigues(
_rvec, rot);
802 std::vector<cv::Point2f> biggest_p2d;
803 std::vector<cv::Point3f> biggest_p3d;
810 cv::Mat_<double> src(3, 1, rot.type());
814 cv::Mat cam_image_point = rot * src +
_tvec;
815 cam_image_point = cam_image_point / cv::norm(cam_image_point);
817 if (cam_image_point.at<
double>(2, 0) > 0.85)
818 biggest_p3d.push_back(pt);
824 if (!biggest_p3d.empty())
830 std::vector<cv::Point2f> smallest_p2d;
831 std::vector<cv::Point3f> smallest_p3d;
833 for (
auto pt : marker_smallest.points)
834 smallest_p3d.push_back(pt);
839 cv::Point2f v01 = smallest_p2d[1] - smallest_p2d[0];
840 cv::Point2f v03 = smallest_p2d[3] - smallest_p2d[0];
841 float area1 = fabs(v01.x * v03.y - v01.y * v03.x);
842 cv::Point2f v21 = smallest_p2d[1] - smallest_p2d[2];
843 cv::Point2f v23 = smallest_p2d[3] - smallest_p2d[2];
844 float area2 = fabs(v21.x * v23.y - v21.y * v23.x);
845 double area = (area2 + area1) / 2.
f;
849 float border = sqrt(area) / sqrt(marker_smallest.nBits()) + 2;
850 int minX = imagePyramid[0].cols, minY = imagePyramid[0].rows, maxX = 0, maxY = 0;
851 for (
auto p : biggest_p2d)
866 if (maxX > imagePyramid[0].cols)
867 maxX = imagePyramid[0].cols;
868 if (maxY > imagePyramid[0].rows)
869 maxY = imagePyramid[0].rows;
873 size_t imgPyrIdx = 0;
874 auto markerWarpSize = (sqrt(marker_smallest.nBits()) + 2) * 10;
875 float desiredarea = std::pow(
static_cast<float>(markerWarpSize), 2.
f);
876 for (
size_t p = 1; p < imagePyramid.size(); p++)
878 if (area / pow(4, p) >= desiredarea)
884 ratio = float(imagePyramid[imgPyrIdx].cols) / float(imagePyramid[0].cols);
885 offset = cv::Point2i(minX, minY) * ratio;
886 cv::Rect region = cv::Rect(cv::Point2i(minX, minY) * ratio, cv::Point2i(maxX, maxY) * ratio);
887 img = imagePyramid[imgPyrIdx](region);
889 for (
auto &pt : innerPoints2d)
891 pt.x = pt.x * ratio - region.x;
892 pt.y = pt.y * ratio - region.y;
901 int ninners, std::vector<std::pair<uint, std::vector<uint>>> inner_kpnt,
902 std::vector<cv::KeyPoint> kpnts, uint32_t maxIter,
float _minInliers,
float _thresInliers)
904 std::vector<cv::Point2f> pnts;
905 cv::KeyPoint::convert(kpnts, pnts);
908 uint32_t numInliers = 4;
910 uint32_t thresInliers = uint32_t(ninners * _thresInliers);
912 uint32_t minInliers = uint32_t(ninners * _minInliers);
915 uint32_t bestInliers = 0;
917 cv::Mat bestH = cv::Mat();
924 for (
auto ik : inner_kpnt)
925 if (ik.second.size() > 0)
927 if (count < minInliers)
938 std::vector<std::pair<uint, uint>> inliers;
939 while (inliers.size() < numInliers && iter2++ < maxIter)
941 uint idx = rand() % inner_kpnt.size();
942 uint id_dst = inner_kpnt[idx].first;
944 uint idxc = rand() % inner_kpnt[idx].second.size();
945 uint id_src = inner_kpnt[idx].second[idxc];
949 for (
auto in : inliers)
951 if ((in.first != id_dst) && (in.second != id_src))
962 inliers.push_back(std::make_pair(id_dst, id_src));
965 if (iter2 >= maxIter)
968 std::vector<cv::Point2f> srcInliers;
969 std::vector<cv::Point2f> dstInliers;
970 for (
auto in : inliers)
973 srcInliers.push_back(kpnts[in.second].pt);
976 cv::Mat H = findHomography(srcInliers, dstInliers);
980 std::vector<std::pair<uint, uint>> newInliers;
981 std::vector<std::pair<uint, uint>> newInliers2;
983 std::vector<cv::Point2f> dstNewInliers;
984 std::vector<cv::Point2f> srcNewInliers;
985 std::vector<cv::Point2f> pntsTranf;
986 perspectiveTransform(pnts, pntsTranf, H);
988 for (uint idP = 0; idP < pntsTranf.size(); idP++)
990 std::vector<std::pair<uint32_t, double>> res =
996 uint32_t innerId = r.first;
997 double dist = sqrt(r.second);
1001 res.erase(res.begin() + i);
1010 for (
auto in : newInliers)
1012 if (in.first != res[0].first)
1023 if (
_innerkpoints[res[0].first].class_id == kpnts[idP].class_id)
1024 newInliers.push_back(std::make_pair(res[0].first, idP));
1028 if (newInliers.size() > bestInliers)
1030 bestInliers = newInliers.size();
1035 }
while (iter < maxIter && bestInliers < thresInliers);
1041 if (bestInliers < minInliers)
1048 bool text,
bool transf)
1053 for (
auto &k : kpoints)
1061 cv::cvtColor(image, out, CV_GRAY2BGR);
1065 for (
auto kp : kpoints)
1067 if (kp.class_id == -1)
1068 color = cv::Scalar(255, 0, 255);
1069 if (kp.class_id == 0)
1070 color = cv::Scalar(0, 0, 255);
1071 if (kp.class_id == 1)
1072 color = cv::Scalar(0, 255, 0);
1073 if (kp.class_id == 2)
1074 color = cv::Scalar(255, 0, 0);
1075 circle(out, kp.pt, 2, color, -1);
1081 for (
auto kp : kpoints)
1083 putText(out, std::to_string(nkm++), kp.pt, CV_FONT_HERSHEY_COMPLEX, 1,
1084 cv::Scalar(0, 0, 255), 2, 8);