aruco_detect.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-20, Ubiquity Robotics Inc., Austin Hendrix
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  *
30  */
31 
32 #include <assert.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #include <math.h>
36 
37 #include <ros/ros.h>
38 #include <tf/transform_datatypes.h>
40 #include <tf2_ros/buffer.h>
44 #include <visualization_msgs/Marker.h>
46 #include <cv_bridge/cv_bridge.h>
48 #include <dynamic_reconfigure/server.h>
49 #include <std_srvs/SetBool.h>
50 #include <std_msgs/String.h>
51 
52 #include "fiducial_msgs/Fiducial.h"
53 #include "fiducial_msgs/FiducialArray.h"
54 #include "fiducial_msgs/FiducialTransform.h"
55 #include "fiducial_msgs/FiducialTransformArray.h"
56 #include "aruco_detect/DetectorParamsConfig.h"
57 
58 #include <vision_msgs/Detection2D.h>
59 #include <vision_msgs/Detection2DArray.h>
60 #include <vision_msgs/ObjectHypothesisWithPose.h>
61 
62 #include <opencv2/highgui.hpp>
63 #include <opencv2/aruco.hpp>
64 #include <opencv2/calib3d.hpp>
65 
66 #include <list>
67 #include <string>
68 #include <boost/algorithm/string.hpp>
69 #include <boost/shared_ptr.hpp>
70 
71 using namespace std;
72 using namespace cv;
73 
75 
77  private:
80 
87 
89 
90  // if set, we publish the images that contain fiducials
93  bool vis_msgs;
94 
95  double fiducial_len;
96 
100  vector <vector <Point2f> > corners;
101  vector <int> ids;
103 
104  cv::Mat cameraMatrix;
106  int frameNum;
107  std::string frameId;
108  std::vector<int> ignoreIds;
109  std::map<int, double> fiducialLens;
112 
114 
115  cv::Ptr<aruco::DetectorParameters> detectorParams;
116  cv::Ptr<aruco::Dictionary> dictionary;
117 
118  void handleIgnoreString(const std::string& str);
119 
120  void estimatePoseSingleMarkers(float markerLength,
121  const cv::Mat &cameraMatrix,
122  const cv::Mat &distCoeffs,
123  vector<Vec3d>& rvecs, vector<Vec3d>& tvecs,
124  vector<double>& reprojectionError);
125 
126 
127  void ignoreCallback(const std_msgs::String &msg);
128  void imageCallback(const sensor_msgs::ImageConstPtr &msg);
129  void poseEstimateCallback(const FiducialArrayConstPtr &msg);
130  void camInfoCallback(const sensor_msgs::CameraInfo::ConstPtr &msg);
131  void configCallback(aruco_detect::DetectorParamsConfig &config, uint32_t level);
132 
133  bool enableDetectionsCallback(std_srvs::SetBool::Request &req,
134  std_srvs::SetBool::Response &res);
135 
136  dynamic_reconfigure::Server<aruco_detect::DetectorParamsConfig> configServer;
137  dynamic_reconfigure::Server<aruco_detect::DetectorParamsConfig>::CallbackType callbackType;
138 
139  public:
140  FiducialsNode();
141 };
142 
143 
147 static void getSingleMarkerObjectPoints(float markerLength, vector<Point3f>& objPoints) {
148 
149  CV_Assert(markerLength > 0);
150 
151  // set coordinate system in the middle of the marker, with Z pointing out
152  objPoints.clear();
153  objPoints.push_back(Vec3f(-markerLength / 2.f, markerLength / 2.f, 0));
154  objPoints.push_back(Vec3f( markerLength / 2.f, markerLength / 2.f, 0));
155  objPoints.push_back(Vec3f( markerLength / 2.f,-markerLength / 2.f, 0));
156  objPoints.push_back(Vec3f(-markerLength / 2.f,-markerLength / 2.f, 0));
157 }
158 
159 // Euclidean distance between two points
160 static double dist(const cv::Point2f &p1, const cv::Point2f &p2)
161 {
162  double x1 = p1.x;
163  double y1 = p1.y;
164  double x2 = p2.x;
165  double y2 = p2.y;
166 
167  double dx = x1 - x2;
168  double dy = y1 - y2;
169 
170  return sqrt(dx*dx + dy*dy);
171 }
172 
173 // Compute area in image of a fiducial, using Heron's formula
174 // to find the area of two triangles
175 static double calcFiducialArea(const std::vector<cv::Point2f> &pts)
176 {
177  const Point2f &p0 = pts.at(0);
178  const Point2f &p1 = pts.at(1);
179  const Point2f &p2 = pts.at(2);
180  const Point2f &p3 = pts.at(3);
181 
182  double a1 = dist(p0, p1);
183  double b1 = dist(p0, p3);
184  double c1 = dist(p1, p3);
185 
186  double a2 = dist(p1, p2);
187  double b2 = dist(p2, p3);
188  double c2 = c1;
189 
190  double s1 = (a1 + b1 + c1) / 2.0;
191  double s2 = (a2 + b2 + c2) / 2.0;
192 
193  a1 = sqrt(s1*(s1-a1)*(s1-b1)*(s1-c1));
194  a2 = sqrt(s2*(s2-a2)*(s2-b2)*(s2-c2));
195  return a1+a2;
196 }
197 
198 // estimate reprojection error
199 static double getReprojectionError(const vector<Point3f> &objectPoints,
200  const vector<Point2f> &imagePoints,
201  const Mat &cameraMatrix, const Mat &distCoeffs,
202  const Vec3d &rvec, const Vec3d &tvec) {
203 
204  vector<Point2f> projectedPoints;
205 
206  cv::projectPoints(objectPoints, rvec, tvec, cameraMatrix,
207  distCoeffs, projectedPoints);
208 
209  // calculate RMS image error
210  double totalError = 0.0;
211  for (unsigned int i=0; i<objectPoints.size(); i++) {
212  double error = dist(imagePoints[i], projectedPoints[i]);
213  totalError += error*error;
214  }
215  double rerror = totalError/(double)objectPoints.size();
216  return rerror;
217 }
218 
220  const cv::Mat &cameraMatrix,
221  const cv::Mat &distCoeffs,
222  vector<Vec3d>& rvecs, vector<Vec3d>& tvecs,
223  vector<double>& reprojectionError) {
224 
225  CV_Assert(markerLength > 0);
226 
227  vector<Point3f> markerObjPoints;
228  int nMarkers = (int)corners.size();
229  rvecs.reserve(nMarkers);
230  tvecs.reserve(nMarkers);
231  reprojectionError.reserve(nMarkers);
232 
233  // for each marker, calculate its pose
234  for (int i = 0; i < nMarkers; i++) {
235  double fiducialSize = markerLength;
236 
237  std::map<int, double>::iterator it = fiducialLens.find(ids[i]);
238  if (it != fiducialLens.end()) {
239  fiducialSize = it->second;
240  }
241 
242  getSingleMarkerObjectPoints(fiducialSize, markerObjPoints);
243  cv::solvePnP(markerObjPoints, corners[i], cameraMatrix, distCoeffs,
244  rvecs[i], tvecs[i]);
245 
246  reprojectionError[i] =
247  getReprojectionError(markerObjPoints, corners[i],
248  cameraMatrix, distCoeffs,
249  rvecs[i], tvecs[i]);
250  }
251 }
252 
253 void FiducialsNode::configCallback(aruco_detect::DetectorParamsConfig & config, uint32_t level)
254 {
255  /* Don't load initial config, since it will overwrite the rosparam settings */
256  if (level == 0xFFFFFFFF) {
257  return;
258  }
259 
260  detectorParams->adaptiveThreshConstant = config.adaptiveThreshConstant;
261  detectorParams->adaptiveThreshWinSizeMin = config.adaptiveThreshWinSizeMin;
262  detectorParams->adaptiveThreshWinSizeMax = config.adaptiveThreshWinSizeMax;
263  detectorParams->adaptiveThreshWinSizeStep = config.adaptiveThreshWinSizeStep;
264  detectorParams->cornerRefinementMaxIterations = config.cornerRefinementMaxIterations;
265  detectorParams->cornerRefinementMinAccuracy = config.cornerRefinementMinAccuracy;
266  detectorParams->cornerRefinementWinSize = config.cornerRefinementWinSize;
267 #if CV_MINOR_VERSION==2 and CV_MAJOR_VERSION==3
268  detectorParams->doCornerRefinement = config.doCornerRefinement;
269 #else
270  if (config.doCornerRefinement) {
271  if (config.cornerRefinementSubpix) {
272  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX;
273  }
274  else {
275  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_CONTOUR;
276  }
277  }
278  else {
279  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;
280  }
281 #endif
282  detectorParams->errorCorrectionRate = config.errorCorrectionRate;
283  detectorParams->minCornerDistanceRate = config.minCornerDistanceRate;
284  detectorParams->markerBorderBits = config.markerBorderBits;
285  detectorParams->maxErroneousBitsInBorderRate = config.maxErroneousBitsInBorderRate;
286  detectorParams->minDistanceToBorder = config.minDistanceToBorder;
287  detectorParams->minMarkerDistanceRate = config.minMarkerDistanceRate;
288  detectorParams->minMarkerPerimeterRate = config.minMarkerPerimeterRate;
289  detectorParams->maxMarkerPerimeterRate = config.maxMarkerPerimeterRate;
290  detectorParams->minOtsuStdDev = config.minOtsuStdDev;
291  detectorParams->perspectiveRemoveIgnoredMarginPerCell = config.perspectiveRemoveIgnoredMarginPerCell;
292  detectorParams->perspectiveRemovePixelPerCell = config.perspectiveRemovePixelPerCell;
293  detectorParams->polygonalApproxAccuracyRate = config.polygonalApproxAccuracyRate;
294 }
295 
296 void FiducialsNode::ignoreCallback(const std_msgs::String& msg)
297 {
298  ignoreIds.clear();
299  pnh.setParam("ignore_fiducials", msg.data);
300  handleIgnoreString(msg.data);
301 }
302 
303 void FiducialsNode::camInfoCallback(const sensor_msgs::CameraInfo::ConstPtr& msg)
304 {
305  if (haveCamInfo) {
306  return;
307  }
308 
309  if (msg->K != boost::array<double, 9>({0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0})) {
310  for (int i=0; i<3; i++) {
311  for (int j=0; j<3; j++) {
312  cameraMatrix.at<double>(i, j) = msg->K[i*3+j];
313  }
314  }
315 
316  for (int i=0; i<5; i++) {
317  distortionCoeffs.at<double>(0,i) = msg->D[i];
318  }
319 
320  haveCamInfo = true;
321  frameId = msg->header.frame_id;
322  }
323  else {
324  ROS_WARN("%s", "CameraInfo message has invalid intrinsics, K matrix all zeros");
325  }
326 }
327 
328 void FiducialsNode::imageCallback(const sensor_msgs::ImageConstPtr & msg)
329 {
330  if (enable_detections == false) {
331  return; //return without doing anything
332  }
333 
334  ROS_INFO("Got image %d", msg->header.seq);
335 
336  fiducial_msgs::FiducialArray fva;
337  fva.header.stamp = msg->header.stamp;
338  fva.header.frame_id = frameId;
339  fva.image_seq = msg->header.seq;
340 
341  try {
343 
344  aruco::detectMarkers(cv_ptr->image, dictionary, corners, ids, detectorParams);
345  ROS_INFO("Detected %d markers", (int)ids.size());
346 
347  for (size_t i=0; i<ids.size(); i++) {
348  if (std::count(ignoreIds.begin(), ignoreIds.end(), ids[i]) != 0) {
349  ROS_INFO("Ignoring id %d", ids[i]);
350  continue;
351  }
352  fiducial_msgs::Fiducial fid;
353  fid.fiducial_id = ids[i];
354 
355  fid.x0 = corners[i][0].x;
356  fid.y0 = corners[i][0].y;
357  fid.x1 = corners[i][1].x;
358  fid.y1 = corners[i][1].y;
359  fid.x2 = corners[i][2].x;
360  fid.y2 = corners[i][2].y;
361  fid.x3 = corners[i][3].x;
362  fid.y3 = corners[i][3].y;
363  fva.fiducials.push_back(fid);
364  }
365 
366  vertices_pub.publish(fva);
367 
368  if(ids.size() > 0) {
369  aruco::drawDetectedMarkers(cv_ptr->image, corners, ids);
370  }
371 
372  if (publish_images) {
373  image_pub.publish(cv_ptr->toImageMsg());
374  }
375  }
376  catch(cv_bridge::Exception & e) {
377  ROS_ERROR("cv_bridge exception: %s", e.what());
378  }
379  catch(cv::Exception & e) {
380  ROS_ERROR("cv exception: %s", e.what());
381  }
382 }
383 
385 {
386  vector <Vec3d> rvecs, tvecs;
387 
388  vision_msgs::Detection2DArray vma;
389  fiducial_msgs::FiducialTransformArray fta;
390  if (vis_msgs) {
391  vma.header.stamp = msg->header.stamp;
392  vma.header.frame_id = frameId;
393  vma.header.seq = msg->header.seq;
394  }
395  else {
396  fta.header.stamp = msg->header.stamp;
397  fta.header.frame_id = frameId;
398  fta.image_seq = msg->header.seq;
399  }
400  frameNum++;
401 
402  if (doPoseEstimation) {
403  try {
404  if (!haveCamInfo) {
405  if (frameNum > 5) {
406  ROS_ERROR("No camera intrinsics");
407  }
408  return;
409  }
410 
411  vector <double>reprojectionError;
412  estimatePoseSingleMarkers((float)fiducial_len,
413  cameraMatrix, distortionCoeffs,
414  rvecs, tvecs,
415  reprojectionError);
416 
417  for (size_t i=0; i<ids.size(); i++) {
418  aruco::drawAxis(cv_ptr->image, cameraMatrix, distortionCoeffs,
419  rvecs[i], tvecs[i], (float)fiducial_len);
420 
421  ROS_INFO("Detected id %d T %.2f %.2f %.2f R %.2f %.2f %.2f", ids[i],
422  tvecs[i][0], tvecs[i][1], tvecs[i][2],
423  rvecs[i][0], rvecs[i][1], rvecs[i][2]);
424 
425  if (std::count(ignoreIds.begin(), ignoreIds.end(), ids[i]) != 0) {
426  ROS_INFO("Ignoring id %d", ids[i]);
427  continue;
428  }
429 
430  double angle = norm(rvecs[i]);
431  Vec3d axis = rvecs[i] / angle;
432  ROS_INFO("angle %f axis %f %f %f",
433  angle, axis[0], axis[1], axis[2]);
434  double object_error =
435  (reprojectionError[i] / dist(corners[i][0], corners[i][2])) *
436  (norm(tvecs[i]) / fiducial_len);
437 
438  // Standard ROS vision_msgs
439  fiducial_msgs::FiducialTransform ft;
440  tf2::Quaternion q;
441  if (vis_msgs) {
442  vision_msgs::Detection2D vm;
443  vision_msgs::ObjectHypothesisWithPose vmh;
444  vmh.id = ids[i];
445  vmh.score = exp(-2 * object_error); // [0, infinity] -> [1,0]
446  vmh.pose.pose.position.x = tvecs[i][0];
447  vmh.pose.pose.position.y = tvecs[i][1];
448  vmh.pose.pose.position.z = tvecs[i][2];
449  q.setRotation(tf2::Vector3(axis[0], axis[1], axis[2]), angle);
450  vmh.pose.pose.orientation.w = q.w();
451  vmh.pose.pose.orientation.x = q.x();
452  vmh.pose.pose.orientation.y = q.y();
453  vmh.pose.pose.orientation.z = q.z();
454 
455  vm.results.push_back(vmh);
456  vma.detections.push_back(vm);
457  }
458  else {
459  ft.fiducial_id = ids[i];
460 
461  ft.transform.translation.x = tvecs[i][0];
462  ft.transform.translation.y = tvecs[i][1];
463  ft.transform.translation.z = tvecs[i][2];
464  q.setRotation(tf2::Vector3(axis[0], axis[1], axis[2]), angle);
465  ft.transform.rotation.w = q.w();
466  ft.transform.rotation.x = q.x();
467  ft.transform.rotation.y = q.y();
468  ft.transform.rotation.z = q.z();
469  ft.fiducial_area = calcFiducialArea(corners[i]);
470  ft.image_error = reprojectionError[i];
471  // Convert image_error (in pixels) to object_error (in meters)
472  ft.object_error =
473  (reprojectionError[i] / dist(corners[i][0], corners[i][2])) *
474  (norm(tvecs[i]) / fiducial_len);
475 
476  fta.transforms.push_back(ft);
477  }
478 
479  // Publish tf for the fiducial relative to the camera
480  if (publishFiducialTf) {
481  if (vis_msgs) {
482  geometry_msgs::TransformStamped ts;
483  ts.transform.translation.x = tvecs[i][0];
484  ts.transform.translation.y = tvecs[i][1];
485  ts.transform.translation.z = tvecs[i][2];
486  ts.transform.rotation.w = q.w();
487  ts.transform.rotation.x = q.x();
488  ts.transform.rotation.y = q.y();
489  ts.transform.rotation.z = q.z();
490  ts.header.frame_id = frameId;
491  ts.header.stamp = msg->header.stamp;
492  ts.child_frame_id = "fiducial_" + std::to_string(ids[i]);
493  broadcaster.sendTransform(ts);
494  }
495  else {
496  geometry_msgs::TransformStamped ts;
497  ts.transform = ft.transform;
498  ts.header.frame_id = frameId;
499  ts.header.stamp = msg->header.stamp;
500  ts.child_frame_id = "fiducial_" + std::to_string(ft.fiducial_id);
501  broadcaster.sendTransform(ts);
502  }
503  }
504  }
505  }
506  catch(cv_bridge::Exception & e) {
507  ROS_ERROR("cv_bridge exception: %s", e.what());
508  }
509  catch(cv::Exception & e) {
510  ROS_ERROR("cv exception: %s", e.what());
511  }
512  }
513  if (vis_msgs)
514  pose_pub.publish(vma);
515  else
516  pose_pub.publish(fta);
517 }
518 
519 void FiducialsNode::handleIgnoreString(const std::string& str)
520 {
521  /*
522  ignogre fiducials can take comma separated list of individual
523  fiducial ids or ranges, eg "1,4,8,9-12,30-40"
524  */
525  std::vector<std::string> strs;
526  boost::split(strs, str, boost::is_any_of(","));
527  for (const string& element : strs) {
528  if (element == "") {
529  continue;
530  }
531  std::vector<std::string> range;
532  boost::split(range, element, boost::is_any_of("-"));
533  if (range.size() == 2) {
534  int start = std::stoi(range[0]);
535  int end = std::stoi(range[1]);
536  ROS_INFO("Ignoring fiducial id range %d to %d", start, end);
537  for (int j=start; j<=end; j++) {
538  ignoreIds.push_back(j);
539  }
540  }
541  else if (range.size() == 1) {
542  int fid = std::stoi(range[0]);
543  ROS_INFO("Ignoring fiducial id %d", fid);
544  ignoreIds.push_back(fid);
545  }
546  else {
547  ROS_ERROR("Malformed ignore_fiducials: %s", element.c_str());
548  }
549  }
550 }
551 
552 bool FiducialsNode::enableDetectionsCallback(std_srvs::SetBool::Request &req,
553  std_srvs::SetBool::Response &res)
554 {
555  enable_detections = req.data;
556  if (enable_detections){
557  res.message = "Enabled aruco detections.";
558  ROS_INFO("Enabled aruco detections.");
559  }
560  else {
561  res.message = "Disabled aruco detections.";
562  ROS_INFO("Disabled aruco detections.");
563  }
564 
565  res.success = true;
566  return true;
567 }
568 
569 
570 FiducialsNode::FiducialsNode() : nh(), pnh("~"), it(nh)
571 {
572  frameNum = 0;
573 
574  // Camera intrinsics
575  cameraMatrix = cv::Mat::zeros(3, 3, CV_64F);
576 
577  // distortion coefficients
578  distortionCoeffs = cv::Mat::zeros(1, 5, CV_64F);
579 
580  haveCamInfo = false;
581  enable_detections = true;
582 
583  int dicno;
584 
585  detectorParams = new aruco::DetectorParameters();
586 
587  pnh.param<bool>("publish_images", publish_images, false);
588  pnh.param<double>("fiducial_len", fiducial_len, 0.14);
589  pnh.param<int>("dictionary", dicno, 7);
590  pnh.param<bool>("do_pose_estimation", doPoseEstimation, true);
591  pnh.param<bool>("publish_fiducial_tf", publishFiducialTf, true);
592  pnh.param<bool>("vis_msgs", vis_msgs, false);
593 
594  std::string str;
595  std::vector<std::string> strs;
596 
597  pnh.param<string>("ignore_fiducials", str, "");
598  handleIgnoreString(str);
599 
600  /*
601  fiducial size can take comma separated list of size: id or size: range,
602  e.g. "200.0: 12, 300.0: 200-300"
603  */
604  pnh.param<string>("fiducial_len_override", str, "");
605  boost::split(strs, str, boost::is_any_of(","));
606  for (const string& element : strs) {
607  if (element == "") {
608  continue;
609  }
610  std::vector<std::string> parts;
611  boost::split(parts, element, boost::is_any_of(":"));
612  if (parts.size() == 2) {
613  double len = std::stod(parts[1]);
614  std::vector<std::string> range;
615  boost::split(range, element, boost::is_any_of("-"));
616  if (range.size() == 2) {
617  int start = std::stoi(range[0]);
618  int end = std::stoi(range[1]);
619  ROS_INFO("Setting fiducial id range %d - %d length to %f",
620  start, end, len);
621  for (int j=start; j<=end; j++) {
622  fiducialLens[j] = len;
623  }
624  }
625  else if (range.size() == 1){
626  int fid = std::stoi(range[0]);
627  ROS_INFO("Setting fiducial id %d length to %f", fid, len);
628  fiducialLens[fid] = len;
629  }
630  else {
631  ROS_ERROR("Malformed fiducial_len_override: %s", element.c_str());
632  }
633  }
634  else {
635  ROS_ERROR("Malformed fiducial_len_override: %s", element.c_str());
636  }
637  }
638 
639  image_pub = it.advertise("/fiducial_images", 1);
640 
641  vertices_pub = nh.advertise<fiducial_msgs::FiducialArray>("fiducial_vertices", 1);
642 
643  if (vis_msgs)
644  pose_pub = nh.advertise<vision_msgs::Detection2DArray>("fiducial_transforms", 1);
645  else
646  pose_pub = nh.advertise<fiducial_msgs::FiducialTransformArray>("fiducial_transforms", 1);
647 
648  dictionary = aruco::getPredefinedDictionary(dicno);
649 
650  img_sub = it.subscribe("camera", 1,
652 
653  vertices_sub = nh.subscribe("fiducial_vertices", 1,
655  caminfo_sub = nh.subscribe("camera_info", 1,
657 
658  ignore_sub = nh.subscribe("ignore_fiducials", 1,
660 
661  service_enable_detections = nh.advertiseService("enable_detections",
663 
664  callbackType = boost::bind(&FiducialsNode::configCallback, this, _1, _2);
665  configServer.setCallback(callbackType);
666 
667  pnh.param<double>("adaptiveThreshConstant", detectorParams->adaptiveThreshConstant, 7);
668  pnh.param<int>("adaptiveThreshWinSizeMax", detectorParams->adaptiveThreshWinSizeMax, 53); /* defailt 23 */
669  pnh.param<int>("adaptiveThreshWinSizeMin", detectorParams->adaptiveThreshWinSizeMin, 3);
670  pnh.param<int>("adaptiveThreshWinSizeStep", detectorParams->adaptiveThreshWinSizeStep, 4); /* default 10 */
671  pnh.param<int>("cornerRefinementMaxIterations", detectorParams->cornerRefinementMaxIterations, 30);
672  pnh.param<double>("cornerRefinementMinAccuracy", detectorParams->cornerRefinementMinAccuracy, 0.01); /* default 0.1 */
673  pnh.param<int>("cornerRefinementWinSize", detectorParams->cornerRefinementWinSize, 5);
674 #if CV_MINOR_VERSION==2 and CV_MAJOR_VERSION==3
675  pnh.param<bool>("doCornerRefinement",detectorParams->doCornerRefinement, true); /* default false */
676 #else
677  bool doCornerRefinement = true;
678  pnh.param<bool>("doCornerRefinement", doCornerRefinement, true);
679  if (doCornerRefinement) {
680  bool cornerRefinementSubPix = true;
681  pnh.param<bool>("cornerRefinementSubPix", cornerRefinementSubPix, true);
682  if (cornerRefinementSubPix) {
683  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX;
684  }
685  else {
686  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_CONTOUR;
687  }
688  }
689  else {
690  detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;
691  }
692 #endif
693  pnh.param<double>("errorCorrectionRate", detectorParams->errorCorrectionRate , 0.6);
694  pnh.param<double>("minCornerDistanceRate", detectorParams->minCornerDistanceRate , 0.05);
695  pnh.param<int>("markerBorderBits", detectorParams->markerBorderBits, 1);
696  pnh.param<double>("maxErroneousBitsInBorderRate", detectorParams->maxErroneousBitsInBorderRate, 0.04);
697  pnh.param<int>("minDistanceToBorder", detectorParams->minDistanceToBorder, 3);
698  pnh.param<double>("minMarkerDistanceRate", detectorParams->minMarkerDistanceRate, 0.05);
699  pnh.param<double>("minMarkerPerimeterRate", detectorParams->minMarkerPerimeterRate, 0.1); /* default 0.3 */
700  pnh.param<double>("maxMarkerPerimeterRate", detectorParams->maxMarkerPerimeterRate, 4.0);
701  pnh.param<double>("minOtsuStdDev", detectorParams->minOtsuStdDev, 5.0);
702  pnh.param<double>("perspectiveRemoveIgnoredMarginPerCell", detectorParams->perspectiveRemoveIgnoredMarginPerCell, 0.13);
703  pnh.param<int>("perspectiveRemovePixelPerCell", detectorParams->perspectiveRemovePixelPerCell, 8);
704  pnh.param<double>("polygonalApproxAccuracyRate", detectorParams->polygonalApproxAccuracyRate, 0.01); /* default 0.05 */
705 
706  ROS_INFO("Aruco detection ready");
707 }
708 
709 int main(int argc, char ** argv) {
710  ros::init(argc, argv, "aruco_detect");
711 
712  FiducialsNode* fd_node = new FiducialsNode();
713 
714  ros::spin();
715 
716  return 0;
717 }
void ignoreCallback(const std_msgs::String &msg)
Subscriber subscribe(const std::string &base_topic, uint32_t queue_size, const boost::function< void(const sensor_msgs::ImageConstPtr &)> &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
ros::Subscriber caminfo_sub
image_transport::Subscriber img_sub
cv::Ptr< aruco::DetectorParameters > detectorParams
dynamic_reconfigure::Server< aruco_detect::DetectorParamsConfig >::CallbackType callbackType
dynamic_reconfigure::Server< aruco_detect::DetectorParamsConfig > configServer
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
f
ros::ServiceServer service_enable_detections
void configCallback(aruco_detect::DetectorParamsConfig &config, uint32_t level)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
std::string frameId
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
std::map< int, double > fiducialLens
cv::Mat distortionCoeffs
vector< int > ids
ros::NodeHandle nh
ros::Subscriber vertices_sub
void imageCallback(const sensor_msgs::ImageConstPtr &msg)
image_transport::ImageTransport it
cv::Ptr< aruco::Dictionary > dictionary
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
#define ROS_WARN(...)
std::vector< int > ignoreIds
int main(int argc, char **argv)
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
ros::Subscriber ignore_sub
ROSCPP_DECL void spin(Spinner &spinner)
void handleIgnoreString(const std::string &str)
static double getReprojectionError(const vector< Point3f > &objectPoints, const vector< Point2f > &imagePoints, const Mat &cameraMatrix, const Mat &distCoeffs, const Vec3d &rvec, const Vec3d &tvec)
double fiducial_len
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
cv_bridge::CvImagePtr cv_ptr
boost::shared_ptr< fiducial_msgs::FiducialArray const > FiducialArrayConstPtr
ros::Publisher pose_pub
void poseEstimateCallback(const FiducialArrayConstPtr &msg)
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
ros::NodeHandle pnh
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
vector< vector< Point2f > > corners
INLINE Rall1d< T, V, S > exp(const Rall1d< T, V, S > &arg)
void setRotation(const Vector3 &axis, const tf2Scalar &angle)
bool enable_detections
image_transport::Publisher image_pub
static double calcFiducialArea(const std::vector< cv::Point2f > &pts)
void estimatePoseSingleMarkers(float markerLength, const cv::Mat &cameraMatrix, const cv::Mat &distCoeffs, vector< Vec3d > &rvecs, vector< Vec3d > &tvecs, vector< double > &reprojectionError)
void camInfoCallback(const sensor_msgs::CameraInfo::ConstPtr &msg)
cv::Mat cameraMatrix
static void getSingleMarkerObjectPoints(float markerLength, vector< Point3f > &objPoints)
Return object points for the system centered in a single marker, given the marker length...
bool publishFiducialTf
tf2_ros::TransformBroadcaster broadcaster
#define ROS_ERROR(...)
bool enableDetectionsCallback(std_srvs::SetBool::Request &req, std_srvs::SetBool::Response &res)
ros::Publisher vertices_pub
static double dist(const cv::Point2f &p1, const cv::Point2f &p2)


aruco_detect
Author(s): Jim Vaughan
autogenerated on Tue Jun 1 2021 03:03:28