TrainMarkerBundle.cpp
Go to the documentation of this file.
1 /*
2  Software License Agreement (BSD License)
3 
4  Copyright (c) 2012, Scott Niekum
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions
9  are met:
10 
11  * Redistributions of source code must retain the above copyright
12  notice, this list of conditions and the following disclaimer.
13  * Redistributions in binary form must reproduce the above
14  copyright notice, this list of conditions and the following
15  disclaimer in the documentation and/or other materials provided
16  with the distribution.
17  * Neither the name of the Willow Garage nor the names of its
18  contributors may be used to endorse or promote products derived
19  from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  POSSIBILITY OF SUCH DAMAGE.
33 
34  author: Scott Niekum
35 */
36 
37 
42 #include "ar_track_alvar/Shared.h"
43 #include <cv_bridge/cv_bridge.h>
44 #include <ar_track_alvar_msgs/AlvarMarker.h>
45 #include <ar_track_alvar_msgs/AlvarMarkers.h>
46 #include <tf/transform_listener.h>
49 #include <Eigen/StdVector>
50 
51 using namespace alvar;
52 using namespace std;
53 
54 #define MAIN_MARKER 1
55 #define VISIBLE_MARKER 2
56 #define GHOST_MARKER 3
57 
63 ar_track_alvar_msgs::AlvarMarkers arPoseMarkers_;
71 
72 bool init=true;
73 bool add_measurement=false;
74 bool optimize = false;
75 bool optimize_done = false;
76 
77 double marker_size;
80 std::string cam_image_topic;
81 std::string cam_info_topic;
82 std::string output_frame;
84 
85 double GetMultiMarkerPose(IplImage *image, Pose &pose);
86 void getCapCallback (const sensor_msgs::ImageConstPtr & image_msg);
87 int keyCallback(int key);
88 void makeMarkerMsgs(int type, int id, Pose &p, sensor_msgs::ImageConstPtr image_msg, tf::StampedTransform &CamToOutput, visualization_msgs::Marker *rvizMarker, ar_track_alvar_msgs::AlvarMarker *ar_pose_marker);
89 
90 
91 double GetMultiMarkerPose(IplImage *image, Pose &pose) {
92  static bool init=true;
93 
94  if (init) {
95  init=false;
96  vector<int> id_vector;
97  for(int i = 0; i < nof_markers; ++i)
98  id_vector.push_back(i);
99  // We make the initialization for MultiMarkerBundle using MultiMarkerInitializer
100  // Each marker needs to be visible in at least two images and at most 64 image are used.
101  multi_marker_init = new MultiMarkerInitializer(id_vector, 2, 64);
102  pose.Reset();
103  multi_marker_init->PointCloudAdd(id_vector[0], marker_size, pose);
104  multi_marker_bundle = new MultiMarkerBundle(id_vector);
105  marker_detector.SetMarkerSize(marker_size);
106  }
107 
108  double error = -1;
109  if (!optimize_done) {
110  if (marker_detector.Detect(image, cam, true, false, max_new_marker_error, max_track_error, CVSEQ, true)) {
111  error = multi_marker_init->Update(marker_detector.markers, cam, pose);
112  }
113  }
114  else {
115  if (marker_detector.Detect(image, cam, true, false, max_new_marker_error, max_track_error, CVSEQ, true)) {
116  error = multi_marker_bundle->Update(marker_detector.markers, cam, pose);
117  if ((multi_marker_bundle->SetTrackMarkers(marker_detector, cam, pose, image) > 0) && (marker_detector.DetectAdditional(image, cam, false) > 0))
118  {
119  error = multi_marker_bundle->Update(marker_detector.markers, cam, pose);
120  }
121  }
122  }
123 
124  if (add_measurement) {
125  cout << "Markers seen: " << marker_detector.markers->size() << "\n";
126  if (marker_detector.markers->size() >= 2) {
127  cout<<"Adding measurement..."<<endl;
128  multi_marker_init->MeasurementsAdd(marker_detector.markers);
129  }
130  else{
131  cout << "Not enough markers to capture measurement\n";
132  }
133  add_measurement=false;
134  }
135 
136  if (optimize) {
137  cout<<"Initializing..."<<endl;
138  if (!multi_marker_init->Initialize(cam)) {
139  cout<<"Initialization failed, add some more measurements."<<endl;
140 
141  } else {
142  // Reset the bundle adjuster.
143  multi_marker_bundle->Reset();
144  multi_marker_bundle->MeasurementsReset();
145  // Copy all measurements into the bundle adjuster.
146  for (int i = 0; i < multi_marker_init->getMeasurementCount(); ++i) {
147  Pose p2;
148  multi_marker_init->getMeasurementPose(i, cam, p2);
149  const std::vector<MultiMarkerInitializer::MarkerMeasurement, Eigen::aligned_allocator<MultiMarkerInitializer::MarkerMeasurement> > markers = multi_marker_init->getMeasurementMarkers(i);
150  multi_marker_bundle->MeasurementsAdd(&markers, p2);
151  }
152  // Initialize the bundle adjuster with initial marker poses.
153  multi_marker_bundle->PointCloudCopy(multi_marker_init);
154  cout<<"Optimizing..."<<endl;
155  if (multi_marker_bundle->Optimize(cam, 0.01, 20)) {
156  cout<<"Optimizing done"<<endl;
157  optimize_done=true;
158 
159  } else {
160  cout<<"Optimizing FAILED!"<<endl;
161  }
162  }
163  optimize=false;
164  }
165  return error;
166 }
167 
168 void makeMarkerMsgs(int type, int id, Pose &p, sensor_msgs::ImageConstPtr image_msg, tf::StampedTransform &CamToOutput, visualization_msgs::Marker *rvizMarker, ar_track_alvar_msgs::AlvarMarker *ar_pose_marker){
169  double px,py,pz,qx,qy,qz,qw;
170 
171  px = p.translation[0]/100.0;
172  py = p.translation[1]/100.0;
173  pz = p.translation[2]/100.0;
174  qx = p.quaternion[1];
175  qy = p.quaternion[2];
176  qz = p.quaternion[3];
177  qw = p.quaternion[0];
178 
179  //Get the marker pose in the camera frame
180  tf::Quaternion rotation (qx,qy,qz,qw);
181  tf::Vector3 origin (px,py,pz);
182  tf::Transform t (rotation, origin);
183 
184  tf::Vector3 markerOrigin (0, 0, 0);
185  tf::Transform m (tf::Quaternion::getIdentity (), markerOrigin);
186  tf::Transform markerPose = t * m;
187 
188  //Publish the transform from the camera to the marker
189  if(type==MAIN_MARKER){
190  std::string markerFrame = "ar_marker_";
191  std::stringstream out;
192  out << id;
193  std::string id_string = out.str();
194  markerFrame += id_string;
195  tf::StampedTransform camToMarker (t, image_msg->header.stamp, image_msg->header.frame_id, markerFrame.c_str());
196  tf_broadcaster->sendTransform(camToMarker);
197  }
198 
199  //Create the rviz visualization message
200  tf::poseTFToMsg (markerPose, rvizMarker->pose);
201  rvizMarker->header.frame_id = image_msg->header.frame_id;
202  rvizMarker->header.stamp = image_msg->header.stamp;
203  rvizMarker->id = id;
204 
205  rvizMarker->scale.x = 1.0 * marker_size/100.0;
206  rvizMarker->scale.y = 1.0 * marker_size/100.0;
207  rvizMarker->scale.z = 0.2 * marker_size/100.0;
208  rvizMarker->ns = "basic_shapes";
209  rvizMarker->type = visualization_msgs::Marker::CUBE;
210  rvizMarker->action = visualization_msgs::Marker::ADD;
211 
212  //Determine a color and opacity, based on marker type
213  if(type==MAIN_MARKER){
214  rvizMarker->color.r = 1.0f;
215  rvizMarker->color.g = 0.0f;
216  rvizMarker->color.b = 0.0f;
217  rvizMarker->color.a = 1.0;
218  }
219  else if(type==VISIBLE_MARKER){
220  rvizMarker->color.r = 0.0f;
221  rvizMarker->color.g = 1.0f;
222  rvizMarker->color.b = 0.0f;
223  rvizMarker->color.a = 1.0;
224  }
225  else if(type==GHOST_MARKER){
226  rvizMarker->color.r = 0.0f;
227  rvizMarker->color.g = 0.0f;
228  rvizMarker->color.b = 1.0f;
229  rvizMarker->color.a = 0.5;
230  }
231 
232  rvizMarker->lifetime = ros::Duration (1.0);
233 
234  //Get the pose of the tag in the camera frame, then convert to the output frame (usually torso)
235  tf::Transform tagPoseOutput = CamToOutput * markerPose;
236 
237  //Create the pose marker message
238  tf::poseTFToMsg (tagPoseOutput, ar_pose_marker->pose.pose);
239  ar_pose_marker->header.frame_id = output_frame;
240  ar_pose_marker->header.stamp = image_msg->header.stamp;
241  ar_pose_marker->id = id;
242 }
243 
244 
245 void getCapCallback (const sensor_msgs::ImageConstPtr & image_msg)
246 {
247  //Check if automatic measurement collection should be triggered
248  if(auto_collect){
249  auto_count++;
250  add_measurement = true;
251  if(auto_count >= 5)
252  auto_collect = false;
253  }
254 
255  //If we've already gotten the cam info, then go ahead
256  if(cam->getCamInfo_){
257  try{
258  //Get the transformation from the Camera to the output frame for this image capture
259  tf::StampedTransform CamToOutput;
260  try{
261  tf_listener->waitForTransform(image_msg->header.frame_id, output_frame, image_msg->header.stamp, ros::Duration(1.0));
262  tf_listener->lookupTransform(image_msg->header.frame_id, output_frame, image_msg->header.stamp, CamToOutput);
263  }
264  catch (tf::TransformException ex){
265  ROS_ERROR("%s",ex.what());
266  }
267 
268  visualization_msgs::Marker rvizMarker;
269  ar_track_alvar_msgs::AlvarMarker ar_pose_marker;
270  arPoseMarkers_.markers.clear ();
271 
272  //Convert the image
274 
275  //Get the estimated pose of the main markers by using all the markers in each bundle
276 
277  // GetMultiMarkersPoses expects an IplImage*, but as of ros groovy, cv_bridge gives
278  // us a cv::Mat. I'm too lazy to change to cv::Mat throughout right now, so I
279  // do this conversion here -jbinney
280  IplImage ipl_image = cv_ptr_->image;
281 
282  //Get the estimated pose of the main marker using the whole bundle
283  static Pose bundlePose;
284  double error = GetMultiMarkerPose(&ipl_image, bundlePose);
285 
286  if (optimize_done){
287  //Draw the main marker
288  makeMarkerMsgs(MAIN_MARKER, 0, bundlePose, image_msg, CamToOutput, &rvizMarker, &ar_pose_marker);
289  rvizMarkerPub_.publish (rvizMarker);
290  arPoseMarkers_.markers.push_back (ar_pose_marker);
291  }
292  //Now grab the poses of the other markers that are visible
293  for (size_t i=0; i<marker_detector.markers->size(); i++)
294  {
295  int id = (*(marker_detector.markers))[i].GetId();
296 
297  //Don't do the main marker (id=0) if we've already drawn it
298  if(id > 0 || ((!optimize_done) && id==0)){
299  Pose p = (*(marker_detector.markers))[i].pose;
300  makeMarkerMsgs(VISIBLE_MARKER, id, p, image_msg, CamToOutput, &rvizMarker, &ar_pose_marker);
301  rvizMarkerPub_.publish (rvizMarker);
302  arPoseMarkers_.markers.push_back (ar_pose_marker);
303  }
304  }
305  arMarkerPub_.publish (arPoseMarkers_);
306  }
307  catch (cv_bridge::Exception& e){
308  ROS_ERROR ("Could not convert from '%s' to 'rgb8'.", image_msg->encoding.c_str ());
309  }
310  }
311 
312  //Sleep if we are auto collecting
313  if(auto_collect)
314  usleep(1000000);
315 }
316 
317 
318 //Do something based on keystrokes from menu
319 int keyProcess(int key)
320 {
321  if(key == 'r')
322  {
323  cout<<"Reseting multi marker"<<endl;
324  multi_marker_init->Reset();
325  multi_marker_init->MeasurementsReset();
326  multi_marker_bundle->Reset();
327  multi_marker_bundle->MeasurementsReset();
328  add_measurement = false;
329  optimize = false;
330  optimize_done = false;
331  }
332  else if(key == 'l')
333  {
334  if(multi_marker_bundle->Load("mmarker.xml", FILE_FORMAT_XML))
335  {
336  cout<<"Multi marker loaded"<<endl;
337  multi_marker_init->PointCloudCopy(multi_marker_bundle);
338  optimize_done = true;
339  }
340  else
341  cout<<"Cannot load multi marker"<<endl;
342  }
343  else if(key == 's')
344  {
345  if(multi_marker_bundle->Save("mmarker.xml", FILE_FORMAT_XML))
346  cout<<"Multi marker saved"<<endl;
347  else
348  cout<<"Cannot save multi marker"<<endl;
349  }
350  else if(key == 'p')
351  {
352  add_measurement=true;
353  }
354  else if(key == 'a')
355  {
356  auto_count = 0;
357  auto_collect = true;
358  }
359  else if(key == 'o')
360  {
361  optimize=true;
362  }
363  else if(key == 'q')
364  {
365  exit(0);
366  }
367  else return key;
368 
369  return 0;
370 }
371 
372 
373 int main(int argc, char *argv[])
374 {
375  ros::init (argc, argv, "marker_detect");
376  ros::NodeHandle n;
377 
378  if(argc < 8){
379  std::cout << std::endl;
380  cout << "Not enough arguments provided." << endl;
381  cout << "Usage: ./trainMarkerBundle <num of markers> <marker size in cm> <max new marker error> <max track error> <cam image topic> <cam info topic> <output frame>" << endl;
382  std::cout << std::endl;
383  return 0;
384  }
385 
386  // Get params from command line
387  nof_markers = atoi(argv[1]);
388  marker_size = atof(argv[2]);
389  max_new_marker_error = atof(argv[3]);
390  max_track_error = atof(argv[4]);
391  cam_image_topic = argv[5];
392  cam_info_topic = argv[6];
393  output_frame = argv[7];
394  marker_detector.SetMarkerSize(marker_size);
395 
396  cam = new Camera(n, cam_info_topic);
397  tf_listener = new tf::TransformListener(n);
398  tf_broadcaster = new tf::TransformBroadcaster();
399  arMarkerPub_ = n.advertise < ar_track_alvar_msgs::AlvarMarkers > ("ar_pose_marker", 0);
400  rvizMarkerPub_ = n.advertise < visualization_msgs::Marker > ("visualization_marker", 0);
401 
402  //Give tf a chance to catch up before the camera callback starts asking for transforms
403  ros::Duration(1.0).sleep();
404  ros::spinOnce();
405 
406  //Subscribe to camera message
407  ROS_INFO ("Subscribing to image topic");
409  cam_sub_ = it_.subscribe (cam_image_topic, 1, &getCapCallback);
410 
411  // Output usage message
412  std::cout << std::endl;
413  std::cout << "Keyboard Shortcuts:" << std::endl;
414  std::cout << " l: load marker configuration from mmarker.txt" << std::endl;
415  std::cout << " s: save marker configuration to mmarker.txt" << std::endl;
416  std::cout << " r: reset marker configuration" << std::endl;
417  std::cout << " p: add measurement" << std::endl;
418  std::cout << " a: auto add measurements (captures once a second for 5 seconds)" << std::endl;
419  std::cout << " o: optimize bundle" << std::endl;
420  std::cout << " q: quit" << std::endl;
421  std::cout << std::endl;
422  std::cout << "Please type commands with the openCV window selected" << std::endl;
423  std::cout << std::endl;
424 
425  cvNamedWindow("Command input window", CV_WINDOW_AUTOSIZE);
426 
427  while(1){
428  int key = cvWaitKey(20);
429  if(key >= 0)
430  keyProcess(key);
431  ros::spinOnce();
432  }
433 
434  return 0;
435 }
double getMeasurementPose(int measurement, Camera *cam, Pose &pose)
Main ALVAR namespace.
Definition: Alvar.h:174
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())
image_transport::Subscriber cam_sub_
std::string cam_image_topic
virtual void Reset()
Resets the multi marker.
Definition: MultiMarker.cpp:50
int keyCallback(int key)
void MeasurementsAdd(const std::vector< M, Eigen::aligned_allocator< M > > *markers, const Pose &camera_pose)
Adds new measurements that are used in bundle adjustment.
void publish(const boost::shared_ptr< M > &message) const
bool optimize
void SetMarkerSize(double _edge_length=1, int _res=5, double _margin=2)
tf::TransformListener * tf_listener
bool sleep() const
Camera * cam
This file implements a generic marker detector.
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
void getCapCallback(const sensor_msgs::ImageConstPtr &image_msg)
bool Optimize(Camera *_cam, double stop, int max_iter, Optimization::OptimizeMethod method=Optimization::TUKEY_LM)
Runs the bundle adjustment optimization.
unsigned char * image
Definition: GlutViewer.cpp:155
Initializes multi marker by estimating their relative positions from one or more images.
std::string cam_info_topic
std::string output_frame
bool auto_collect
int DetectAdditional(IplImage *image, Camera *cam, bool visualize=false, double max_track_error=0.2)
#define GHOST_MARKER
Simple Camera class for calculating distortions, orientation or projections with pre-calibrated camer...
Definition: Camera.h:82
double GetMultiMarkerPose(IplImage *image, Pose &pose)
int keyProcess(int key)
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
std::vector< M, Eigen::aligned_allocator< M > > * markers
MultiMarkerBundle * multi_marker_bundle
MarkerDetector for detecting markers of type M
int main(int argc, char *argv[])
double max_track_error
static const Quaternion & getIdentity()
#define ROS_INFO(...)
ar_track_alvar_msgs::AlvarMarkers arPoseMarkers_
MultiMarkerInitializer * multi_marker_init
void Reset()
Definition: Pose.cpp:69
Multi marker that uses bundle adjustment to refine the 3D positions of the markers (point cloud)...
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
void PointCloudCopy(const MultiMarker *m)
Copies the 3D point cloud from other multi marker object.
void sendTransform(const StampedTransform &transform)
Pose representation derived from the Rotation class
Definition: Pose.h:50
int auto_count
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
XML file format.
Definition: FileFormat.h:66
void PointCloudAdd(int marker_id, double edge_length, Pose &pose)
Adds marker corners to 3D point cloud of multi marker.
cv_bridge::CvImagePtr cv_ptr_
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
double quaternion[4]
Definition: Rotation.h:48
void makeMarkerMsgs(int type, int id, Pose &p, sensor_msgs::ImageConstPtr image_msg, tf::StampedTransform &CamToOutput, visualization_msgs::Marker *rvizMarker, ar_track_alvar_msgs::AlvarMarker *ar_pose_marker)
bool init
void MeasurementsAdd(MarkerIterator &begin, MarkerIterator &end)
static void poseTFToMsg(const Pose &bt, geometry_msgs::Pose &msg)
This file implements a initialization algorithm to create a multi-marker field configuration.
MarkerDetector< MarkerData > marker_detector
bool Load(const char *fname, FILE_FORMAT format=FILE_FORMAT_DEFAULT)
Loads multi marker configuration from a text file.
int Detect(IplImage *image, Camera *cam, bool track=false, bool visualize=false, double max_new_marker_error=0.08, double max_track_error=0.2, LabelingMethod labeling_method=CVSEQ, bool update_pose=true)
Detect Marker &#39;s from image
bool optimize_done
double max_new_marker_error
ros::Publisher arMarkerPub_
bool add_measurement
This file implements an algorithm to create a multi-marker field configuration.
#define VISIBLE_MARKER
double marker_size
void MeasurementsReset()
Resets the measurements and camera poses that are stored for bundle adjustment. If something goes fro...
double translation[4]
Definition: Pose.h:54
ROSCPP_DECL void spinOnce()
int SetTrackMarkers(MarkerDetector< M > &marker_detector, Camera *cam, Pose &pose, IplImage *image=0)
Set new markers to be tracked for MarkerDetector Sometimes the MultiMarker implementation knows more ...
Definition: MultiMarker.h:191
#define ROS_ERROR(...)
const std::vector< MarkerMeasurement, Eigen::aligned_allocator< MarkerMeasurement > > & getMeasurementMarkers(int measurement)
double Update(const std::vector< M, Eigen::aligned_allocator< M > > *markers, Camera *cam, Pose &pose, IplImage *image=0)
Calls GetPose to obtain camera pose.
Definition: MultiMarker.h:120
#define MAIN_MARKER
bool Save(const char *fname, FILE_FORMAT format=FILE_FORMAT_DEFAULT)
Saves multi marker configuration to a text file.
tf::TransformBroadcaster * tf_broadcaster
ros::Publisher rvizMarkerPub_
int nof_markers
bool getCamInfo_
Definition: Camera.h:92


ar_track_alvar
Author(s): Scott Niekum
autogenerated on Mon Jun 10 2019 12:47:04