tuw_checkerboard_node.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (c) 2017
3  * Florian Beck <florian.beck@tuwien.ac.at>
4  * Markus Bader <markus.bader@tuwien.ac.at>
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 are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  * must display the following acknowledgement:
16  * This product includes software developed by the TU-Wien.
17  * 4. Neither the name of the TU-Wien nor the
18  * names of its contributors may be used to endorse or promote products
19  * derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Markus Bader ''AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  * DISCLAIMED. IN NO EVENT SHALL Markus Bader BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  ***************************************************************************/
32 
33 #include "tuw_checkerboard_node.h"
34 
35 #include <cv_bridge/cv_bridge.h>
36 #include <geometry_msgs/PoseStamped.h>
37 #include <opencv2/highgui/highgui.hpp>
38 #include <opencv2/calib3d.hpp>
39 
40 using namespace cv;
41 using std::vector;
42 using std::string;
43 
44 CheckerboardNode::CheckerboardNode() : nh_private_ ( "~" ) {
46 
47  // Advert checkerboard pose publisher
48  pub_pose_ = nh_.advertise<geometry_msgs::PoseStamped> ( "pose", 1 );
49  // Advert marker publisher
50  pub_markers_ = nh_.advertise<marker_msgs::MarkerDetection> ( "markers", 10 );
51  // Advert fiducial publisher
52  pub_fiducials_ = nh_.advertise<marker_msgs::FiducialDetection> ( "fiducials", 10 );
53 
54  tf_broadcaster_ = std::make_shared<tf::TransformBroadcaster>();
55 
56  nh_.param<std::string> ( "frame_id", checkerboard_frame_id_, "checkerboard" );
57 
58  reconfigureServer_ = new dynamic_reconfigure::Server<tuw_checkerboard::CheckerboardDetectionConfig> ( ros::NodeHandle ( "~" ) );
59  reconfigureFnc_ = boost::bind ( &CheckerboardNode::callbackConfig, this, _1, _2 );
60  reconfigureServer_->setCallback ( reconfigureFnc_ );
61 
62  sub_cam_ = it_.subscribeCamera ( "image", 1, &CheckerboardNode::callbackCamera, this );
63 
64 }
65 
66 void CheckerboardNode::callbackConfig ( tuw_checkerboard::CheckerboardDetectionConfig &_config, uint32_t _level ) {
67  config_ = _config;
68 
69  object_corners_.clear();
70  for ( int i = 0; i < config_.checkerboard_rows; i++ ) {
71  for ( int j = 0; j < config_.checkerboard_columns; j++ ) {
72  object_corners_.push_back ( Point3f ( float ( i * config_.checkerboard_square_size ), float ( j * config_.checkerboard_square_size ), 0.f ) );
73  }
74  }
75 }
76 /*
77  * Camera callback
78  * detects chessboard pattern using opencv and finds camera to image tf using solvePnP
79  */
80 void CheckerboardNode::callbackCamera ( const sensor_msgs::ImageConstPtr& image_msg,
81  const sensor_msgs::CameraInfoConstPtr& info_msg ) {
82 
83  Size patternsize ( config_.checkerboard_columns, config_.checkerboard_rows );
84  cv_bridge::CvImagePtr input_bridge;
85  try {
86  input_bridge = cv_bridge::toCvCopy ( image_msg, sensor_msgs::image_encodings::MONO8 );
87  if(config_.rotate_camera_image_180) {
88  cv::flip(input_bridge->image, image_grey_, -1);
89  } else {
90  image_grey_ = input_bridge->image;
91  }
92  cvtColor ( image_grey_, image_rgb_, CV_GRAY2BGR, 0 );
93 
94  } catch ( cv_bridge::Exception& ex ) {
95  ROS_ERROR ( "[camera_tf_node] Failed to convert image" );
96  return;
97  }
98 
99 
100 
101  int flags = 0;
102  if ( config_.adaptive_thresh ) flags += CV_CALIB_CB_ADAPTIVE_THRESH;
103  if ( config_.normalize_image ) flags += CV_CALIB_CB_NORMALIZE_IMAGE;
104  if ( config_.filter_quads ) flags += CV_CALIB_CB_FILTER_QUADS;
105  if ( config_.fast_check ) flags += CALIB_CB_FAST_CHECK;
106  bool patternfound = findChessboardCorners ( image_grey_, patternsize, image_corners_, flags );
107 
108  if ( patternfound ) {
109  if ( config_.subpixelfit ) {
110 
111  int winSize = config_.subpixelfit_window_size;
112  cornerSubPix ( image_grey_, image_corners_, Size ( winSize, winSize ), Size ( -1, -1 ), TermCriteria ( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1 ) );
113  }
114 
115 
116  cam_model_.fromCameraInfo ( info_msg );
117  intrinsic_matrix_ = cv::Mat_<double>::eye ( 4,4 );
118  Mat camera_matrix = Mat ( cam_model_.intrinsicMatrix() );
119  Mat dist_coeff = Mat ( cam_model_.distortionCoeffs() );
120 
121  if ( config_.input_raw == false ) {
122  Mat projection_matrix = Mat ( cam_model_.projectionMatrix() );
123  camera_matrix = projection_matrix ( cv::Rect ( 0,0,3,3 ) );
124  dist_coeff = Mat::zeros ( 1,5,CV_32F );
125  }
127  for ( int r = 0; r < 3; r++ )
128  for ( int c = 0; c < 3; c++ )
129  intrinsic_matrix_ ( c,r ) = camera_matrix.at<double> ( c, r );
130 
131  const double &fx = intrinsic_matrix_ ( 0, 0 );
132  const double &fy = intrinsic_matrix_ ( 1, 1 );
133  const double &cx = intrinsic_matrix_ ( 0, 2 );
134  const double &cy = intrinsic_matrix_ ( 1, 2 );
135  double f = ( fx+fy ) /2.;
136 
137 
138 
139 
140 
141  Vec3d rotation_vec;
142  Vec3d translation_vec;
143 
144  solvePnP ( object_corners_, image_corners_, cv::Mat ( intrinsic_matrix_, cv::Rect ( 0, 0, 3, 3 ) ) , dist_coeff, rotation_vec, translation_vec );
145 
146  // generate rotation matrix from vector
147  extrinsic_matrix_ = cv::Mat_<double>::eye ( 4,4 );
148  extrinsic_matrix_ ( 0,3 ) = translation_vec ( 0 );
149  extrinsic_matrix_ ( 1,3 ) = translation_vec ( 1 );
150  extrinsic_matrix_ ( 2,3 ) = translation_vec ( 2 );
151  Rodrigues ( rotation_vec, cv::Mat ( extrinsic_matrix_, cv::Rect ( 0, 0, 3, 3 ) ), noArray() );
153 
154  // generate tf model to camera
156  extrinsic_matrix_( 1, 0 ), extrinsic_matrix_( 1, 1 ), extrinsic_matrix_( 1, 2 ),
157  extrinsic_matrix_( 2, 0 ), extrinsic_matrix_( 2, 1 ), extrinsic_matrix_( 2, 2 ) );
158 
159  tf::Vector3 t ( translation_vec ( 0 ), translation_vec ( 1 ), translation_vec ( 2 ) );
160  transform_ = tf::Transform ( R, t );
162 
163  if ( config_.plubish_tf ) {
164  tf_broadcaster_->sendTransform ( tf::StampedTransform ( transform_, image_msg->header.stamp, image_msg->header.frame_id, checkerboard_frame_id_ ) );
165  }
166  if ( config_.plubish_marker ) {
167  marker_detection_.header = image_msg->header;
168  marker_detection_.distance_min = ( f*config_.checkerboard_square_size* ( config_.checkerboard_columns+1 ) ) / ( 2.0*cx );
169  marker_detection_.distance_max = ( f*config_.checkerboard_square_size ) /config_.checkerboard_min_square_size;
170  marker_detection_.distance_max_id = 0; //TODO
171  marker_detection_.view_direction.x = 0; //TODO
172  marker_detection_.view_direction.y = 0; //TODO
173  marker_detection_.view_direction.z = 0; //TODO
174  marker_detection_.view_direction.w = 1; //TODO
175  marker_detection_.fov_horizontal = 2.0 * atan2 ( cx, fx );
176  marker_detection_.fov_vertical = 2.0 * atan2 ( cy, fy );
177  marker_detection_.type = "checkerboard";
178  marker_detection_.markers.resize ( 1 );
179  marker_msgs::Marker &marker = marker_detection_.markers[0];
180 
181  marker.pose.orientation.x = q.x();
182  marker.pose.orientation.y = q.y();
183  marker.pose.orientation.z = q.z();
184  marker.pose.orientation.w = q.w();
185  marker.pose.position.x = t.x();
186  marker.pose.position.y = t.y();
187  marker.pose.position.z = t.z();
189  }
190  if ( config_.publish_fiducials ) {
191  fiducial_detection_.header = image_msg->header;
192  fiducial_detection_.camera_d.resize(5);
193  for (size_t i = 0; i < 5; i++)
194  fiducial_detection_.camera_d[i] = dist_coeff.at<double> (0,i);
195  for (size_t r = 0; r < 3; r++)
196  for (size_t c = 0; c < 3; c++)
197  fiducial_detection_.camera_k[r*3+c] = intrinsic_matrix_ ( r, c );
198  fiducial_detection_.type = "checkerboard";
199  fiducial_detection_.fiducial.resize ( 1 );
200  marker_msgs::Fiducial &fiducial = fiducial_detection_.fiducial[0];
201  fiducial.object_points.resize(object_corners_.size());
202  fiducial.image_points.resize(image_corners_.size());
203  for (size_t i = 0; i < object_corners_.size(); i++){
204  fiducial.object_points[i].x = object_corners_[i].x;
205  fiducial.object_points[i].y = object_corners_[i].y;
206  fiducial.object_points[i].z = object_corners_[i].z;
207 
208  fiducial.image_points[i].x = image_corners_[i].x;
209  fiducial.image_points[i].y = image_corners_[i].y;
210  }
212  }
213  if ( config_.publish_pose ) {
214  geometry_msgs::PoseStamped pose;
215  pose.header = image_msg->header;
216  pose.pose.orientation.x = q.x();
217  pose.pose.orientation.y = q.y();
218  pose.pose.orientation.z = q.z();
219  pose.pose.orientation.w = q.w();
220  pose.pose.position.x = t.x();
221  pose.pose.position.y = t.y();
222  pose.pose.position.z = t.z();
223  pub_pose_.publish(pose);
224  }
225  if ( config_.show_camera_image ) {
226 
227  double square_size = config_.checkerboard_square_size;
228  double nr_of_square = std::max ( config_.checkerboard_columns, config_.checkerboard_rows );
229  double size = square_size * nr_of_square;
230 
231  int font = cv::FONT_HERSHEY_SIMPLEX;
232  double fontScale = 1.0;
233  double thickness = 1.0;
234  double lineType = CV_AA;
235  double lineThickness = 3;
236 
237  cv::Mat_<double> Pi0 = projection_matrix_ * ( cv::Mat_<double> ( 4,1 ) << 0, 0, 0, 1 );
238  cv::Point2d pi0 ( Pi0 ( 0,0 ) / Pi0 ( 0,2 ), Pi0 ( 0,1 ) / Pi0 ( 0,2 ) );
239  cv::circle ( image_rgb_, pi0, 3, CV_RGB ( 255,255,255 ) );
240 
241  cv::Mat_<double> Pi1 = projection_matrix_ * ( cv::Mat_<double> ( 4,1 ) << size, 0, 0, 1 );;
242  cv::Point2d pi1 ( Pi1 ( 0,0 ) / Pi1 ( 0,2 ), Pi1 ( 0,1 ) / Pi1 ( 0,2 ) );
243  cv::circle ( image_rgb_, pi1, 3, CV_RGB ( 255,0,0 ) );
244  putText ( image_rgb_, "X", pi1, font, fontScale, CV_RGB ( 255,0,0 ), thickness, CV_AA );
245  cv::line ( image_rgb_, pi0, pi1, CV_RGB ( 255,0,0 ), lineThickness );
246 
247  cv::Mat_<double> Pi2 = projection_matrix_ * ( cv::Mat_<double> ( 4,1 ) << 0, size, 0, 1 );
248  cv::Point2d pi2 ( Pi2 ( 0,0 ) / Pi2 ( 0,2 ), Pi2 ( 0,1 ) / Pi2 ( 0,2 ) );
249  cv::circle ( image_rgb_, pi2, 3, CV_RGB ( 0,255,0 ) );
250  putText ( image_rgb_, "Y", pi2, font, fontScale, CV_RGB ( 0,255,0 ), thickness, CV_AA );
251  cv::line ( image_rgb_, pi0, pi2, CV_RGB ( 0,255,0 ), lineThickness );
252 
253  cv::Mat_<double> Pi3 = projection_matrix_ * ( cv::Mat_<double> ( 4,1 ) << 0, 0, size, 1 );
254  cv::Point2d pi3 ( Pi3 ( 0,0 ) / Pi3 ( 0,2 ), Pi3 ( 0,1 ) / Pi3 ( 0,2 ) );
255  cv::circle ( image_rgb_, pi3, 3, CV_RGB ( 0,0,255 ) );
256  putText ( image_rgb_, "Z", pi3, font, fontScale, CV_RGB ( 0,0,255 ) , thickness, CV_AA );
257  cv::line ( image_rgb_, pi0, pi3, CV_RGB ( 0,0,255 ), lineThickness );
258 
259  }
260  }
261  if ( config_.show_camera_image ) {
262 
263  drawChessboardCorners ( image_rgb_, patternsize, Mat ( image_corners_ ), patternfound );
264  cv::imshow ( nh_private_.getNamespace(), image_rgb_ );
265  cv::waitKey ( config_.show_camera_image_waitkey );
266  }
267 }
268 
269 
270 int main ( int argc, char** argv ) {
271  ros::init ( argc, argv, "tuw_checkerboard" );
272 
273  CheckerboardNode checkerboard_node;
274 
275  ros::spin();
276 
277  return 0;
278 }
279 
280 
281 
282 
std::shared_ptr< tf::TransformBroadcaster > tf_broadcaster_
const cv::Matx33d & intrinsicMatrix() const
image_geometry::PinholeCameraModel cam_model_
ros::Publisher pub_fiducials_
void publish(const boost::shared_ptr< M > &message) const
f
CameraSubscriber subscribeCamera(const std::string &base_topic, uint32_t queue_size, const CameraSubscriber::Callback &callback, const ros::VoidPtr &tracked_object=ros::VoidPtr(), const TransportHints &transport_hints=TransportHints())
dynamic_reconfigure::Server< tuw_checkerboard::CheckerboardDetectionConfig > * reconfigureServer_
parameter server stuff
ros::NodeHandle nh_private_
int main(int argc, char **argv)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
std::vector< cv::Point3f > object_corners_
std::string checkerboard_frame_id_
marker_msgs::MarkerDetection marker_detection_
image_transport::CameraSubscriber sub_cam_
ROSCPP_DECL void spin(Spinner &spinner)
TFSIMD_FORCE_INLINE const tfScalar & x() const
marker_msgs::FiducialDetection fiducial_detection_
TFSIMD_FORCE_INLINE const tfScalar & z() const
CvImagePtr cvtColor(const CvImageConstPtr &source, const std::string &encoding)
void callbackCamera(const sensor_msgs::ImageConstPtr &image_msg, const sensor_msgs::CameraInfoConstPtr &info_msg)
bool fromCameraInfo(const sensor_msgs::CameraInfo &msg)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
TFSIMD_FORCE_INLINE const tfScalar & y() const
const std::string & getNamespace() const
std::vector< cv::Point2f > image_corners_
cv::Mat_< double > projection_matrix_
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
cv::Mat_< double > extrinsic_matrix_
dynamic_reconfigure::Server< tuw_checkerboard::CheckerboardDetectionConfig >::CallbackType reconfigureFnc_
parameter server stuff
cv::Mat_< double > intrinsic_matrix_
Quaternion getRotation() const
void callbackConfig(tuw_checkerboard::CheckerboardDetectionConfig &_config, uint32_t _level)
callback function on incoming parameter changes
tf::Transform transform_
const cv::Matx34d & projectionMatrix() const
const cv::Mat_< double > & distortionCoeffs() const
tuw_checkerboard::CheckerboardDetectionConfig config_
ros::Publisher pub_markers_
ros::Publisher pub_pose_
#define ROS_ERROR(...)


tuw_checkerboard
Author(s): Florian Beck , Markus Bader
autogenerated on Mon Jun 10 2019 15:42:06