44 #include <visualization_msgs/Marker.h> 48 #include <dynamic_reconfigure/server.h> 49 #include <std_srvs/SetBool.h> 50 #include <std_msgs/String.h> 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" 58 #include <vision_msgs/Detection2D.h> 59 #include <vision_msgs/Detection2DArray.h> 60 #include <vision_msgs/ObjectHypothesisWithPose.h> 62 #include <opencv2/highgui.hpp> 63 #include <opencv2/aruco.hpp> 64 #include <opencv2/calib3d.hpp> 68 #include <boost/algorithm/string.hpp> 69 #include <boost/shared_ptr.hpp> 118 void handleIgnoreString(
const std::string& str);
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);
127 void ignoreCallback(
const std_msgs::String &msg);
130 void camInfoCallback(
const sensor_msgs::CameraInfo::ConstPtr &msg);
131 void configCallback(aruco_detect::DetectorParamsConfig &config, uint32_t level);
133 bool enableDetectionsCallback(std_srvs::SetBool::Request &req,
134 std_srvs::SetBool::Response &res);
136 dynamic_reconfigure::Server<aruco_detect::DetectorParamsConfig>
configServer;
137 dynamic_reconfigure::Server<aruco_detect::DetectorParamsConfig>::CallbackType
callbackType;
149 CV_Assert(markerLength > 0);
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));
160 static double dist(
const cv::Point2f &p1,
const cv::Point2f &p2)
170 return sqrt(dx*dx + dy*dy);
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);
182 double a1 =
dist(p0, p1);
183 double b1 =
dist(p0, p3);
184 double c1 =
dist(p1, p3);
186 double a2 =
dist(p1, p2);
187 double b2 =
dist(p2, p3);
190 double s1 = (a1 + b1 + c1) / 2.0;
191 double s2 = (a2 + b2 + c2) / 2.0;
193 a1 =
sqrt(s1*(s1-a1)*(s1-b1)*(s1-c1));
194 a2 =
sqrt(s2*(s2-a2)*(s2-b2)*(s2-c2));
200 const vector<Point2f> &imagePoints,
201 const Mat &cameraMatrix,
const Mat &distCoeffs,
202 const Vec3d &rvec,
const Vec3d &tvec) {
204 vector<Point2f> projectedPoints;
206 cv::projectPoints(objectPoints, rvec, tvec, cameraMatrix,
207 distCoeffs, projectedPoints);
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;
215 double rerror = totalError/(double)objectPoints.size();
220 const cv::Mat &cameraMatrix,
221 const cv::Mat &distCoeffs,
222 vector<Vec3d>& rvecs, vector<Vec3d>& tvecs,
223 vector<double>& reprojectionError) {
225 CV_Assert(markerLength > 0);
227 vector<Point3f> markerObjPoints;
228 int nMarkers = (
int)corners.size();
229 rvecs.reserve(nMarkers);
230 tvecs.reserve(nMarkers);
231 reprojectionError.reserve(nMarkers);
234 for (
int i = 0; i < nMarkers; i++) {
235 double fiducialSize = markerLength;
237 std::map<int, double>::iterator it = fiducialLens.find(ids[i]);
238 if (it != fiducialLens.end()) {
239 fiducialSize = it->second;
243 cv::solvePnP(markerObjPoints, corners[i], cameraMatrix, distCoeffs,
246 reprojectionError[i] =
248 cameraMatrix, distCoeffs,
256 if (level == 0xFFFFFFFF) {
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;
270 if (config.doCornerRefinement) {
271 if (config.cornerRefinementSubpix) {
272 detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX;
275 detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_CONTOUR;
279 detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;
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;
299 pnh.setParam(
"ignore_fiducials", msg.data);
300 handleIgnoreString(msg.data);
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];
316 for (
int i=0; i<5; i++) {
317 distortionCoeffs.at<
double>(0,i) = msg->D[i];
321 frameId = msg->header.frame_id;
324 ROS_WARN(
"%s",
"CameraInfo message has invalid intrinsics, K matrix all zeros");
330 if (enable_detections ==
false) {
334 ROS_INFO(
"Got image %d", msg->header.seq);
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;
344 aruco::detectMarkers(cv_ptr->image, dictionary, corners, ids, detectorParams);
345 ROS_INFO(
"Detected %d markers", (
int)ids.size());
347 for (
size_t i=0; i<ids.size(); i++) {
348 if (std::count(ignoreIds.begin(), ignoreIds.end(), ids[i]) != 0) {
352 fiducial_msgs::Fiducial fid;
353 fid.fiducial_id = ids[i];
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);
366 vertices_pub.publish(fva);
369 aruco::drawDetectedMarkers(cv_ptr->image, corners, ids);
372 if (publish_images) {
373 image_pub.publish(cv_ptr->toImageMsg());
377 ROS_ERROR(
"cv_bridge exception: %s", e.what());
379 catch(cv::Exception & e) {
386 vector <Vec3d> rvecs, tvecs;
388 vision_msgs::Detection2DArray vma;
389 fiducial_msgs::FiducialTransformArray fta;
391 vma.header.stamp = msg->header.stamp;
392 vma.header.frame_id = frameId;
393 vma.header.seq = msg->header.seq;
396 fta.header.stamp = msg->header.stamp;
397 fta.header.frame_id = frameId;
398 fta.image_seq = msg->header.seq;
402 if (doPoseEstimation) {
411 vector <double>reprojectionError;
412 estimatePoseSingleMarkers((
float)fiducial_len,
413 cameraMatrix, distortionCoeffs,
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);
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]);
425 if (std::count(ignoreIds.begin(), ignoreIds.end(), ids[i]) != 0) {
430 double angle = norm(rvecs[i]);
431 Vec3d axis = rvecs[i] / angle;
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);
439 fiducial_msgs::FiducialTransform ft;
442 vision_msgs::Detection2D vm;
443 vision_msgs::ObjectHypothesisWithPose vmh;
445 vmh.score =
exp(-2 * object_error);
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();
455 vm.results.push_back(vmh);
456 vma.detections.push_back(vm);
459 ft.fiducial_id = ids[i];
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();
470 ft.image_error = reprojectionError[i];
473 (reprojectionError[i] /
dist(corners[i][0], corners[i][2])) *
474 (norm(tvecs[i]) / fiducial_len);
476 fta.transforms.push_back(ft);
480 if (publishFiducialTf) {
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);
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);
507 ROS_ERROR(
"cv_bridge exception: %s", e.what());
509 catch(cv::Exception & e) {
514 pose_pub.publish(vma);
516 pose_pub.publish(fta);
525 std::vector<std::string> strs;
526 boost::split(strs, str, boost::is_any_of(
","));
527 for (
const string& element : strs) {
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);
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);
547 ROS_ERROR(
"Malformed ignore_fiducials: %s", element.c_str());
553 std_srvs::SetBool::Response &res)
555 enable_detections = req.data;
556 if (enable_detections){
557 res.message =
"Enabled aruco detections.";
558 ROS_INFO(
"Enabled aruco detections.");
561 res.message =
"Disabled aruco detections.";
562 ROS_INFO(
"Disabled aruco detections.");
595 std::vector<std::string> strs;
597 pnh.
param<
string>(
"ignore_fiducials", str,
"");
604 pnh.
param<
string>(
"fiducial_len_override", str,
"");
605 boost::split(strs, str, boost::is_any_of(
","));
606 for (
const string& element : strs) {
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",
621 for (
int j=start; j<=end; j++) {
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);
631 ROS_ERROR(
"Malformed fiducial_len_override: %s", element.c_str());
635 ROS_ERROR(
"Malformed fiducial_len_override: %s", element.c_str());
648 dictionary = aruco::getPredefinedDictionary(dicno);
674 #if CV_MINOR_VERSION==2 and CV_MAJOR_VERSION==3 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;
686 detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_CONTOUR;
690 detectorParams->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;
702 pnh.
param<
double>(
"perspectiveRemoveIgnoredMarginPerCell",
detectorParams->perspectiveRemoveIgnoredMarginPerCell, 0.13);
709 int main(
int argc,
char ** argv) {
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)
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())
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
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)
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)
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val) const
cv_bridge::CvImagePtr cv_ptr
boost::shared_ptr< fiducial_msgs::FiducialArray const > FiducialArrayConstPtr
void poseEstimateCallback(const FiducialArrayConstPtr &msg)
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
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)
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)
static void getSingleMarkerObjectPoints(float markerLength, vector< Point3f > &objPoints)
Return object points for the system centered in a single marker, given the marker length...
tf2_ros::TransformBroadcaster broadcaster
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)