Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
visp-compute-apriltag-poses.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Compute apriltag poses to prepare hand-eye calibration.
32 */
33
35#include <iostream>
36
37#include <visp3/core/vpConfig.h>
38
39#if defined(VISP_HAVE_PUGIXML)
40
41#include <visp3/core/vpIoTools.h>
42#include <visp3/core/vpXmlParserCamera.h>
43#include <visp3/detection/vpDetectorAprilTag.h>
44#include <visp3/gui/vpDisplayFactory.h>
45#include <visp3/io/vpVideoReader.h>
46
47#if defined(ENABLE_VISP_NAMESPACE)
48using namespace VISP_NAMESPACE_NAME;
49#endif
50
51void usage(const char **argv, int error)
52{
53 std::cout << "Synopsis" << std::endl
54 << " " << argv[0]
55 << " [--tag-size <size>]"
56 << " [--tag-z-aligned]"
57 << " [--input <images filename>]"
58 << " [--intrinsic <xml file>]"
59 << " [--camera-name <name>]"
60 << " [--output <poses filename>]"
61#if defined(VISP_HAVE_MODULE_GUI)
62 << " [--no-interactive]"
63#endif
64 << " [--help, -h]" << std::endl
65 << std::endl;
66 std::cout << "Description" << std::endl
67 << " Compute the pose of a 36h11 Apriltag in a sequence of images." << std::endl
68 << " Consider that only one tag is present in each image." << std::endl
69 << std::endl
70 << " --tag-size <size>" << std::endl
71 << " Apriltag size in [m]." << std::endl
72 << " Default: 0.03" << std::endl
73 << std::endl
74 << " --tag-z-aligned" << std::endl
75 << " When enabled, tag z-axis and camera z-axis are aligned." << std::endl
76 << " Default: false" << std::endl
77 << std::endl
78 << " --input <images filename>" << std::endl
79 << " Generic name of the images to process." << std::endl
80 << " Default: empty" << std::endl
81 << " Example: \"image-%d.jpg\"" << std::endl
82 << std::endl
83 << " --intrinsic <xml file>" << std::endl
84 << " XML file that contains camera intrinsic parameters. " << std::endl
85 << " Default: \"camera.xml\"" << std::endl
86 << std::endl
87 << " --camera-name <name>" << std::endl
88 << " Camera name in the XML file that contains camera intrinsic parameters." << std::endl
89 << " Default: \"Camera\"" << std::endl
90 << std::endl
91 << " --output <poses filename>" << std::endl
92 << " Generic name of the yaml files that contains the resulting tag poses." << std::endl
93 << " Default: \"pose_cPo_%d.yaml\"" << std::endl
94 << std::endl
95#if defined(VISP_HAVE_MODULE_GUI)
96 << " --no-interactive" << std::endl
97 << " To compute the tag poses without interactive validation by the user." << std::endl
98 << std::endl
99#endif
100 << " --help, -h" << std::endl
101 << " Print this helper message." << std::endl
102 << std::endl;
103
104 std::cout << "Example" << std::endl
105 << " "
106 << argv[0]
107 << " --input image-%d.jpg" << std::endl
108 << std::endl;
109
110 if (error) {
111 std::cout << "Error" << std::endl
112 << " "
113 << "Unsupported parameter " << argv[error] << std::endl;
114 }
115}
116
117int main(int argc, const char **argv)
118{
119 double opt_tag_size = 0.048;
120 std::string opt_input_img_files = "";
121 std::string opt_intrinsic_file = "camera.xml";
122 std::string opt_camera_name = "Camera";
123 std::string opt_output_pose_files = "pose_cPo_%d.yaml";
124 bool opt_interactive = true;
125
128
129 float quad_decimate = 1.0;
130 vpDetectorAprilTag detector(tag_family);
131 bool display_tag = true;
132 bool opt_tag_z_aligned = false;
133
134 unsigned int thickness = 2;
135
136 for (int i = 1; i < argc; ++i) {
137 if (std::string(argv[i]) == "--tag-size" && i + 1 < argc) {
138 opt_tag_size = atof(argv[++i]);
139 }
140 else if (std::string(argv[i]) == "--tag-z-aligned") {
141 opt_tag_z_aligned = true;
142 }
143 else if (std::string(argv[i]) == "--input" && i + 1 < argc) {
144 opt_input_img_files = std::string(argv[++i]);
145 }
146 else if (std::string(argv[i]) == "--intrinsic" && i + 1 < argc) {
147 opt_intrinsic_file = std::string(argv[++i]);
148 }
149 else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
150 opt_output_pose_files = std::string(argv[++i]);
151 }
152 else if (std::string(argv[i]) == "--camera-name" && i + 1 < argc) {
153 opt_camera_name = std::string(argv[++i]);
154 }
155#if defined(VISP_HAVE_MODULE_GUI)
156 else if (std::string(argv[i]) == "--no-interactive") {
157 opt_interactive = false;
158 }
159#endif
160 else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
161 usage(argv, 0);
162 return EXIT_SUCCESS;
163 }
164 else {
165 usage(argv, i);
166 return EXIT_FAILURE;
167 }
168 }
169
170 if (!vpIoTools::checkFilename(opt_intrinsic_file)) {
171 std::cout << "Camera parameters file " << opt_intrinsic_file << " doesn't exist." << std::endl;
172 std::cout << "Use --help option to see how to set its location..." << std::endl;
173 return EXIT_FAILURE;
174 }
175
176 if (opt_input_img_files.empty()) {
177 std::cout << "Input images location empty." << std::endl;
178 std::cout << "Use --help option to see how to set input image location..." << std::endl;
179 return EXIT_FAILURE;
180 }
181
182 // Create output folder if necessary
183 std::string output_parent = vpIoTools::getParent(opt_output_pose_files);
184 if (!vpIoTools::checkDirectory(output_parent)) {
185 std::cout << "Create output directory: " << output_parent << std::endl;
186 vpIoTools::makeDirectory(output_parent);
187 }
188
189 try {
190 vpVideoReader reader;
191 reader.setFileName(opt_input_img_files);
192
195 reader.open(I);
196
197 std::cout << "Parameters:" << std::endl;
198 std::cout << " Apriltag " << std::endl;
199 std::cout << " Size [m] : " << opt_tag_size << std::endl;
200 std::cout << " Z aligned : " << (opt_tag_z_aligned ? "true" : "false") << std::endl;
201 std::cout << " Input images location : " << opt_input_img_files << std::endl;
202 std::cout << " First frame : " << reader.getFirstFrameIndex() << std::endl;
203 std::cout << " Last frame : " << reader.getLastFrameIndex() << std::endl;
204 std::cout << " Camera intrinsics " << std::endl;
205 std::cout << " Param file name [.xml]: " << opt_intrinsic_file << std::endl;
206 std::cout << " Camera name : " << opt_camera_name << std::endl;
207 std::cout << " Output camera poses : " << opt_output_pose_files << std::endl;
208 std::cout << " Interactive mode : " << (opt_interactive ? "yes" : "no") << std::endl << std::endl;
209
210#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
211 std::shared_ptr<vpDisplay> display;
212#else
213 vpDisplay *display = nullptr;
214#endif
215
216#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
218#else
220#endif
221
222 detector.setAprilTagQuadDecimate(quad_decimate);
223 detector.setAprilTagPoseEstimationMethod(pose_estimation_method);
224 detector.setDisplayTag(display_tag, vpColor::none, thickness);
225 detector.setZAlignedWithCameraAxis(opt_tag_z_aligned);
226
229 if (!opt_intrinsic_file.empty() && !opt_camera_name.empty()) {
230 if (parser.parse(cam, opt_intrinsic_file, opt_camera_name, vpCameraParameters::perspectiveProjWithDistortion) !=
232 std::cout << "Unable to parse parameters with distortion for camera \"" << opt_camera_name << "\" from "
233 << opt_intrinsic_file << " file" << std::endl;
234 std::cout << "Attempt to find parameters without distortion" << std::endl;
235
236 if (parser.parse(cam, opt_intrinsic_file, opt_camera_name,
238 std::cout << "Unable to parse parameters without distortion for camera \"" << opt_camera_name << "\" from "
239 << opt_intrinsic_file << " file" << std::endl;
240 return EXIT_FAILURE;
241 }
242 }
243 }
244 std::cout << "Camera parameters used to compute the pose:\n" << cam << std::endl;
245
246 bool quit = false;
247 do {
248 reader.acquire(I);
249 std::cout << "Process image: " << reader.getFrameName() << std::endl;
250
251 vpImageConvert::convert(I, I_gray);
252
253#if defined(VISP_HAVE_MODULE_GUI)
255#endif
256
257 std::vector<vpHomogeneousMatrix> cMo_vec;
258 detector.detect(I_gray, opt_tag_size, cam, cMo_vec);
259
260 bool found = (detector.getNbObjects() == 1) ? true : false;
262 if (found) {
263 cMo = cMo_vec[0];
264 vpPoseVector pose_vec(cMo);
265 std::string filename = vpIoTools::formatString(opt_output_pose_files, reader.getFrameIndex());
266 pose_vec.saveYAML(filename, pose_vec);
267 }
268
269#if defined(VISP_HAVE_MODULE_GUI)
270 if (opt_interactive) {
272 vpDisplay::displayText(I, 20, 20, "Left click for the next image, right click to quit.", vpColor::red);
273 if (found) {
274 vpDisplay::displayFrame(I, cMo, cam, opt_tag_size / 2, vpColor::none, 3);
275 }
277
279 if (vpDisplay::getClick(I, button, true)) {
280 if (button == vpMouseButton::button3) {
281 quit = true;
282 }
283 }
284 }
285#endif
286 } while (!quit && !reader.end());
287
288#if defined(VISP_HAVE_MODULE_GUI)
289 if (opt_interactive) {
290#if (VISP_CXX_STANDARD < VISP_CXX_STANDARD_11)
291 if (display) {
292 delete display;
293 }
294#endif
295 }
296#endif
297 }
298 catch (const vpException &e) {
299 std::cout << "Catch an exception: " << e.getMessage() << std::endl;
300 }
301
302 return EXIT_SUCCESS;
303}
304#else
305int main()
306{
307#if !defined(VISP_HAVE_PUGIXML)
308 std::cout << "pugixml built-in 3rdparty is requested to compute tag poses." << std::endl;
309#endif
310 return EXIT_SUCCESS;
311}
312#endif
Generic class defining intrinsic camera parameters.
@ perspectiveProjWithDistortion
Perspective projection with distortion model.
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
static const vpColor red
Definition vpColor.h:198
static const vpColor none
Definition vpColor.h:210
@ TAG_36h11
AprilTag 36h11 pattern (recommended).
Class that defines generic functionalities for display.
Definition vpDisplay.h:171
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void display(const vpImage< unsigned char > &I)
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 setTitle(const vpImage< unsigned char > &I, const std::string &windowtitle)
static void flush(const vpImage< unsigned char > &I)
static void displayText(const vpImage< unsigned char > &I, const vpImagePoint &ip, const std::string &s, const vpColor &color)
error that can be emitted by ViSP classes.
Definition vpException.h:60
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Definition of the vpImage class member functions.
Definition vpImage.h:131
static bool checkFilename(const std::string &filename)
static bool checkDirectory(const std::string &dirname)
static std::string formatString(const std::string &name, unsigned int val)
static void makeDirectory(const std::string &dirname)
static std::string getParent(const std::string &pathname)
Implementation of a pose vector and operations on poses.
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
long getLastFrameIndex()
void open(vpImage< vpRGBa > &I) VP_OVERRIDE
void setFileName(const std::string &filename)
long getFirstFrameIndex()
std::string getFrameName() const
long getFrameIndex() const
void acquire(vpImage< vpRGBa > &I) VP_OVERRIDE
XML parser to load and save intrinsic camera parameters.
std::shared_ptr< vpDisplay > createDisplay()
Return a smart pointer vpDisplay specialization if a GUI library is available or nullptr otherwise.
vpDisplay * allocateDisplay()
Return a newly allocated vpDisplay specialization if a GUI library is available or nullptr otherwise.