33#include <visp3/core/vpConfig.h>
35#ifdef VISP_HAVE_APRILTAG
42#include <apriltag_pose.h>
43#include <common/homography.h>
49#include <tagCircle21h7.h>
50#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
51#include <tagCircle49h12.h>
52#include <tagCustom48h12.h>
53#include <tagStandard41h12.h>
54#include <tagStandard52h13.h>
56#include <tagAruco4x4_50.h>
57#include <tagAruco4x4_100.h>
58#include <tagAruco4x4_250.h>
59#include <tagAruco4x4_1000.h>
60#include <tagAruco5x5_50.h>
61#include <tagAruco5x5_100.h>
62#include <tagAruco5x5_250.h>
63#include <tagAruco5x5_1000.h>
64#include <tagAruco6x6_50.h>
65#include <tagAruco6x6_100.h>
66#include <tagAruco6x6_250.h>
67#include <tagAruco6x6_1000.h>
68#include <tagAruco7x7_50.h>
69#include <tagAruco7x7_100.h>
70#include <tagAruco7x7_250.h>
71#include <tagAruco7x7_1000.h>
72#include <tagAruco_MIP_36h12.h>
77#include <visp3/core/vpDisplay.h>
78#include <visp3/core/vpPixelMeterConversion.h>
79#include <visp3/core/vpPoint.h>
80#include <visp3/detection/vpDetectorAprilTag.h>
81#include <visp3/vision/vpPose.h>
85#ifndef DOXYGEN_SHOULD_SKIP_THIS
86class vpDetectorAprilTag::Impl
89 Impl(
const vpAprilTagFamily &tagFamily,
const vpPoseEstimationMethod &method)
90 : m_poseEstimationMethod(method), m_tagsId(), m_tagFamily(tagFamily), m_tagsDecisionMargin(),
91 m_tagsHammingDistance(), m_td(nullptr), m_tf(nullptr),
92 m_detections(nullptr), m_decisionMarginThreshold(-1), m_hammingDistanceThreshold(2), m_zAlignedWithCameraFrame(false)
94 switch (m_tagFamily) {
96 m_tf = tag36h11_create();
100 m_tf = tag36h10_create();
103 case TAG_36ARTOOLKIT:
107 m_tf = tag25h9_create();
111 m_tf = tag25h7_create();
115 m_tf = tag16h5_create();
119 m_tf = tagCircle21h7_create();
122 case TAG_CIRCLE49h12:
123#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
124 m_tf = tagCircle49h12_create();
128 case TAG_CUSTOM48h12:
129#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
130 m_tf = tagCustom48h12_create();
134 case TAG_STANDARD52h13:
135#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
136 m_tf = tagStandard52h13_create();
140 case TAG_STANDARD41h12:
141#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
142 m_tf = tagStandard41h12_create();
146 case TAG_ARUCO_4x4_50:
147 m_tf = tagAruco4x4_50_create();
150 case TAG_ARUCO_4x4_100:
151 m_tf = tagAruco4x4_100_create();
154 case TAG_ARUCO_4x4_250:
155 m_tf = tagAruco4x4_250_create();
158 case TAG_ARUCO_4x4_1000:
159 m_tf = tagAruco4x4_1000_create();
162 case TAG_ARUCO_5x5_50:
163 m_tf = tagAruco5x5_50_create();
166 case TAG_ARUCO_5x5_100:
167 m_tf = tagAruco5x5_100_create();
170 case TAG_ARUCO_5x5_250:
171 m_tf = tagAruco5x5_250_create();
174 case TAG_ARUCO_5x5_1000:
175 m_tf = tagAruco5x5_1000_create();
178 case TAG_ARUCO_6x6_50:
179 m_tf = tagAruco6x6_50_create();
182 case TAG_ARUCO_6x6_100:
183 m_tf = tagAruco6x6_100_create();
186 case TAG_ARUCO_6x6_250:
187 m_tf = tagAruco6x6_250_create();
190 case TAG_ARUCO_6x6_1000:
191 m_tf = tagAruco6x6_1000_create();
194 case TAG_ARUCO_7x7_50:
195 m_tf = tagAruco7x7_50_create();
198 case TAG_ARUCO_7x7_100:
199 m_tf = tagAruco7x7_100_create();
202 case TAG_ARUCO_7x7_250:
203 m_tf = tagAruco7x7_250_create();
206 case TAG_ARUCO_7x7_1000:
207 m_tf = tagAruco7x7_1000_create();
210 case TAG_ARUCO_MIP_36h12:
211 m_tf = tagArucoMIP_36h12_create();
218 if ((m_tagFamily != TAG_36ARTOOLKIT) && m_tf) {
219 m_td = apriltag_detector_create();
220 int bits_corrected = (m_tagFamily == TAG_ARUCO_4x4_1000) ? 1 : 2;
221 apriltag_detector_add_family(m_td, m_tf, bits_corrected);
229 : m_poseEstimationMethod(o.m_poseEstimationMethod), m_tagsId(o.m_tagsId), m_tagFamily(o.m_tagFamily),
230 m_tagsDecisionMargin(o.m_tagsDecisionMargin), m_tagsHammingDistance(o.m_tagsHammingDistance), m_td(nullptr),
231 m_tf(nullptr), m_detections(nullptr), m_decisionMarginThreshold(o.m_decisionMarginThreshold),
232 m_hammingDistanceThreshold(o.m_hammingDistanceThreshold), m_zAlignedWithCameraFrame(o.m_zAlignedWithCameraFrame)
234 switch (m_tagFamily) {
236 m_tf = tag36h11_create();
240 m_tf = tag36h10_create();
243 case TAG_36ARTOOLKIT:
247 m_tf = tag25h9_create();
251 m_tf = tag25h7_create();
255 m_tf = tag16h5_create();
259 m_tf = tagCircle21h7_create();
262 case TAG_CIRCLE49h12:
263#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
264 m_tf = tagCircle49h12_create();
268 case TAG_CUSTOM48h12:
269#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
270 m_tf = tagCustom48h12_create();
274 case TAG_STANDARD52h13:
275#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
276 m_tf = tagStandard52h13_create();
280 case TAG_STANDARD41h12:
281#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
282 m_tf = tagStandard41h12_create();
286 case TAG_ARUCO_4x4_50:
287 m_tf = tagAruco4x4_50_create();
290 case TAG_ARUCO_4x4_100:
291 m_tf = tagAruco4x4_100_create();
294 case TAG_ARUCO_4x4_250:
295 m_tf = tagAruco4x4_250_create();
298 case TAG_ARUCO_4x4_1000:
299 m_tf = tagAruco4x4_1000_create();
302 case TAG_ARUCO_5x5_50:
303 m_tf = tagAruco5x5_50_create();
306 case TAG_ARUCO_5x5_100:
307 m_tf = tagAruco5x5_100_create();
310 case TAG_ARUCO_5x5_250:
311 m_tf = tagAruco5x5_250_create();
314 case TAG_ARUCO_5x5_1000:
315 m_tf = tagAruco5x5_1000_create();
318 case TAG_ARUCO_6x6_50:
319 m_tf = tagAruco6x6_50_create();
322 case TAG_ARUCO_6x6_100:
323 m_tf = tagAruco6x6_100_create();
326 case TAG_ARUCO_6x6_250:
327 m_tf = tagAruco6x6_250_create();
330 case TAG_ARUCO_6x6_1000:
331 m_tf = tagAruco6x6_1000_create();
334 case TAG_ARUCO_7x7_50:
335 m_tf = tagAruco7x7_50_create();
338 case TAG_ARUCO_7x7_100:
339 m_tf = tagAruco7x7_100_create();
342 case TAG_ARUCO_7x7_250:
343 m_tf = tagAruco7x7_250_create();
346 case TAG_ARUCO_7x7_1000:
347 m_tf = tagAruco7x7_1000_create();
350 case TAG_ARUCO_MIP_36h12:
351 m_tf = tagArucoMIP_36h12_create();
358 if ((m_tagFamily != TAG_36ARTOOLKIT) && m_tf) {
359 m_td = apriltag_detector_copy(o.m_td);
360 int bits_corrected = (m_tagFamily == TAG_ARUCO_4x4_1000) ? 1 : 2;
361 apriltag_detector_add_family(m_td, m_tf, bits_corrected);
367 if (o.m_detections !=
nullptr) {
368 m_detections = apriltag_detections_copy(o.m_detections);
375 apriltag_detector_destroy(m_td);
379 switch (m_tagFamily) {
381 tag36h11_destroy(m_tf);
385 tag36h10_destroy(m_tf);
388 case TAG_36ARTOOLKIT:
392 tag25h9_destroy(m_tf);
396 tag25h7_destroy(m_tf);
400 tag16h5_destroy(m_tf);
404 tagCircle21h7_destroy(m_tf);
407 case TAG_CIRCLE49h12:
408#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
409 tagCustom48h12_destroy(m_tf);
413 case TAG_CUSTOM48h12:
414#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
415 tagCustom48h12_destroy(m_tf);
419 case TAG_STANDARD52h13:
420#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
421 tagStandard52h13_destroy(m_tf);
425 case TAG_STANDARD41h12:
426#if defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
427 tagStandard41h12_destroy(m_tf);
431 case TAG_ARUCO_4x4_50:
432 tagAruco4x4_50_destroy(m_tf);
435 case TAG_ARUCO_4x4_100:
436 tagAruco4x4_100_destroy(m_tf);
439 case TAG_ARUCO_4x4_250:
440 tagAruco4x4_250_destroy(m_tf);
443 case TAG_ARUCO_4x4_1000:
444 tagAruco4x4_1000_destroy(m_tf);
447 case TAG_ARUCO_5x5_50:
448 tagAruco5x5_50_destroy(m_tf);
451 case TAG_ARUCO_5x5_100:
452 tagAruco5x5_100_destroy(m_tf);
455 case TAG_ARUCO_5x5_250:
456 tagAruco5x5_250_destroy(m_tf);
459 case TAG_ARUCO_5x5_1000:
460 tagAruco5x5_1000_destroy(m_tf);
463 case TAG_ARUCO_6x6_50:
464 tagAruco6x6_50_destroy(m_tf);
467 case TAG_ARUCO_6x6_100:
468 tagAruco6x6_100_destroy(m_tf);
471 case TAG_ARUCO_6x6_250:
472 tagAruco6x6_250_destroy(m_tf);
475 case TAG_ARUCO_6x6_1000:
476 tagAruco6x6_1000_destroy(m_tf);
479 case TAG_ARUCO_7x7_50:
480 tagAruco7x7_50_destroy(m_tf);
483 case TAG_ARUCO_7x7_100:
484 tagAruco7x7_100_destroy(m_tf);
487 case TAG_ARUCO_7x7_250:
488 tagAruco7x7_250_destroy(m_tf);
491 case TAG_ARUCO_7x7_1000:
492 tagAruco7x7_1000_destroy(m_tf);
495 case TAG_ARUCO_MIP_36h12:
496 tagArucoMIP_36h12_destroy(m_tf);
505 apriltag_detections_destroy(m_detections);
506 m_detections =
nullptr;
510 void convertHomogeneousMatrix(
const apriltag_pose_t &pose, vpHomogeneousMatrix &cMo)
512 const unsigned int val_3 = 3;
513 for (
unsigned int i = 0;
i < val_3; ++
i) {
514 for (
unsigned int j = 0;
j < val_3; ++
j) {
515 cMo[
i][
j] = MATD_EL(pose.R, i, j);
517 cMo[
i][val_3] = MATD_EL(pose.t, i, 0);
521 bool detect(
const vpImage<unsigned char> &I,
double tagSize,
const vpCameraParameters &cam,
522 std::vector<std::vector<vpImagePoint> > &polygons, std::vector<std::string> &messages,
bool displayTag,
523 const vpColor color,
unsigned int thickness, std::vector<vpHomogeneousMatrix> *cMo_vec,
524 std::vector<vpHomogeneousMatrix> *cMo_vec2, std::vector<double> *projErrors,
525 std::vector<double> *projErrors2)
527 if (m_tagFamily == TAG_36ARTOOLKIT) {
529 std::cerr <<
"TAG_36ARTOOLKIT detector is not available anymore." << std::endl;
532#if !defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
533 if ((m_tagFamily == TAG_CIRCLE49h12) || (m_tagFamily == TAG_CUSTOM48h12) || (m_tagFamily == TAG_STANDARD41h12) ||
534 (m_tagFamily == TAG_STANDARD52h13)) {
535 std::cerr <<
"TAG_CIRCLE49h12, TAG_CUSTOM48h12, TAG_STANDARD41h12 and TAG_STANDARD52h13 are disabled."
541 const bool computePose = (cMo_vec !=
nullptr);
543 image_u8_t im = {
static_cast<int32_t
>(I.
getWidth()),
549 apriltag_detections_destroy(m_detections);
550 m_detections =
nullptr;
553 m_detections = apriltag_detector_detect(m_td, &im);
554 int nb_detections = zarray_size(m_detections);
555 bool detected = nb_detections > 0;
557 polygons.clear(); messages.clear(); m_tagsId.clear(); m_tagsDecisionMargin.clear(); m_tagsHammingDistance.clear();
558 polygons.reserve(
static_cast<size_t>(nb_detections));
559 messages.reserve(
static_cast<size_t>(nb_detections));
560 m_tagsId.reserve(
static_cast<size_t>(nb_detections));
561 m_tagsDecisionMargin.reserve(
static_cast<size_t>(nb_detections));
562 m_tagsHammingDistance.reserve(
static_cast<size_t>(nb_detections));
564 int zarray_size_m_detections = zarray_size(m_detections);
565 for (
int i = 0;
i < zarray_size_m_detections; ++
i) {
566 apriltag_detection_t *det;
567 zarray_get(m_detections, i, &det);
569 if (m_decisionMarginThreshold > 0) {
570 if (det->decision_margin < m_decisionMarginThreshold) {
574 if (det->hamming > m_hammingDistanceThreshold) {
578 std::vector<vpImagePoint> polygon;
579 const int polygonSize = 4;
580 for (
int j = 0;
j < polygonSize; ++
j) {
581 polygon.push_back(vpImagePoint(det->p[j][1], det->p[j][0]));
583 polygons.push_back(polygon);
584 std::stringstream ss;
585 ss << m_tagFamily <<
" id: " << det->id;
586 messages.push_back(ss.str());
587 m_tagsId.push_back(det->id);
588 m_tagsDecisionMargin.push_back(det->decision_margin);
589 m_tagsHammingDistance.push_back(det->hamming);
597 const unsigned int polyId0 = 0, polyId1 = 1, polyId2 = 2, polyId3 = 3;
599 static_cast<int>(det->p[polyId1][1]),
static_cast<int>(det->p[polyId1][0]), Ox, thickness);
601 static_cast<int>(det->p[polyId3][1]),
static_cast<int>(det->p[polyId3][0]), Oy, thickness);
603 static_cast<int>(det->p[polyId2][1]),
static_cast<int>(det->p[polyId2][0]), Ox2, thickness);
605 static_cast<int>(det->p[polyId3][1]),
static_cast<int>(det->p[polyId3][0]), Oy2, thickness);
609 vpHomogeneousMatrix
cMo, cMo2;
611 if (getPose(
static_cast<size_t>(i), tagSize, cam, cMo, cMo_vec2 ? &cMo2 :
nullptr, projErrors ? &err1 :
nullptr,
612 projErrors2 ? &err2 :
nullptr)) {
613 cMo_vec->push_back(cMo);
615 cMo_vec2->push_back(cMo2);
618 projErrors->push_back(err1);
621 projErrors2->push_back(err2);
631 void displayFrames(
const vpImage<unsigned char> &I,
const std::vector<vpHomogeneousMatrix> &cMo_vec,
632 const vpCameraParameters &cam,
double size,
const vpColor &color,
unsigned int thickness)
const
634 size_t cmo_vec_size = cMo_vec.size();
635 for (
size_t i = 0;
i < cmo_vec_size; ++
i) {
636 const vpHomogeneousMatrix &
cMo = cMo_vec[
i];
641 void displayFrames(
const vpImage<vpRGBa> &I,
const std::vector<vpHomogeneousMatrix> &cMo_vec,
642 const vpCameraParameters &cam,
double size,
const vpColor &color,
unsigned int thickness)
const
644 size_t cmo_vec_size = cMo_vec.size();
645 for (
size_t i = 0;
i < cmo_vec_size; ++
i) {
646 const vpHomogeneousMatrix &
cMo = cMo_vec[
i];
651 void displayTags(
const vpImage<unsigned char> &I,
const std::vector<std::vector<vpImagePoint> > &tagsCorners,
652 const vpColor &color,
unsigned int thickness)
const
654 size_t tagscorners_size = tagsCorners.size();
655 for (
size_t i = 0;
i < tagscorners_size; ++
i) {
661 const std::vector<vpImagePoint> &corners = tagsCorners[
i];
662 assert(corners.size() == 4);
663 const unsigned int cornerId0 = 0, cornerId1 = 1, cornerId2 = 2, cornerId3 = 3;
665 unsigned int rect_size =
static_cast<unsigned int>(line0_length_8);
666 if (line0_length_8 < 2) {
671 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId0].get_i()),
static_cast<int>(corners[cornerId0].get_j()),
672 static_cast<int>(corners[cornerId1].get_i()),
static_cast<int>(corners[cornerId1].get_j()), Ox, thickness);
673 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId0].get_i()),
static_cast<int>(corners[cornerId0].get_j()),
674 static_cast<int>(corners[cornerId3].get_i()),
static_cast<int>(corners[cornerId3].get_j()), Oy, thickness);
675 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId1].get_i()),
static_cast<int>(corners[cornerId1].get_j()),
676 static_cast<int>(corners[cornerId2].get_i()),
static_cast<int>(corners[cornerId2].get_j()), Ox2, thickness);
677 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId2].get_i()),
static_cast<int>(corners[cornerId2].get_j()),
678 static_cast<int>(corners[cornerId3].get_i()),
static_cast<int>(corners[cornerId3].get_j()), Oy2, thickness);
682 void displayTags(
const vpImage<vpRGBa> &I,
const std::vector<std::vector<vpImagePoint> > &tagsCorners,
683 const vpColor &color,
unsigned int thickness)
const
685 size_t tagscorners_size = tagsCorners.size();
686 for (
size_t i = 0;
i < tagscorners_size; ++
i) {
692 const std::vector<vpImagePoint> &corners = tagsCorners[
i];
693 assert(corners.size() == 4);
694 const unsigned int cornerId0 = 0, cornerId1 = 1, cornerId2 = 2, cornerId3 = 3;
696 unsigned int rect_size =
static_cast<unsigned int>(line0_length_8);
697 if (line0_length_8 < 2) {
702 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId0].get_i()),
static_cast<int>(corners[cornerId0].get_j()),
703 static_cast<int>(corners[cornerId1].get_i()),
static_cast<int>(corners[cornerId1].get_j()), Ox, thickness);
704 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId0].get_i()),
static_cast<int>(corners[cornerId0].get_j()),
705 static_cast<int>(corners[cornerId3].get_i()),
static_cast<int>(corners[cornerId3].get_j()), Oy, thickness);
706 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId1].get_i()),
static_cast<int>(corners[cornerId1].get_j()),
707 static_cast<int>(corners[cornerId2].get_i()),
static_cast<int>(corners[cornerId2].get_j()), Ox2, thickness);
708 vpDisplay::displayLine(I,
static_cast<int>(corners[cornerId2].get_i()),
static_cast<int>(corners[cornerId2].get_j()),
709 static_cast<int>(corners[cornerId3].get_i()),
static_cast<int>(corners[cornerId3].get_j()), Oy2, thickness);
720 bool getTagImage(vpImage<unsigned char> &I,
int id)
722 if (
id >=
static_cast<int>(m_tf->ncodes)) {
723 std::cerr <<
"Cannot get tag image with id " <<
id <<
" for family " << m_tagFamily << std::endl;
726 image_u8_t *img_8u = apriltag_to_image(m_tf,
id);
728 I.
init(img_8u->height, img_8u->width);
729 for (
int i = 0;
i < img_8u->height;
i++) {
730 for (
int j = 0;
j < img_8u->width;
j++) {
731 I[
i][
j] = img_8u->buf[
i*img_8u->stride +
j];
735 image_u8_destroy(img_8u);
739 bool getPose(
size_t tagIndex,
double tagSize,
const vpCameraParameters &cam, vpHomogeneousMatrix &cMo,
740 vpHomogeneousMatrix *cMo2,
double *projErrors,
double *projErrors2)
742 if (m_detections ==
nullptr) {
745 if (m_tagFamily == TAG_36ARTOOLKIT) {
747 std::cerr <<
"TAG_36ARTOOLKIT detector is not available anymore." << std::endl;
750#if !defined(VISP_HAVE_APRILTAG_BIG_FAMILY)
751 if ((m_tagFamily == TAG_CIRCLE49h12) || (m_tagFamily == TAG_CUSTOM48h12) || (m_tagFamily == TAG_STANDARD41h12) ||
752 (m_tagFamily == TAG_STANDARD52h13)) {
753 std::cerr <<
"TAG_CIRCLE49h12, TAG_CUSTOM48h12, TAG_STANDARD41h12 and TAG_STANDARD52h13 are disabled."
759 apriltag_detection_t *det;
760 zarray_get(m_detections,
static_cast<int>(tagIndex), &det);
762 int nb_detections = zarray_size(m_detections);
763 if (tagIndex >=
static_cast<size_t>(nb_detections)) {
773 vpHomogeneousMatrix cMo_homography_ortho_iter;
774 if ((m_poseEstimationMethod == HOMOGRAPHY_ORTHOGONAL_ITERATION) ||
775 (m_poseEstimationMethod == BEST_RESIDUAL_VIRTUAL_VS)) {
776 double fx =
cam.get_px(), fy =
cam.get_py();
777 double cx =
cam.get_u0(), cy =
cam.get_v0();
779 apriltag_detection_info_t info;
781 info.tagsize = tagSize;
788 getPoseWithOrthogonalMethod(info, cMo, cMo2, projErrors, projErrors2);
789 cMo_homography_ortho_iter =
cMo;
792 vpHomogeneousMatrix cMo_homography;
793 if ((m_poseEstimationMethod == HOMOGRAPHY) || (m_poseEstimationMethod == HOMOGRAPHY_VIRTUAL_VS) ||
794 (m_poseEstimationMethod == BEST_RESIDUAL_VIRTUAL_VS)) {
795 double fx =
cam.get_px(), fy =
cam.get_py();
796 double cx =
cam.get_u0(), cy =
cam.get_v0();
798 apriltag_detection_info_t info;
800 info.tagsize = tagSize;
806 apriltag_pose_t pose;
807 estimate_pose_for_tag_homography(&info, &pose);
808 convertHomogeneousMatrix(pose, cMo);
810 matd_destroy(pose.R);
811 matd_destroy(pose.t);
813 cMo_homography =
cMo;
821 double x = 0.0,
y = 0.0;
822 std::vector<vpPoint> pts(4);
824 imPt.
set_uv(det->p[0][0], det->p[0][1]);
831 imPt.
set_uv(det->p[1][0], det->p[1][1]);
837 const int idCorner2 = 2;
839 imPt.
set_uv(det->p[idCorner2][0], det->p[idCorner2][1]);
845 const int idCorner3 = 3;
847 imPt.
set_uv(det->p[idCorner3][0], det->p[idCorner3][1]);
855 if ((m_poseEstimationMethod != HOMOGRAPHY) && (m_poseEstimationMethod != HOMOGRAPHY_VIRTUAL_VS) &&
856 (m_poseEstimationMethod != HOMOGRAPHY_ORTHOGONAL_ITERATION)) {
857 if (m_poseEstimationMethod == BEST_RESIDUAL_VIRTUAL_VS) {
858 vpHomogeneousMatrix cMo_dementhon, cMo_lagrange;
860 double residual_dementhon = std::numeric_limits<double>::max(),
861 residual_lagrange = std::numeric_limits<double>::max();
863 double residual_homography_ortho_iter = pose.
computeResidual(cMo_homography_ortho_iter);
873 std::vector<double> residuals;
874 residuals.push_back(residual_dementhon);
875 residuals.push_back(residual_lagrange);
876 residuals.push_back(residual_homography);
877 residuals.push_back(residual_homography_ortho_iter);
878 std::vector<vpHomogeneousMatrix> poses;
879 poses.push_back(cMo_dementhon);
880 poses.push_back(cMo_lagrange);
881 poses.push_back(cMo_homography);
882 poses.push_back(cMo_homography_ortho_iter);
884 std::ptrdiff_t minIndex = std::min_element(residuals.begin(), residuals.end()) - residuals.begin();
885 cMo = *(poses.begin() + minIndex);
888 pose.
computePose(m_mapOfCorrespondingPoseMethods[m_poseEstimationMethod], cMo);
892 if ((m_poseEstimationMethod != HOMOGRAPHY) && (m_poseEstimationMethod != HOMOGRAPHY_ORTHOGONAL_ITERATION)) {
898 if (m_poseEstimationMethod != HOMOGRAPHY_ORTHOGONAL_ITERATION) {
900 double scale = tagSize / 2.0;
901 double data_p0[] = { -scale, scale, 0 };
902 double data_p1[] = { scale, scale, 0 };
903 double data_p2[] = { scale, -scale, 0 };
904 double data_p3[] = { -scale, -scale, 0 };
905 const unsigned int nbPoints = 4;
906 const int nbRows = 3;
907 matd_t *
p[nbPoints] = { matd_create_data(nbRows, 1, data_p0), matd_create_data(nbRows, 1, data_p1),
908 matd_create_data(nbRows, 1, data_p2), matd_create_data(nbRows, 1, data_p3) };
910 for (
unsigned int i = 0;
i < nbPoints; ++
i) {
911 double data_v[] = { (det->p[
i][0] -
cam.get_u0()) /
cam.get_px(), (det->p[
i][1] -
cam.get_v0()) /
cam.get_py(),
913 v[
i] = matd_create_data(nbRows, 1, data_v);
916 apriltag_pose_t solution1, solution2;
917 const int nIters = 50;
918 const int nbCols = 3;
919 solution1.R = matd_create_data(nbRows, nbCols,
cMo.getRotationMatrix().data);
920 solution1.t = matd_create_data(nbRows, 1,
cMo.getTranslationVector().data);
923 get_second_solution(v, p, &solution1, &solution2, nIters, &err2);
925 for (
unsigned int i = 0;
i < nbPoints; ++
i) {
931 convertHomogeneousMatrix(solution2, *cMo2);
933 matd_destroy(solution2.R);
934 matd_destroy(solution2.t);
937 matd_destroy(solution1.R);
938 matd_destroy(solution1.t);
946 if (projErrors2 && cMo2) {
950 if (!m_zAlignedWithCameraFrame) {
951 const unsigned int idX = 0, idY = 1, idZ = 2;
952 vpHomogeneousMatrix oMo;
972 void getPoseWithOrthogonalMethod(apriltag_detection_info_t &info, vpHomogeneousMatrix &cMo1,
973 vpHomogeneousMatrix *cMo2,
double *err1,
double *err2)
975 apriltag_pose_t pose1, pose2;
977 const unsigned int nbIters = 50;
978 estimate_tag_pose_orthogonal_iteration(&info, &err_1, &pose1, &err_2, &pose2, nbIters);
979 if (err_1 <= err_2) {
980 convertHomogeneousMatrix(pose1, cMo1);
983 convertHomogeneousMatrix(pose2, *cMo2);
991 convertHomogeneousMatrix(pose2, cMo1);
993 convertHomogeneousMatrix(pose1, *cMo2);
997 matd_destroy(pose1.R);
998 matd_destroy(pose1.t);
1000 matd_destroy(pose2.t);
1002 matd_destroy(pose2.R);
1012 bool getZAlignedWithCameraAxis() {
return m_zAlignedWithCameraFrame; }
1014 float getAprilTagDecisionMarginThreshold()
const
1016 return m_decisionMarginThreshold;
1019 int getAprilTagHammingDistanceThreshold()
const
1021 return m_hammingDistanceThreshold;
1024 bool getAprilTagDecodeSharpening(
double &decodeSharpening)
const
1027 decodeSharpening = m_td->decode_sharpening;
1033 bool getNbThreads(
int &nThreads)
const
1036 nThreads = m_td->nthreads;
1042 bool getQuadDecimate(
float &quadDecimate)
const
1045 quadDecimate = m_td->quad_decimate;
1051 bool getQuadSigma(
float &quadSigma)
const
1054 quadSigma = m_td->quad_sigma;
1060 bool getRefineEdges(
bool &refineEdges)
const
1063 refineEdges = (m_td->refine_edges ? true :
false);
1069 bool getZAlignedWithCameraAxis()
const {
return m_zAlignedWithCameraFrame; }
1071 std::vector<float> getTagsDecisionMargin()
const {
return m_tagsDecisionMargin; }
1073 std::vector<int> getTagsHammingDistance()
const {
return m_tagsHammingDistance; }
1075 std::vector<int> getTagsId()
const {
return m_tagsId; }
1077 void setAprilTagDecisionMarginThreshold(
float decisionMarginThreshold)
1079 m_decisionMarginThreshold = decisionMarginThreshold;
1082 void setAprilTagHammingDistanceThreshold(
int hammingDistanceThreshold)
1084 m_hammingDistanceThreshold = hammingDistanceThreshold;
1087 void setAprilTagDecodeSharpening(
double decodeSharpening)
1090 m_td->decode_sharpening = decodeSharpening;
1094 void setDebugFlag(
bool flag)
1101 void setNbThreads(
int nThreads)
1104 m_td->nthreads = nThreads;
1108 void setQuadDecimate(
float quadDecimate)
1111 m_td->quad_decimate = quadDecimate;
1115 void setQuadSigma(
float quadSigma)
1118 m_td->quad_sigma = quadSigma;
1122 void setRefineDecode(
bool) { }
1124 void setRefineEdges(
bool refineEdges)
1127 m_td->refine_edges = (refineEdges ? true :
false);
1131 void setRefinePose(
bool) { }
1133 void setPoseEstimationMethod(
const vpPoseEstimationMethod &method)
1135 m_poseEstimationMethod = method;
1138 void setZAlignedWithCameraAxis(
bool zAlignedWithCameraFrame)
1140 m_zAlignedWithCameraFrame = zAlignedWithCameraFrame;
1143 bool isZAlignedWithCameraAxis()
const
1145 return m_zAlignedWithCameraFrame;
1149 std::map<vpPoseEstimationMethod, vpPose::vpPoseMethodType> m_mapOfCorrespondingPoseMethods;
1150 vpPoseEstimationMethod m_poseEstimationMethod;
1151 std::vector<int> m_tagsId;
1152 vpAprilTagFamily m_tagFamily;
1153 std::vector<float> m_tagsDecisionMargin;
1154 std::vector<int> m_tagsHammingDistance;
1155 apriltag_detector_t *m_td;
1156 apriltag_family_t *m_tf;
1157 zarray_t *m_detections;
1158 float m_decisionMarginThreshold;
1159 int m_hammingDistanceThreshold;
1160 bool m_zAlignedWithCameraFrame;
1165const unsigned int def_tagThickness = 2;
1173 m_impl(new Impl(tagFamily, poseEstimationMethod))
1179 m_impl(new Impl(*o.m_impl))
1203 std::vector<vpHomogeneousMatrix> cMo_vec;
1204 const double tagSize = 1.0;
1230 std::vector<vpHomogeneousMatrix> &cMo_vec, std::vector<vpHomogeneousMatrix> *cMo_vec2,
1231 std::vector<double> *projErrors, std::vector<double> *projErrors2)
1261 m_impl->displayFrames(I, cMo_vec, cam, size, color, thickness);
1277 m_impl->displayFrames(I, cMo_vec, cam, size, color, thickness);
1289 const vpColor &color,
unsigned int thickness)
const
1291 m_impl->displayTags(I, tagsCorners, color, thickness);
1303 const vpColor &color,
unsigned int thickness)
const
1305 m_impl->displayTags(I, tagsCorners, color, thickness);
1343 return m_impl->getPose(tagIndex, tagSize, cam, cMo, cMo2, projError, projError2);
1364 const std::map<int, double> &tagsSize)
const
1366 std::vector<std::vector<vpPoint> > tagsPoints3D;
1368 double default_size = -1;
1370 std::map<int, double>::const_iterator it = tagsSize.find(-1);
1371 if (it != tagsSize.end()) {
1372 default_size = it->second;
1375 size_t tagsid_size = tagsId.size();
1376 for (
size_t i = 0; i < tagsid_size; ++i) {
1377 it = tagsSize.find(tagsId[i]);
1378 double tagSize = default_size;
1379 if (it == tagsSize.end()) {
1380 if (default_size < 0) {
1382 "Tag with id %d has no 3D size or there is no default 3D size defined", tagsId[i]));
1386 tagSize = it->second;
1388 std::vector<vpPoint> points3D(4);
1389 const unsigned int idX = 0, idY = 1, idZ = 2, idHomogen = 3;
1390 const double middleFactor = 2.0;
1391 if (m_impl->getZAlignedWithCameraAxis()) {
1392 points3D[idX] =
vpPoint(-tagSize / middleFactor, tagSize / middleFactor, 0);
1393 points3D[idY] =
vpPoint(tagSize / middleFactor, tagSize / middleFactor, 0);
1394 points3D[idZ] =
vpPoint(tagSize / middleFactor, -tagSize / middleFactor, 0);
1395 points3D[idHomogen] =
vpPoint(-tagSize / middleFactor, -tagSize / middleFactor, 0);
1398 points3D[idX] =
vpPoint(-tagSize / middleFactor, -tagSize / middleFactor, 0);
1399 points3D[idY] =
vpPoint(tagSize / middleFactor, -tagSize / middleFactor, 0);
1400 points3D[idZ] =
vpPoint(tagSize / middleFactor, tagSize / middleFactor, 0);
1401 points3D[idHomogen] =
vpPoint(-tagSize / middleFactor, tagSize / middleFactor, 0);
1403 tagsPoints3D.push_back(points3D);
1406 return tagsPoints3D;
1419 return m_impl->getAprilTagDecisionMarginThreshold();
1437 return m_impl->getAprilTagHammingDistanceThreshold();
1464 m_impl->setDebugFlag(flag);
1476 return m_impl->getTagImage(I,
id);
1511 return m_impl->setAprilTagDecodeSharpening(decodeSharpening);
1539 m_impl->setAprilTagDecisionMarginThreshold(decisionMarginThreshold);
1565 assert((hammingDistanceThreshold > -1) && (hammingDistanceThreshold < 256));
1566 m_impl->setAprilTagHammingDistanceThreshold(hammingDistanceThreshold);
1572 double decodeSharpening = 0.25;
1573 m_impl->getAprilTagDecodeSharpening(decodeSharpening);
1575 m_impl->getNbThreads(nThreads);
1576 float quadDecimate = 1;
1577 m_impl->getQuadDecimate(quadDecimate);
1578 float quadSigma = 0;
1579 m_impl->getQuadSigma(quadSigma);
1580 bool refineEdges =
true;
1581 m_impl->getRefineEdges(refineEdges);
1582 bool zAxis = m_impl->getZAlignedWithCameraAxis();
1586 m_impl->setAprilTagDecodeSharpening(decodeSharpening);
1587 m_impl->setNbThreads(nThreads);
1588 m_impl->setQuadDecimate(quadDecimate);
1589 m_impl->setQuadSigma(quadSigma);
1590 m_impl->setRefineEdges(refineEdges);
1591 m_impl->setZAlignedWithCameraAxis(zAxis);
1602 m_impl->setNbThreads(nThreads);
1614 m_impl->setPoseEstimationMethod(poseEstimationMethod);
1645#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
1651 m_impl->setRefineDecode(refineDecode);
1671#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
1689 m_impl->setZAlignedWithCameraAxis(zAlignedWithCameraFrame);
1698 return m_impl->isZAlignedWithCameraAxis();
1701#elif !defined(VISP_BUILD_SHARED_LIBS)
1704void dummy_vpDetectorAprilTag() { }
Generic class defining intrinsic camera parameters.
Class to define RGB colors available for display functionalities.
static const vpColor none
static const vpColor blue
static const vpColor yellow
static const vpColor green
std::vector< float > getTagsDecisionMargin() const
void setZAlignedWithCameraAxis(bool zAlignedWithCameraFrame)
std::vector< std::vector< vpImagePoint > > getTagsCorners() const
void setAprilTagQuadDecimate(float quadDecimate)
friend void swap(vpDetectorAprilTag &o1, vpDetectorAprilTag &o2)
void setAprilTagHammingDistanceThreshold(int hammingDistanceThreshold)
void displayFrames(const vpImage< unsigned char > &I, const std::vector< vpHomogeneousMatrix > &cMo_vec, const vpCameraParameters &cam, double size, const vpColor &color, unsigned int thickness=1) const
vpDetectorAprilTag & operator=(vpDetectorAprilTag o)
std::vector< std::vector< vpPoint > > getTagsPoints3D(const std::vector< int > &tagsId, const std::map< int, double > &tagsSize) const
vpDetectorAprilTag(const vpAprilTagFamily &tagFamily=TAG_36h11, const vpPoseEstimationMethod &poseEstimationMethod=HOMOGRAPHY_VIRTUAL_VS)
bool detect(const vpImage< unsigned char > &I) VP_OVERRIDE
unsigned int m_displayTagThickness
std::vector< int > getTagsHammingDistance() const
vpAprilTagFamily m_tagFamily
void setAprilTagRefineEdges(bool refineEdges)
bool isZAlignedWithCameraAxis() const
vpPoseEstimationMethod m_poseEstimationMethod
VP_DEPRECATED void setAprilTagRefinePose(bool refinePose)
void setAprilTagQuadSigma(float quadSigma)
void setAprilTagDebugOption(bool flag)
void setAprilTagNbThreads(int nThreads)
VP_DEPRECATED void setAprilTagRefineDecode(bool refineDecode)
void setAprilTagPoseEstimationMethod(const vpPoseEstimationMethod &poseEstimationMethod)
void setAprilTagDecisionMarginThreshold(float decisionMarginThreshold)
std::vector< int > getTagsId() const
bool getTagImage(vpImage< unsigned char > &I, int id)
bool getPose(size_t tagIndex, double tagSize, const vpCameraParameters &cam, vpHomogeneousMatrix &cMo, vpHomogeneousMatrix *cMo2=nullptr, double *projError=nullptr, double *projError2=nullptr)
float getAprilTagDecisionMarginThreshold() const
void displayTags(const vpImage< unsigned char > &I, const std::vector< std::vector< vpImagePoint > > &tagsCorners, const vpColor &color=vpColor::none, unsigned int thickness=1) const
void setAprilTagFamily(const vpAprilTagFamily &tagFamily)
virtual ~vpDetectorAprilTag() VP_OVERRIDE
vpColor m_displayTagColor
int getAprilTagHammingDistanceThreshold() const
void setAprilTagDecodeSharpening(double decodeSharpening)
std::vector< std::string > m_message
Message attached to each object.
std::vector< std::vector< vpImagePoint > > m_polygon
For each object, defines the polygon that contains the object.
size_t m_nb_objects
Number of detected objects.
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
static void displayFrame(const vpImage< unsigned char > &I, const vpHomogeneousMatrix &cMo, const vpCameraParameters &cam, double size, const vpColor &color=vpColor::none, unsigned int thickness=1, const vpImagePoint &offset=vpImagePoint(0, 0), const std::string &frameName="", const vpColor &textColor=vpColor::black, const vpImagePoint &textOffset=vpImagePoint(15, 15))
static void displayRectangle(const vpImage< unsigned char > &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color, bool fill=false, unsigned int thickness=1)
error that can be emitted by ViSP classes.
Implementation of an homogeneous matrix and operations on such kind of matrices.
static double distance(const vpImagePoint &iP1, const vpImagePoint &iP2)
void set_uv(double u, double v)
Definition of the vpImage class member functions.
void init(unsigned int height, unsigned int width)
Set the size of the image.
unsigned int getWidth() const
Type * bitmap
points toward the bitmap
unsigned int getHeight() const
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
void set_x(double x)
Set the point x coordinate in the image plane.
void setWorldCoordinates(double oX, double oY, double oZ)
void set_y(double y)
Set the point y coordinate in the image plane.
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, FuncCheckValidityPose func=nullptr)
void addPoints(const std::vector< vpPoint > &lP)
double computeResidual(const vpHomogeneousMatrix &cMo) const
Compute and return the sum of squared residuals expressed in meter^2 for the pose matrix cMo.