23 #include <opencv2/aruco.hpp>
24 #include <opencv2/calib3d.hpp>
25 #include <yaml-cpp/yaml.h>
32 #include <dynamic_reconfigure/server.h>
39 #include <aruco_opencv/ArucoDetectorConfig.h>
41 #include <aruco_opencv_msgs/ArucoDetection.h>
67 dynamic_reconfigure::Server<aruco_opencv::ArucoDetectorConfig> *
dyn_srv_;
75 std::vector<std::pair<std::string, cv::Ptr<cv::aruco::Board>>>
boards_;
109 dyn_srv_ =
new dynamic_reconfigure::Server<aruco_opencv::ArucoDetectorConfig>(pnh);
128 detection_pub_ = nh.advertise<aruco_opencv_msgs::ArucoDetection>(
"aruco_detections", 5);
149 ROS_INFO(
"Marker detections will be published in the camera frame");
174 YAML::Node descriptions;
177 }
catch (
const YAML::Exception &e) {
182 if (!descriptions.IsSequence()) {
183 ROS_ERROR_STREAM(
"Failed to load board descriptions: root node is not a sequence");
186 for (
const YAML::Node &desc : descriptions) {
189 name = desc[
"name"].as<std::string>();
190 const bool frame_at_center = desc[
"frame_at_center"].as<
bool>();
191 const int markers_x = desc[
"markers_x"].as<
int>();
192 const int markers_y = desc[
"markers_y"].as<
int>();
193 const double marker_size = desc[
"marker_size"].as<
double>();
194 const double separation = desc[
"separation"].as<
double>();
196 auto board = cv::aruco::GridBoard::create(markers_x, markers_y, marker_size, separation,
199 if (frame_at_center) {
200 double offset_x = (markers_x * (marker_size + separation) - separation) / 2.0;
201 double offset_y = (markers_y * (marker_size + separation) - separation) / 2.0;
202 for (
auto &obj : board->objPoints) {
203 for (
auto &point : obj) {
210 boards_.push_back(std::make_pair(name, board));
211 }
catch (
const YAML::Exception &e) {
215 ROS_INFO_STREAM(
"Successfully loaded configuration for board '" << name <<
"'");
220 if (config.adaptiveThreshWinSizeMax < config.adaptiveThreshWinSizeMin)
221 config.adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMin;
223 if (config.maxMarkerPerimeterRate < config.minMarkerPerimeterRate)
224 config.maxMarkerPerimeterRate = config.minMarkerPerimeterRate;
239 config.perspectiveRemoveIgnoredMarginPerCell;
243 #if CV_VERSION_MAJOR >= 4
257 for (
int i = 0; i < 9; ++i)
260 for (
int i = 0; i < 9; ++i)
278 ROS_DEBUG(
"The new image has the same timestamp as the previous one (duplicate frame?). "
289 std::vector<int> marker_ids;
290 std::vector<std::vector<cv::Point2f>> marker_corners;
292 cv::aruco::detectMarkers(cv_ptr->image,
dictionary_, marker_corners, marker_ids,
295 int n_markers = marker_ids.size();
296 std::vector<cv::Vec3d> rvec_final(n_markers), tvec_final(n_markers);
298 aruco_opencv_msgs::ArucoDetection detection;
299 detection.header.frame_id = img_msg->header.frame_id;
300 detection.header.stamp = img_msg->header.stamp;
301 detection.markers.resize(n_markers);
305 #if CV_VERSION_MAJOR >= 4
306 cv::parallel_for_(cv::Range(0, n_markers), [&](
const cv::Range &range) {
308 const cv::Range range = cv::Range(0, n_markers);
311 for (
int i = range.start; i < range.end; i++) {
312 int id = marker_ids[i];
314 #if CV_VERSION_MAJOR >= 4
316 rvec_final[i], tvec_final[i],
false, cv::SOLVEPNP_IPPE_SQUARE);
319 rvec_final[i], tvec_final[i],
false, cv::SOLVEPNP_ITERATIVE);
322 detection.markers[i].marker_id = id;
327 for (
const auto &board_desc :
boards_) {
328 std::string name = board_desc.first;
329 auto &board = board_desc.second;
331 cv::Vec3d rvec, tvec;
332 int valid = cv::aruco::estimatePoseBoard(marker_corners, marker_ids, board,
camera_matrix_,
336 aruco_opencv_msgs::BoardPose bpose;
337 bpose.board_name = name;
339 detection.boards.push_back(bpose);
340 rvec_final.push_back(rvec);
341 tvec_final.push_back(tvec);
349 geometry_msgs::TransformStamped cam_to_output;
358 for (
auto &marker_pose : detection.markers)
360 for (
auto &board_pose : detection.boards)
365 std::vector<geometry_msgs::TransformStamped> transforms;
366 for (
auto &marker_pose : detection.markers) {
367 geometry_msgs::TransformStamped transform;
368 transform.header.stamp = detection.header.stamp;
369 transform.header.frame_id = detection.header.frame_id;
370 transform.child_frame_id = std::string(
"marker_") + std::to_string(marker_pose.marker_id);
373 transform.transform =
tf2::toMsg(tf_transform);
374 transforms.push_back(transform);
376 for (
auto &board_pose : detection.boards) {
377 geometry_msgs::TransformStamped transform;
378 transform.header.stamp = detection.header.stamp;
379 transform.header.frame_id = detection.header.frame_id;
380 transform.child_frame_id = std::string(
"board_") + board_pose.board_name;
383 transform.transform =
tf2::toMsg(tf_transform);
384 transforms.push_back(transform);
393 cv::aruco::drawDetectedMarkers(debug_cv_ptr->image, marker_corners, marker_ids);
396 for (
size_t i = 0; i < n_markers; i++) {
397 #if CV_VERSION_MAJOR >= 4
399 tvec_final[i], 0.2, 3);
402 rvec_final[i], tvec_final[i], 0.2);
411 double whole_callback_duration = (callback_end_time - callback_start_time).toSec();
412 double image_send_duration = (callback_start_time - img_msg->header.stamp).toSec();
414 NODELET_DEBUG(
"Image callback completed. The callback started %.4f s after "
415 "the image frame was "
416 "grabbed and completed its execution in %.4f s.",
417 image_send_duration, whole_callback_duration);