common_functions.cpp
Go to the documentation of this file.
1 
34 
35 #include "common/homography.h"
36 #include "tag36h11.h"
37 #include "tag36h10.h"
38 #include "tag25h9.h"
39 #include "tag25h7.h"
40 #include "tag16h5.h"
41 
42 namespace apriltags2_ros
43 {
44 
46  family_(getAprilTagOption<std::string>(pnh, "tag_family", "tag36h11")),
47  border_(getAprilTagOption<int>(pnh, "tag_border", 1)),
48  threads_(getAprilTagOption<int>(pnh, "tag_threads", 4)),
49  decimate_(getAprilTagOption<double>(pnh, "tag_decimate", 1.0)),
50  blur_(getAprilTagOption<double>(pnh, "tag_blur", 0.0)),
51  refine_edges_(getAprilTagOption<int>(pnh, "tag_refine_edges", 1)),
52  refine_decode_(getAprilTagOption<int>(pnh, "tag_refine_decode", 0)),
53  refine_pose_(getAprilTagOption<int>(pnh, "tag_refine_pose", 0)),
54  debug_(getAprilTagOption<int>(pnh, "tag_debug", 0)),
55  publish_tf_(getAprilTagOption<bool>(pnh, "publish_tf", false))
56 {
57  // Parse standalone tag descriptions specified by user (stored on ROS
58  // parameter server)
59  XmlRpc::XmlRpcValue standalone_tag_descriptions;
60  if(!pnh.getParam("standalone_tags", standalone_tag_descriptions))
61  {
62  ROS_WARN("No april tags specified");
63  }
64  else
65  {
66  try
67  {
69  parseStandaloneTags(standalone_tag_descriptions);
70  }
72  {
73  // in case any of the asserts in parseStandaloneTags() fail
74  ROS_ERROR_STREAM("Error loading standalone tag descriptions: " <<
75  e.getMessage().c_str());
76  }
77  }
78 
79  // parse tag bundle descriptions specified by user (stored on ROS parameter
80  // server)
81  XmlRpc::XmlRpcValue tag_bundle_descriptions;
82  if(!pnh.getParam("tag_bundles", tag_bundle_descriptions))
83  {
84  ROS_WARN("No tag bundles specified");
85  }
86  else
87  {
88  try
89  {
90  tag_bundle_descriptions_ = parseTagBundles(tag_bundle_descriptions);
91  }
93  {
94  // In case any of the asserts in parseStandaloneTags() fail
95  ROS_ERROR_STREAM("Error loading tag bundle descriptions: " <<
96  e.getMessage().c_str());
97  }
98  }
99 
100  // Optionally remove duplicate detections in scene. Defaults to removing
101  if(!pnh.getParam("remove_duplicates", remove_duplicates_))
102  {
103  ROS_WARN("remove_duplicates parameter not provided. Defaulting to true");
104  remove_duplicates_ = true;
105  }
106 
107  // Define the tag family whose tags should be searched for in the camera
108  // images
109  if (family_ == "tag36h11")
110  {
111  tf_ = tag36h11_create();
112  }
113  else if (family_ == "tag36h10")
114  {
115  tf_ = tag36h10_create();
116  }
117  else if (family_ == "tag25h9")
118  {
119  tf_ = tag25h9_create();
120  }
121  else if (family_ == "tag25h7")
122  {
123  tf_ = tag25h7_create();
124  }
125  else if (family_ == "tag16h5")
126  {
127  tf_ = tag16h5_create();
128  }
129  else
130  {
131  ROS_WARN("Invalid tag family specified! Aborting");
132  exit(1);
133  }
134  tf_->black_border = (uint32_t)border_;
135 
136  // Create the AprilTags 2 detector
138  apriltag_detector_add_family(td_, tf_);
139  td_->quad_decimate = (float)decimate_;
140  td_->quad_sigma = (float)blur_;
141  td_->nthreads = threads_;
142  td_->debug = debug_;
146 
147  detections_ = NULL;
148 
149  // Get tf frame name to use for the camera
150  if (!pnh.getParam("camera_frame", camera_tf_frame_))
151  {
152  ROS_WARN_STREAM("Camera frame not specified, using 'camera'");
153  camera_tf_frame_ = "camera";
154  }
155 }
156 
157 // destructor
159  // free memory associated with tag detector
161 
162  // Free memory associated with the array of tag detections
164 
165  // free memory associated with tag family
166  if (family_ == "tag36h11")
167  {
169  }
170  else if (family_ == "tag36h10")
171  {
173  }
174  else if (family_ == "tag25h9")
175  {
177  }
178  else if (family_ == "tag25h7")
179  {
181  }
182  else if (family_ == "tag16h5")
183  {
185  }
186 }
187 
188 AprilTagDetectionArray TagDetector::detectTags (
189  const cv_bridge::CvImagePtr& image,
190  const sensor_msgs::CameraInfoConstPtr& camera_info) {
191  // Convert image to AprilTag code's format
192  cv::Mat gray_image;
193  cv::cvtColor(image->image, gray_image, CV_BGR2GRAY);
194  image_u8_t apriltags2_image = { .width = gray_image.cols,
195  .height = gray_image.rows,
196  .stride = gray_image.cols,
197  .buf = gray_image.data
198  };
199 
201  camera_model.fromCameraInfo(camera_info);
202 
203  // Get camera intrinsic properties for rectified image.
204  double fx = camera_model.fx(); // focal length in camera x-direction [px]
205  double fy = camera_model.fy(); // focal length in camera y-direction [px]
206  double cx = camera_model.cx(); // optical center x-coordinate [px]
207  double cy = camera_model.cy(); // optical center y-coordinate [px]
208 
209  // Run AprilTags 2 algorithm on the image
210  if (detections_)
211  {
213  detections_ = NULL;
214  }
215  detections_ = apriltag_detector_detect(td_, &apriltags2_image);
216 
217  // If remove_dulpicates_ is set to true, then duplicate tags are not allowed.
218  // Thus any duplicate tag IDs visible in the scene must include at least 1
219  // erroneous detection. Remove any tags with duplicate IDs to ensure removal
220  // of these erroneous detections
221  if (remove_duplicates_)
222  {
224  }
225 
226  // Compute the estimated translation and rotation individually for each
227  // detected tag
228  AprilTagDetectionArray tag_detection_array;
229  std::vector<std::string > detection_names;
230  tag_detection_array.header = image->header;
231  std::map<std::string, std::vector<cv::Point3d > > bundleObjectPoints;
232  std::map<std::string, std::vector<cv::Point2d > > bundleImagePoints;
233  for (int i=0; i < zarray_size(detections_); i++)
234  {
235  // Get the i-th detected tag
236  apriltag_detection_t *detection;
237  zarray_get(detections_, i, &detection);
238 
239  // Bootstrap this for loop to find this tag's description amongst
240  // the tag bundles. If found, add its points to the bundle's set of
241  // object-image corresponding points (tag corners) for cv::solvePnP.
242  // Don't yet run cv::solvePnP on the bundles, though, since we're still in
243  // the process of collecting all the object-image corresponding points
244  int tagID = detection->id;
245  bool is_part_of_bundle = false;
246  for (unsigned int j=0; j<tag_bundle_descriptions_.size(); j++)
247  {
248  // Iterate over the registered bundles
250 
251  if (bundle.id2idx_.find(tagID) != bundle.id2idx_.end())
252  {
253  // This detected tag belongs to the j-th tag bundle (its ID was found in
254  // the bundle description)
255  is_part_of_bundle = true;
256  std::string bundleName = bundle.name();
257 
258  //===== Corner points in the world frame coordinates
259  double s = bundle.memberSize(tagID)/2;
260  addObjectPoints(s, bundle.memberT_oi(tagID),
261  bundleObjectPoints[bundleName]);
262 
263  //===== Corner points in the image frame coordinates
264  addImagePoints(detection, bundleImagePoints[bundleName]);
265  }
266  }
267 
268  // Find this tag's description amongst the standalone tags
269  // Print warning when a tag was found that is neither part of a
270  // bundle nor standalone (thus it is a tag in the environment
271  // which the user specified no description for, or Apriltags
272  // misdetected a tag (bad ID or a false positive)).
273  StandaloneTagDescription* standaloneDescription;
274  if (!findStandaloneTagDescription(tagID, standaloneDescription,
275  !is_part_of_bundle))
276  {
277  continue;
278  }
279 
280  //=================================================================
281  // The remainder of this for loop is concerned with standalone tag
282  // poses!
283  double tag_size = standaloneDescription->size();
284 
285  // Get estimated tag pose in the camera frame.
286  //
287  // Note on frames:
288  // The raw AprilTags 2 uses the following frames:
289  // - camera frame: looking from behind the camera (like a
290  // photographer), x is right, y is up and z is towards you
291  // (i.e. the back of camera)
292  // - tag frame: looking straight at the tag (oriented correctly),
293  // x is right, y is down and z is away from you (into the tag).
294  // But we want:
295  // - camera frame: looking from behind the camera (like a
296  // photographer), x is right, y is down and z is straight
297  // ahead
298  // - tag frame: looking straight at the tag (oriented correctly),
299  // x is right, y is up and z is towards you (out of the tag).
300  // Using these frames together with cv::solvePnP directly avoids
301  // AprilTag 2's frames altogether.
302  // TODO solvePnP[Ransac] better?
303  std::vector<cv::Point3d > standaloneTagObjectPoints;
304  std::vector<cv::Point2d > standaloneTagImagePoints;
305  addObjectPoints(tag_size/2, cv::Matx44d::eye(), standaloneTagObjectPoints);
306  addImagePoints(detection, standaloneTagImagePoints);
307  Eigen::Matrix4d transform = getRelativeTransform(standaloneTagObjectPoints,
308  standaloneTagImagePoints,
309  fx, fy, cx, cy);
310  Eigen::Matrix3d rot = transform.block(0, 0, 3, 3);
311  Eigen::Quaternion<double> rot_quaternion(rot);
312 
313  geometry_msgs::PoseWithCovarianceStamped tag_pose =
314  makeTagPose(transform, rot_quaternion, image->header);
315 
316  // Add the detection to the back of the tag detection array
317  AprilTagDetection tag_detection;
318  tag_detection.pose = tag_pose;
319  tag_detection.id.push_back(detection->id);
320  tag_detection.size.push_back(tag_size);
321  tag_detection_array.detections.push_back(tag_detection);
322  detection_names.push_back(standaloneDescription->frame_name());
323  }
324 
325  //=================================================================
326  // Estimate bundle origin pose for each bundle in which at least one
327  // member tag was detected
328 
329  for (unsigned int j=0; j<tag_bundle_descriptions_.size(); j++)
330  {
331  // Get bundle name
332  std::string bundleName = tag_bundle_descriptions_[j].name();
333 
334  std::map<std::string,
335  std::vector<cv::Point3d> >::iterator it =
336  bundleObjectPoints.find(bundleName);
337  if (it != bundleObjectPoints.end())
338  {
339  // Some member tags of this bundle were detected, get the bundle's
340  // position!
342 
343  Eigen::Matrix4d transform =
344  getRelativeTransform(bundleObjectPoints[bundleName],
345  bundleImagePoints[bundleName], fx, fy, cx, cy);
346  Eigen::Matrix3d rot = transform.block(0, 0, 3, 3);
347  Eigen::Quaternion<double> rot_quaternion(rot);
348 
349  geometry_msgs::PoseWithCovarianceStamped bundle_pose =
350  makeTagPose(transform, rot_quaternion, image->header);
351 
352  // Add the detection to the back of the tag detection array
353  AprilTagDetection tag_detection;
354  tag_detection.pose = bundle_pose;
355  tag_detection.id = bundle.bundleIds();
356  tag_detection.size = bundle.bundleSizes();
357  tag_detection_array.detections.push_back(tag_detection);
358  detection_names.push_back(bundle.name());
359  }
360  }
361 
362  // If set, publish the transform /tf topic
363  if (publish_tf_) {
364  for (unsigned int i=0; i<tag_detection_array.detections.size(); i++) {
365  geometry_msgs::PoseStamped pose;
366  pose.pose = tag_detection_array.detections[i].pose.pose.pose;
367  pose.header = tag_detection_array.detections[i].pose.header;
368  tf::Stamped<tf::Transform> tag_transform;
369  tf::poseStampedMsgToTF(pose, tag_transform);
371  tag_transform.stamp_,
373  detection_names[i]));
374  }
375  }
376 
377  return tag_detection_array;
378 }
379 
380 int TagDetector::idComparison (const void* first, const void* second)
381 {
382  int id1 = ((apriltag_detection_t*) first)->id;
383  int id2 = ((apriltag_detection_t*) second)->id;
384  return (id1 < id2) ? -1 : ((id1 == id2) ? 0 : 1);
385 }
386 
388 {
389  zarray_sort(detections_, &idComparison);
390  int count = 0;
391  bool duplicate_detected = false;
392  while (true)
393  {
394  if (count > zarray_size(detections_)-1)
395  {
396  // The entire detection set was parsed
397  return;
398  }
399  apriltag_detection_t *detection;
400  zarray_get(detections_, count, &detection);
401  int id_current = detection->id;
402  // Default id_next value of -1 ensures that if the last detection
403  // is a duplicated tag ID, it will get removed
404  int id_next = -1;
405  if (count < zarray_size(detections_)-1)
406  {
407  zarray_get(detections_, count+1, &detection);
408  id_next = detection->id;
409  }
410  if (id_current == id_next || (id_current != id_next && duplicate_detected))
411  {
412  duplicate_detected = true;
413  // Remove the current tag detection from detections array
414  int shuffle = 0;
415  zarray_remove_index(detections_, count, shuffle);
416  if (id_current != id_next)
417  {
418  ROS_WARN_STREAM("Pruning tag ID " << id_current << " because it "
419  "appears more than once in the image.");
420  duplicate_detected = false; // Reset
421  }
422  continue;
423  }
424  else
425  {
426  count++;
427  }
428  }
429 }
430 
432  double s, cv::Matx44d T_oi, std::vector<cv::Point3d >& objectPoints) const
433 {
434  // Add to object point vector the tag corner coordinates in the bundle frame
435  // Going counterclockwise starting from the bottom left corner
436  objectPoints.push_back(T_oi.get_minor<3, 4>(0, 0)*cv::Vec4d(-s,-s, 0, 1));
437  objectPoints.push_back(T_oi.get_minor<3, 4>(0, 0)*cv::Vec4d( s,-s, 0, 1));
438  objectPoints.push_back(T_oi.get_minor<3, 4>(0, 0)*cv::Vec4d( s, s, 0, 1));
439  objectPoints.push_back(T_oi.get_minor<3, 4>(0, 0)*cv::Vec4d(-s, s, 0, 1));
440 }
441 
443  apriltag_detection_t *detection,
444  std::vector<cv::Point2d >& imagePoints) const
445 {
446  // Add to image point vector the tag corners in the image frame
447  // Going counterclockwise starting from the bottom left corner
448  double tag_x[4] = {-1,1,1,-1};
449  double tag_y[4] = {1,1,-1,-1}; // Negated because AprilTag tag local
450  // frame has y-axis pointing DOWN
451  // while we use the tag local frame
452  // with y-axis pointing UP
453  for (int i=0; i<4; i++)
454  {
455  // Homography projection taking tag local frame coordinates to image pixels
456  double im_x, im_y;
457  homography_project(detection->H, tag_x[i], tag_y[i], &im_x, &im_y);
458  imagePoints.push_back(cv::Point2d(im_x, im_y));
459  }
460 }
461 
463  std::vector<cv::Point3d > objectPoints,
464  std::vector<cv::Point2d > imagePoints,
465  double fx, double fy, double cx, double cy) const
466 {
467  // perform Perspective-n-Point camera pose estimation using the
468  // above 3D-2D point correspondences
469  cv::Mat rvec, tvec;
470  cv::Matx33d cameraMatrix(fx, 0, cx,
471  0, fy, cy,
472  0, 0, 1);
473  cv::Vec4f distCoeffs(0,0,0,0); // distortion coefficients
474  // TODO Perhaps something like SOLVEPNP_EPNP would be faster? Would
475  // need to first check WHAT is a bottleneck in this code, and only
476  // do this if PnP solution is the bottleneck.
477  cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);
478  cv::Matx33d R;
479  cv::Rodrigues(rvec, R);
480  Eigen::Matrix3d wRo;
481  wRo << R(0,0), R(0,1), R(0,2), R(1,0), R(1,1), R(1,2), R(2,0), R(2,1), R(2,2);
482 
483  Eigen::Matrix4d T; // homogeneous transformation matrix
484  T.topLeftCorner(3, 3) = wRo;
485  T.col(3).head(3) <<
486  tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2);
487  T.row(3) << 0,0,0,1;
488  return T;
489 }
490 
491 geometry_msgs::PoseWithCovarianceStamped TagDetector::makeTagPose(
492  const Eigen::Matrix4d& transform,
493  const Eigen::Quaternion<double> rot_quaternion,
494  const std_msgs::Header& header)
495 {
496  geometry_msgs::PoseWithCovarianceStamped pose;
497  pose.header = header;
498  //===== Position and orientation
499  pose.pose.pose.position.x = transform(0, 3);
500  pose.pose.pose.position.y = transform(1, 3);
501  pose.pose.pose.position.z = transform(2, 3);
502  pose.pose.pose.orientation.x = rot_quaternion.x();
503  pose.pose.pose.orientation.y = rot_quaternion.y();
504  pose.pose.pose.orientation.z = rot_quaternion.z();
505  pose.pose.pose.orientation.w = rot_quaternion.w();
506  return pose;
507 }
508 
510 {
511  for (int i = 0; i < zarray_size(detections_); i++)
512  {
514  zarray_get(detections_, i, &det);
515 
516  // Check if this ID is present in config/tags.yaml
517  // Check if is part of a tag bundle
518  int tagID = det->id;
519  bool is_part_of_bundle = false;
520  for (unsigned int j=0; j<tag_bundle_descriptions_.size(); j++)
521  {
523  if (bundle.id2idx_.find(tagID) != bundle.id2idx_.end())
524  {
525  is_part_of_bundle = true;
526  break;
527  }
528  }
529  // If not part of a bundle, check if defined as a standalone tag
530  StandaloneTagDescription* standaloneDescription;
531  if (!is_part_of_bundle &&
532  !findStandaloneTagDescription(tagID, standaloneDescription, false))
533  {
534  // Neither a standalone tag nor part of a bundle, so this is a "rogue"
535  // tag, skip it.
536  continue;
537  }
538 
539  // Draw tag outline with edge colors green, blue, blue, red
540  // (going counter-clockwise, starting from lower-left corner in
541  // tag coords). cv::Scalar(Blue, Green, Red) format for the edge
542  // colors!
543  line(image->image, cv::Point((int)det->p[0][0], (int)det->p[0][1]),
544  cv::Point((int)det->p[1][0], (int)det->p[1][1]),
545  cv::Scalar(0, 0xff, 0)); // green
546  line(image->image, cv::Point((int)det->p[0][0], (int)det->p[0][1]),
547  cv::Point((int)det->p[3][0], (int)det->p[3][1]),
548  cv::Scalar(0, 0, 0xff)); // red
549  line(image->image, cv::Point((int)det->p[1][0], (int)det->p[1][1]),
550  cv::Point((int)det->p[2][0], (int)det->p[2][1]),
551  cv::Scalar(0xff, 0, 0)); // blue
552  line(image->image, cv::Point((int)det->p[2][0], (int)det->p[2][1]),
553  cv::Point((int)det->p[3][0], (int)det->p[3][1]),
554  cv::Scalar(0xff, 0, 0)); // blue
555 
556  // Print tag ID in the middle of the tag
557  std::stringstream ss;
558  ss << det->id;
559  cv::String text = ss.str();
560  int fontface = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;
561  double fontscale = 0.5;
562  int baseline;
563  cv::Size textsize = cv::getTextSize(text, fontface,
564  fontscale, 2, &baseline);
565  cv::putText(image->image, text,
566  cv::Point((int)(det->c[0]-textsize.width/2),
567  (int)(det->c[1]+textsize.height/2)),
568  fontface, fontscale, cv::Scalar(0xff, 0x99, 0), 2);
569  }
570 }
571 
572 // Parse standalone tag descriptions
573 std::map<int, StandaloneTagDescription> TagDetector::parseStandaloneTags (
574  XmlRpc::XmlRpcValue& standalone_tags)
575 {
576  // Create map that will be filled by the function and returned in the end
577  std::map<int, StandaloneTagDescription> descriptions;
578  // Ensure the type is correct
579  ROS_ASSERT(standalone_tags.getType() == XmlRpc::XmlRpcValue::TypeArray);
580  // Loop through all tag descriptions
581  for (int32_t i = 0; i < standalone_tags.size(); i++)
582  {
583 
584  // i-th tag description
585  XmlRpc::XmlRpcValue& tag_description = standalone_tags[i];
586 
587  // Assert the tag description is a struct
588  ROS_ASSERT(tag_description.getType() ==
590  // Assert type of field "id" is an int
591  ROS_ASSERT(tag_description["id"].getType() ==
593  // Assert type of field "size" is a double
594  ROS_ASSERT(tag_description["size"].getType() ==
596 
597  int id = (int)tag_description["id"]; // tag id
598  // Tag size (square, side length in meters)
599  double size = (double)tag_description["size"];
600 
601  // Custom frame name, if such a field exists for this tag
602  std::string frame_name;
603  if(tag_description.hasMember("name"))
604  {
605  // Assert type of field "name" is a string
606  ROS_ASSERT(tag_description["name"].getType() ==
608  frame_name = (std::string)tag_description["name"];
609  }
610  else
611  {
612  std::stringstream frame_name_stream;
613  frame_name_stream << "tag_" << id;
614  frame_name = frame_name_stream.str();
615  }
616 
617  StandaloneTagDescription description(id, size, frame_name);
618  ROS_INFO_STREAM("Loaded tag config: " << id << ", size: " <<
619  size << ", frame_name: " << frame_name.c_str());
620  // Add this tag's description to map of descriptions
621  descriptions.insert(std::make_pair(id, description));
622  }
623 
624  return descriptions;
625 }
626 
627 // parse tag bundle descriptions
628 std::vector<TagBundleDescription > TagDetector::parseTagBundles (
629  XmlRpc::XmlRpcValue& tag_bundles)
630 {
631  std::vector<TagBundleDescription > descriptions;
633 
634  // Loop through all tag bundle descritions
635  for (int32_t i=0; i<tag_bundles.size(); i++)
636  {
637  ROS_ASSERT(tag_bundles[i].getType() == XmlRpc::XmlRpcValue::TypeStruct);
638  // i-th tag bundle description
639  XmlRpc::XmlRpcValue& bundle_description = tag_bundles[i];
640 
641  std::string bundleName;
642  if (bundle_description.hasMember("name"))
643  {
644  ROS_ASSERT(bundle_description["name"].getType() ==
646  bundleName = (std::string)bundle_description["name"];
647  }
648  else
649  {
650  std::stringstream bundle_name_stream;
651  bundle_name_stream << "bundle_" << i;
652  bundleName = bundle_name_stream.str();
653  }
654  TagBundleDescription bundle_i(bundleName);
655  ROS_INFO("Loading tag bundle '%s'",bundle_i.name().c_str());
656 
657  ROS_ASSERT(bundle_description["layout"].getType() ==
659  XmlRpc::XmlRpcValue& member_tags = bundle_description["layout"];
660 
661  // Loop through each member tag of the bundle
662  for (int32_t j=0; j<member_tags.size(); j++)
663  {
664  ROS_ASSERT(member_tags[j].getType() == XmlRpc::XmlRpcValue::TypeStruct);
665  XmlRpc::XmlRpcValue& tag = member_tags[j];
666 
667  ROS_ASSERT(tag["id"].getType() == XmlRpc::XmlRpcValue::TypeInt);
668  int id = tag["id"];
669 
670  ROS_ASSERT(tag["size"].getType() == XmlRpc::XmlRpcValue::TypeDouble);
671  double size = tag["size"];
672 
673  // Make sure that if this tag was specified also as standalone,
674  // then the sizes match
675  StandaloneTagDescription* standaloneDescription;
676  if (findStandaloneTagDescription(id, standaloneDescription, false))
677  {
678  ROS_ASSERT(size == standaloneDescription->size());
679  }
680 
681  // Get this tag's pose with respect to the bundle origin
682  double x = xmlRpcGetDoubleWithDefault(tag, "x", 0.);
683  double y = xmlRpcGetDoubleWithDefault(tag, "y", 0.);
684  double z = xmlRpcGetDoubleWithDefault(tag, "z", 0.);
685  double qw = xmlRpcGetDoubleWithDefault(tag, "qw", 1.);
686  double qx = xmlRpcGetDoubleWithDefault(tag, "qx", 0.);
687  double qy = xmlRpcGetDoubleWithDefault(tag, "qy", 0.);
688  double qz = xmlRpcGetDoubleWithDefault(tag, "qz", 0.);
689  Eigen::Quaterniond q_tag(qw, qx, qy, qz);
690  q_tag.normalize();
691  Eigen::Matrix3d R_oi = q_tag.toRotationMatrix();
692 
693  // Build the rigid transform from tag_j to the bundle origin
694  cv::Matx44d T_mj(R_oi(0,0), R_oi(0,1), R_oi(0,2), x,
695  R_oi(1,0), R_oi(1,1), R_oi(1,2), y,
696  R_oi(2,0), R_oi(2,1), R_oi(2,2), z,
697  0, 0, 0, 1);
698 
699  // Register the tag member
700  bundle_i.addMemberTag(id, size, T_mj);
701  ROS_INFO_STREAM(" " << j << ") id: " << id << ", size: " << size << ", "
702  << "p = [" << x << "," << y << "," << z << "], "
703  << "q = [" << qw << "," << qx << "," << qy << ","
704  << qz << "]");
705  }
706  descriptions.push_back(bundle_i);
707  }
708  return descriptions;
709 }
710 
712  std::string field) const
713 {
714  ROS_ASSERT((xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeDouble) ||
715  (xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeInt));
716  if (xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeInt)
717  {
718  int tmp = xmlValue[field];
719  return (double)tmp;
720  }
721  else
722  {
723  return xmlValue[field];
724  }
725 }
726 
728  std::string field,
729  double defaultValue) const
730 {
731  if (xmlValue.hasMember(field))
732  {
733  ROS_ASSERT((xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeDouble) ||
734  (xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeInt));
735  if (xmlValue[field].getType() == XmlRpc::XmlRpcValue::TypeInt)
736  {
737  int tmp = xmlValue[field];
738  return (double)tmp;
739  }
740  else
741  {
742  return xmlValue[field];
743  }
744  }
745  else
746  {
747  return defaultValue;
748  }
749 }
750 
752  int id, StandaloneTagDescription*& descriptionContainer, bool printWarning)
753 {
754  std::map<int, StandaloneTagDescription>::iterator description_itr =
756  if (description_itr == standalone_tag_descriptions_.end())
757  {
758  if (printWarning)
759  {
760  ROS_WARN_THROTTLE(10.0, "Requested description of standalone tag ID [%d],"
761  " but no description was found...",id);
762  }
763  return false;
764  }
765  descriptionContainer = &(description_itr->second);
766  return true;
767 }
768 
769 } // namespace apriltags2_ros
static int idComparison(const void *first, const void *second)
const std::string & getMessage() const
void tag25h7_destroy(apriltag_family_t *tf)
#define ROS_WARN_THROTTLE(rate,...)
double xmlRpcGetDoubleWithDefault(XmlRpc::XmlRpcValue &xmlValue, std::string field, double defaultValue) const
std::map< int, StandaloneTagDescription > standalone_tag_descriptions_
double p[4][2]
int size() const
XmlRpcServer s
uint32_t black_border
apriltag_family_t * tag36h10_create()
apriltag_detector_t * apriltag_detector_create()
double xmlRpcGetDouble(XmlRpc::XmlRpcValue &xmlValue, std::string field) const
void apriltag_detections_destroy(zarray_t *detections)
Type const & getType() const
void tag36h11_destroy(apriltag_family_t *tf)
apriltag_family_t * tag16h5_create()
TFSIMD_FORCE_INLINE const tfScalar & y() const
#define ROS_WARN(...)
void addMemberTag(int id, double size, cv::Matx44d T_oi)
const int32_t width
apriltag_family_t * tag25h9_create()
bool findStandaloneTagDescription(int id, StandaloneTagDescription *&descriptionContainer, bool printWarning=true)
std::vector< TagBundleDescription > tag_bundle_descriptions_
Eigen::Matrix4d getRelativeTransform(std::vector< cv::Point3d > objectPoints, std::vector< cv::Point2d > imagePoints, double fx, double fy, double cx, double cy) const
#define ROS_INFO(...)
bool fromCameraInfo(const sensor_msgs::CameraInfo &msg)
void tag36h10_destroy(apriltag_family_t *tf)
T getAprilTagOption(ros::NodeHandle &pnh, const std::string &param_name, const T &default_val)
void sendTransform(const StampedTransform &transform)
void addImagePoints(apriltag_detection_t *detection, std::vector< cv::Point2d > &imagePoints) const
TFSIMD_FORCE_INLINE const tfScalar & x() const
geometry_msgs::PoseWithCovarianceStamped makeTagPose(const Eigen::Matrix4d &transform, const Eigen::Quaternion< double > rot_quaternion, const std_msgs::Header &header)
#define ROS_WARN_STREAM(args)
std::map< int, StandaloneTagDescription > parseStandaloneTags(XmlRpc::XmlRpcValue &standalone_tag_descriptions)
apriltag_detector_t * td_
TFSIMD_FORCE_INLINE const tfScalar & z() const
apriltag_family_t * tag25h7_create()
AprilTagDetectionArray detectTags(const cv_bridge::CvImagePtr &image, const sensor_msgs::CameraInfoConstPtr &camera_info)
ros::Time stamp_
bool hasMember(const std::string &name) const
void tag25h9_destroy(apriltag_family_t *tf)
#define ROS_INFO_STREAM(args)
void drawDetections(cv_bridge::CvImagePtr image)
bool getParam(const std::string &key, std::string &s) const
tf::TransformBroadcaster tf_pub_
void apriltag_detector_destroy(apriltag_detector_t *td)
static void poseStampedMsgToTF(const geometry_msgs::PoseStamped &msg, Stamped< Pose > &bt)
void addObjectPoints(double s, cv::Matx44d T_oi, std::vector< cv::Point3d > &objectPoints) const
#define ROS_ERROR_STREAM(args)
std::vector< TagBundleDescription > parseTagBundles(XmlRpc::XmlRpcValue &tag_bundles)
TagDetector(ros::NodeHandle pnh)
#define ROS_ASSERT(cond)
apriltag_family_t * tag36h11_create()
void tag16h5_destroy(apriltag_family_t *tf)
zarray_t * apriltag_detector_detect(apriltag_detector_t *td, image_u8_t *im_orig)


apriltags2_ros
Author(s): Danylo Malyuta
autogenerated on Fri Oct 19 2018 04:02:45