00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <ros/ros.h>
00037 #include <rospack/rospack.h>
00038 #include <opencv/highgui.h>
00039 #include <cv_bridge/cv_bridge.h>
00040 #include <opencv/cv.hpp>
00041 #include <posedetection_msgs/Feature0DDetect.h>
00042 #include <posedetection_msgs/ImageFeature0D.h>
00043 #include <posedetection_msgs/ObjectDetection.h>
00044 #include <posedetection_msgs/Object6DPose.h>
00045 #include <geometry_msgs/PoseStamped.h>
00046 #include <geometry_msgs/Pose.h>
00047 #include <geometry_msgs/Quaternion.h>
00048 #include <sensor_msgs/image_encodings.h>
00049 #include <image_transport/image_transport.h>
00050 #include <image_geometry/pinhole_camera_model.h>
00051 #include <tf/tf.h>
00052 #include <boost/shared_ptr.hpp>
00053 #include <boost/foreach.hpp>
00054 #include <boost/filesystem.hpp>
00055 #include <vector>
00056 #include <sstream>
00057 #include <iostream>
00058 #include <dynamic_reconfigure/server.h>
00059 #include <jsk_perception/point_pose_extractorConfig.h>
00060 #include <jsk_perception/SetTemplate.h>
00061 #include <jsk_topic_tools/log_utils.h>
00062 namespace enc = sensor_msgs::image_encodings;
00063
00064 bool _first_sample_change;
00065
00066 void features2keypoint (posedetection_msgs::Feature0D features,
00067 std::vector<cv::KeyPoint>& keypoints,
00068 cv::Mat& descriptors){
00069 keypoints.resize(features.scales.size());
00070 descriptors.create(features.scales.size(),features.descriptor_dim,CV_32FC1);
00071 std::vector<cv::KeyPoint>::iterator keypoint_it = keypoints.begin();
00072 for ( int i=0; keypoint_it != keypoints.end(); ++keypoint_it, ++i ) {
00073 *keypoint_it = cv::KeyPoint(cv::Point2f(features.positions[i*2+0],
00074 features.positions[i*2+1]),
00075 features.descriptor_dim,
00076 features.orientations[i],
00077 0,
00078 features.scales[i]
00079 );
00080 for (int j = 0; j < features.descriptor_dim; j++){
00081 descriptors.at<float>(i,j) =
00082 features.descriptors[i*features.descriptor_dim+j];
00083 }
00084 }
00085 }
00086
00087
00088 class Matching_Template
00089 {
00090 public:
00091 std::string _matching_frame;
00092 cv::Mat _template_img;
00093 std::vector<cv::KeyPoint> _template_keypoints;
00094 cv::Mat _template_descriptors;
00095 int _original_width_size;
00096 int _original_height_size;
00097 double _template_width;
00098 double _template_height;
00099 tf::Transform _relativepose;
00100 cv::Mat _affine_matrix;
00101 std::string _window_name;
00102
00103 double _reprojection_threshold;
00104
00105 double _distanceratio_threshold;
00106 std::vector<cv::Point2d> _correspondances;
00107 cv::Mat _previous_stack_img;
00108
00109 Matching_Template(){
00110 }
00111 Matching_Template(cv::Mat img,
00112 std::string matching_frame,
00113 int original_width_size,
00114 int original_height_size,
00115 double template_width,
00116 double template_height,
00117 tf::Transform relativepose,
00118 cv::Mat affine_matrix,
00119 double reprojection_threshold,
00120 double distanceratio_threshold,
00121 std::string window_name,
00122 bool autosize){
00123
00124 _template_img = img.clone();
00125 _matching_frame = matching_frame;
00126 _original_width_size = original_width_size;
00127 _original_height_size = original_height_size;
00128 _template_width = template_width;
00129 _template_height = template_height;
00130 _relativepose = relativepose;
00131 _affine_matrix = affine_matrix;
00132 _window_name = window_name;
00133 _reprojection_threshold = reprojection_threshold;
00134 _distanceratio_threshold = distanceratio_threshold;
00135 _template_keypoints.clear();
00136 _correspondances.clear();
00137
00138 }
00139
00140
00141 virtual ~Matching_Template(){
00142 std::cerr << "delete " << _window_name << std::endl;
00143 }
00144
00145
00146 std::string get_window_name(){
00147 return _window_name;
00148 }
00149
00150 std::vector<cv::Point2d>* correspondances(){
00151 return &_correspondances;
00152 }
00153
00154 bool check_template (){
00155 if (_template_img.empty() && _template_keypoints.size() == 0 &&
00156 _template_descriptors.empty()){
00157 return false;
00158 }
00159 else {
00160 return true;
00161 }
00162 }
00163
00164
00165 int set_template(ros::ServiceClient client){
00166 posedetection_msgs::Feature0DDetect srv;
00167
00168 if ( _template_img.empty()) {
00169 ROS_ERROR ("template picture is empty.");
00170 return -1;
00171 }
00172 ROS_INFO_STREAM("read template image and call " << client.getService() << " service");
00173 cv_bridge::CvImage cv_img;
00174 cv_img.image = cv::Mat(_template_img);
00175 cv_img.encoding = std::string("bgr8");
00176 srv.request.image = *(cv_img.toImageMsg());
00177 if (client.call(srv)) {
00178 ROS_INFO_STREAM("get features with " << srv.response.features.scales.size() << " descriptoins");
00179 features2keypoint (srv.response.features, _template_keypoints, _template_descriptors);
00180
00181
00182 cv::Mat M_inv;
00183 cv::Mat dst;
00184 cv::invert(_affine_matrix, M_inv);
00185
00186
00187
00188
00189
00190 for (int i = 0; i < (int)_template_keypoints.size(); i++){
00191 cv::Point2f pt;
00192 cv::Mat pt_mat(cv::Size(1,1), CV_32FC2, &pt);
00193 cv::Mat pt_src_mat(cv::Size(1,1), CV_32FC2, &_template_keypoints.at(i).pt);
00194 cv::perspectiveTransform (pt_src_mat, pt_mat, M_inv);
00195 _template_keypoints.at(i).pt = pt_mat.at<cv::Point2f>(0,0);
00196 }
00197
00198
00199 return 0;
00200 } else {
00201 ROS_ERROR("Failed to call service Feature0DDetect");
00202 return 1;
00203 }
00204 }
00205
00206
00207 double log_fac( int n )
00208 {
00209 static std::vector<double> slog_table;
00210 int osize = slog_table.size();
00211 if(osize <= n){
00212 slog_table.resize(n+1);
00213 if(osize == 0){
00214 slog_table[0] = -1;
00215 slog_table[1] = log(1.0);
00216 osize = 2;
00217 }
00218 for(int i = osize; i <= n; i++ ){
00219 slog_table[i] = slog_table[i-1] + log(i);
00220 }
00221 }
00222 return slog_table[n];
00223 }
00224
00225 int min_inlier( int n, int m, double p_badsupp, double p_badxform )
00226 {
00227 double pi, sum;
00228 int i, j;
00229 double lp_badsupp = log( p_badsupp );
00230 double lp_badsupp1 = log( 1.0 - p_badsupp );
00231 for( j = m+1; j <= n; j++ ){
00232 sum = 0;
00233 for( i = j; i <= n; i++ ){
00234 pi = (i-m) * lp_badsupp + (n-i+m) * lp_badsupp1 +
00235 log_fac( n - m ) - log_fac( i - m ) - log_fac( n - i );
00236 sum += exp( pi );
00237 }
00238 if( sum < p_badxform )
00239 break;
00240 }
00241 return j;
00242 }
00243
00244
00245 bool estimate_od (ros::ServiceClient client, cv::Mat src_img,
00246 std::vector<cv::KeyPoint> sourceimg_keypoints,
00247 image_geometry::PinholeCameraModel pcam,
00248 double err_thr, cv::Mat &stack_img,
00249 cv::flann::Index* ft, posedetection_msgs::Object6DPose* o6p){
00250
00251 if ( _template_keypoints.size()== 0 &&
00252 _template_descriptors.empty() ){
00253 set_template(client);
00254 }
00255 if ( _template_keypoints.size()== 0 &&
00256 _template_descriptors.empty() ) {
00257 ROS_ERROR ("Template image was not set.");
00258 return false;
00259 }
00260
00261 cv::Size stack_size = cv::Size(MAX(src_img.cols,_template_img.cols),
00262 src_img.rows+_template_img.rows);
00263 stack_img = cv::Mat(stack_size,CV_8UC3);
00264 stack_img = cv::Scalar(0);
00265 cv::Mat stack_img_tmp(stack_img,cv::Rect(0,0,_template_img.cols,_template_img.rows));
00266 cv::add(stack_img_tmp, _template_img, stack_img_tmp);
00267 cv::Mat stack_img_src(stack_img,cv::Rect(0,_template_img.rows,src_img.cols,src_img.rows));
00268 cv::add(stack_img_src, src_img, stack_img_src);
00269 _previous_stack_img = stack_img.clone();
00270
00271
00272 cv::Mat m_indices(_template_descriptors.rows, 2, CV_32S);
00273 cv::Mat m_dists(_template_descriptors.rows, 2, CV_32F);
00274 ft->knnSearch(_template_descriptors, m_indices, m_dists, 2, cv::flann::SearchParams(-1) );
00275
00276
00277 std::vector<cv::Point2f> pt1, pt2;
00278 std::vector<int> queryIdxs,trainIdxs;
00279 for ( unsigned int j = 0; j < _template_keypoints.size(); j++ ) {
00280 if ( m_dists.at<float>(j,0) < m_dists.at<float>(j,1) * _distanceratio_threshold) {
00281 queryIdxs.push_back(j);
00282 trainIdxs.push_back(m_indices.at<int>(j,0));
00283 }
00284 }
00285 if ( queryIdxs.size() == 0 ) {
00286 ROS_WARN_STREAM("could not found matched points with distanceratio(" <<_distanceratio_threshold << ")");
00287 } else {
00288 cv::KeyPoint::convert(_template_keypoints,pt1,queryIdxs);
00289 cv::KeyPoint::convert(sourceimg_keypoints,pt2,trainIdxs);
00290 }
00291
00292 ROS_INFO ("Found %d total matches among %d template keypoints", (int)pt2.size(), (int)_template_keypoints.size());
00293
00294 cv::Mat H;
00295 std::vector<uchar> mask((int)pt2.size());
00296
00297 if ( pt1.size() > 4 ) {
00298
00299 H = cv::findHomography(cv::Mat(pt1), cv::Mat(pt2), mask, CV_RANSAC, _reprojection_threshold);
00300 }
00301
00302
00303 for (int j = 0; j < (int)pt1.size(); j++){
00304 cv::Point2f pt, pt_orig;
00305 cv::Mat pt_mat(cv::Size(1,1), CV_32FC2, &pt);
00306 cv::Mat pt_src_mat(cv::Size(1,1), CV_32FC2, &pt1.at(j));
00307 cv::perspectiveTransform (pt_src_mat, pt_mat, _affine_matrix);
00308 pt_orig = pt_mat.at<cv::Point2f>(0,0);
00309 if ( mask.at(j)){
00310 cv::line(stack_img, pt_orig, pt2.at(j)+cv::Point2f(0,_template_img.rows),
00311 CV_RGB(0,255,0), 1,8,0);
00312 }
00313 else {
00314 cv::line(stack_img, pt_orig, pt2.at(j)+cv::Point2f(0,_template_img.rows),
00315 CV_RGB(255,0,255), 1,8,0);
00316 }
00317 }
00318 int inlier_sum = 0;
00319 for (int k=0; k < (int)mask.size(); k++){
00320 inlier_sum += (int)mask.at(k);
00321 }
00322
00323 double text_scale = 1.5;
00324 {
00325 int fontFace = 0, thickness = 0, baseLine;
00326 int x, y;
00327 cv::Size text_size;
00328 std::string text;
00329
00330 text = "inlier: " + boost::lexical_cast<std::string>((int)inlier_sum) + " / " + boost::lexical_cast<std::string>((int)pt2.size());
00331 text_size = cv::getTextSize(text, fontFace, text_scale, thickness, &baseLine);
00332 x = stack_img.size().width - text_size.width;
00333 y = text_size.height + thickness + 10;
00334 cv::putText (stack_img, text, cv::Point(x, y),
00335 fontFace, text_scale, CV_RGB(0, 255, 0),
00336 thickness, 8, false);
00337
00338 text = "template: " + boost::lexical_cast<std::string>((int)_template_keypoints.size());
00339 text_size = cv::getTextSize(text, fontFace, text_scale, thickness, &baseLine);
00340 x = stack_img.size().width - text_size.width;
00341 y += text_size.height + thickness + 10;
00342 cv::putText (stack_img, text, cv::Point(x, y),
00343 fontFace, text_scale, CV_RGB(0, 255, 0),
00344 thickness, 8, false);
00345 }
00346
00347
00348 ROS_INFO(" _correspondances.size: %d", (int)_correspondances.size());
00349 for (int j = 0; j < (int)_correspondances.size(); j++){
00350 cv::circle(stack_img, cv::Point2f(_correspondances.at(j).x, _correspondances.at(j).y + _template_img.size().height),
00351 8, CV_RGB(255,0,0), -1);
00352 }
00353
00354 ROS_INFO(" inlier_sum:%d min_lier:%d", inlier_sum, min_inlier((int)pt2.size(), 4, 0.10, 0.01));
00355 if ((cv::countNonZero( H ) == 0) || (inlier_sum < min_inlier((int)pt2.size(), 4, 0.10, 0.01))){
00356 ROS_INFO(" inlier_sum < min_lier return-from estimate-od");
00357 if( _window_name != "" )
00358 cv::imshow(_window_name, stack_img);
00359 return false;
00360 }
00361
00362 std::string _type;
00363 char chr[20];
00364
00365
00366 sprintf(chr, "%d", (int)pt2.size());
00367 _type = _matching_frame + "_" + std::string(chr);
00368
00369 sprintf(chr, "%d", inlier_sum);
00370 _type = _type + "_" + std::string(chr);
00371
00372 cv::Point2f corners2d[4] = {cv::Point2f(0,0),
00373 cv::Point2f(_original_width_size,0),
00374 cv::Point2f(_original_width_size,_original_height_size),
00375 cv::Point2f(0,_original_height_size)};
00376 cv::Mat corners2d_mat (cv::Size(4, 1), CV_32FC2, corners2d);
00377 cv::Point3f corners3d[4] = {cv::Point3f(0,0,0),
00378 cv::Point3f(0,_template_width,0),
00379 cv::Point3f(_template_height,_template_width,0),
00380 cv::Point3f(_template_height,0,0)};
00381 cv::Mat corners3d_mat (cv::Size(4, 1), CV_32FC3, corners3d);
00382
00383 cv::Mat corners2d_mat_trans;
00384
00385 cv::perspectiveTransform (corners2d_mat, corners2d_mat_trans, H);
00386
00387 double fR3[3], fT3[3];
00388 cv::Mat rvec(3, 1, CV_64FC1, fR3);
00389 cv::Mat tvec(3, 1, CV_64FC1, fT3);
00390 cv::Mat zero_distortion_mat = cv::Mat::zeros(4, 1, CV_64FC1);
00391
00392 cv::solvePnP (corners3d_mat, corners2d_mat_trans,
00393 pcam.intrinsicMatrix(),
00394 zero_distortion_mat,
00395 rvec, tvec);
00396
00397 tf::Transform checktf, resulttf;
00398
00399 checktf.setOrigin( tf::Vector3(fT3[0], fT3[1], fT3[2] ) );
00400
00401 double rx = fR3[0], ry = fR3[1], rz = fR3[2];
00402 tf::Quaternion quat;
00403 double angle = cv::norm(rvec);
00404 quat.setRotation(tf::Vector3(rx/angle, ry/angle, rz/angle), angle);
00405 checktf.setRotation( quat );
00406
00407 resulttf = checktf * _relativepose;
00408
00409 ROS_INFO( " tx: (%0.2lf,%0.2lf,%0.2lf) rx: (%0.2lf,%0.2lf,%0.2lf)",
00410 resulttf.getOrigin().getX(),
00411 resulttf.getOrigin().getY(),
00412 resulttf.getOrigin().getZ(),
00413 resulttf.getRotation().getAxis().x() * resulttf.getRotation().getAngle(),
00414 resulttf.getRotation().getAxis().y() * resulttf.getRotation().getAngle(),
00415 resulttf.getRotation().getAxis().z() * resulttf.getRotation().getAngle());
00416
00417 o6p->pose.position.x = resulttf.getOrigin().getX();
00418 o6p->pose.position.y = resulttf.getOrigin().getY();
00419 o6p->pose.position.z = resulttf.getOrigin().getZ();
00420 o6p->pose.orientation.w = resulttf.getRotation().w();
00421 o6p->pose.orientation.x = resulttf.getRotation().x();
00422 o6p->pose.orientation.y = resulttf.getRotation().y();
00423 o6p->pose.orientation.z = resulttf.getRotation().z();
00424 o6p->type = _matching_frame;
00425
00426
00427 std::vector<cv::Point2f> projected_top;
00428 {
00429 tf::Vector3 coords[8] = {tf::Vector3(0,0,0),
00430 tf::Vector3(0, _template_width, 0),
00431 tf::Vector3(_template_height, _template_width,0),
00432 tf::Vector3(_template_height, 0, 0),
00433 tf::Vector3(0, 0, -0.03),
00434 tf::Vector3(0, _template_width, -0.03),
00435 tf::Vector3(_template_height, _template_width, -0.03),
00436 tf::Vector3(_template_height, 0, -0.03)};
00437
00438 projected_top = std::vector<cv::Point2f>(8);
00439
00440 for(int i=0; i<8; i++) {
00441 coords[i] = checktf * coords[i];
00442 cv::Point3f pt(coords[i].getX(), coords[i].getY(), coords[i].getZ());
00443 projected_top[i] = pcam.project3dToPixel(pt);
00444 }
00445 }
00446
00447 {
00448 float max_x, max_y, min_x, min_y;
00449 max_x = max_y = -1e9;
00450 min_x = min_y = 1e9;
00451 for (int j = 0; j < 4; j++){
00452 cv::Point2f pt = corners2d_mat_trans.at<cv::Point2f>(0,j);
00453 max_x = std::max(max_x, pt.x), max_y = std::max(max_y, pt.y);
00454 min_x = std::min(min_x, pt.x), min_y = std::min(min_y, pt.y);
00455 }
00456 if((max_x - min_x) < 30 || (max_y - min_y) < 30 ||
00457 src_img.rows < (max_x - min_x)/2 || src_img.cols < (max_y - min_y)/2){
00458 ROS_INFO(" matched region is too big or small (2< && <30) width:%f height:%f return-from estimate-od", max_x - min_x, max_y - min_y);
00459 return false;
00460 }
00461 }
00462
00463 double err_sum = 0;
00464 bool err_success = true;
00465 for (int j = 0; j < 4; j++){
00466 double err = sqrt(pow((corners2d_mat_trans.at<cv::Point2f>(0,j).x - projected_top.at(j).x), 2) +
00467 pow((corners2d_mat_trans.at<cv::Point2f>(0,j).y - projected_top.at(j).y), 2));
00468 err_sum += err;
00469 }
00470 if (err_sum > err_thr){
00471 ROS_INFO(" err_sum:%f > err_thr:%f return-from estimate-od", err_sum, err_thr);
00472 err_success = false;
00473 }
00474
00475 for (int j = 0; j < corners2d_mat_trans.cols; j++){
00476 cv::Point2f p1(corners2d_mat_trans.at<cv::Point2f>(0,j).x,
00477 corners2d_mat_trans.at<cv::Point2f>(0,j).y+_template_img.rows);
00478 cv::Point2f p2(corners2d_mat_trans.at<cv::Point2f>(0,(j+1)%corners2d_mat_trans.cols).x,
00479 corners2d_mat_trans.at<cv::Point2f>(0,(j+1)%corners2d_mat_trans.cols).y+_template_img.rows);
00480 cv::line (stack_img, p1, p2, CV_RGB(255, 0, 0),
00481 (err_success?4:1),
00482 CV_AA, 0);
00483 }
00484
00485
00486 if(projected_top.size() == 8) {
00487 int cnt = 8;
00488 std::vector<cv::Point2f> ps(cnt);
00489 for(int i=0; i<cnt; i++)
00490 ps[i] = cv::Point2f(projected_top[i].x,
00491 projected_top[i].y+_template_img.rows);
00492
00493 int draw_width = ( err_success ? 3 : 1);
00494 for(int i=0; i<4; i++) {
00495 cv::line (stack_img, ps[i], ps[(i+1)%4], CV_RGB(0, 0, 255), draw_width, CV_AA, 0);
00496 cv::line (stack_img, ps[i+4], ps[(i+1)%4+4], CV_RGB(0, 0, 255), draw_width, CV_AA, 0);
00497 cv::line (stack_img, ps[i], ps[i+4], CV_RGB(0, 0, 255), draw_width, CV_AA, 0);
00498 }
00499 }
00500
00501
00502 if ( err_success )
00503 {
00504 tf::Vector3 coords[4] = { tf::Vector3(0,0,0),
00505 tf::Vector3(0.05,0,0),
00506 tf::Vector3(0,0.05,0),
00507 tf::Vector3(0,0,0.05)};
00508 std::vector<cv::Point2f> ps(4);
00509
00510 for(int i=0; i<4; i++) {
00511 coords[i] = resulttf * coords[i];
00512 cv::Point3f pt(coords[i].getX(), coords[i].getY(), coords[i].getZ());
00513 ps[i] = pcam.project3dToPixel(pt);
00514 ps[i].y += _template_img.rows;
00515 }
00516
00517 cv::line (stack_img, ps[0], ps[1], CV_RGB(255, 0, 0), 3, CV_AA, 0);
00518 cv::line (stack_img, ps[0], ps[2], CV_RGB(0, 255, 0), 3, CV_AA, 0);
00519 cv::line (stack_img, ps[0], ps[3], CV_RGB(0, 0, 255), 3, CV_AA, 0);
00520 }
00521
00522
00523 {
00524 std::string text;
00525 int x, y;
00526 text = "error: " + boost::lexical_cast<std::string>(err_sum);
00527 x = stack_img.size().width - 16*17*text_scale;
00528 y = _template_img.size().height - (16 + 2)*text_scale*6;
00529 cv::putText (stack_img, text, cv::Point(x, y),
00530 0, text_scale, CV_RGB(0, 255, 0),
00531 2, 8, false);
00532 ROS_INFO(" %s < %f (threshold)", text.c_str(), err_thr );
00533 }
00534
00535 if( _window_name != "" )
00536 cv::imshow(_window_name, stack_img);
00537
00538 return err_success;
00539 }
00540 };
00541
00542
00543 class PointPoseExtractor
00544 {
00545 ros::NodeHandle _n;
00546 image_transport::ImageTransport it;
00547 ros::Subscriber _sub;
00548 ros::ServiceServer _server;
00549 ros::ServiceClient _client;
00550 ros::Publisher _pub, _pub_agg, _pub_pose;
00551 image_transport::Publisher _debug_pub;
00552 double _reprojection_threshold;
00553 double _distanceratio_threshold;
00554 double _th_step;
00555 double _phi_step;
00556 bool _autosize;
00557 double _err_thr;
00558 static std::vector<Matching_Template *> _templates;
00559 image_geometry::PinholeCameraModel pcam;
00560 bool pnod;
00561 bool _initialized;
00562 bool _viewer;
00563
00564 public:
00565 PointPoseExtractor() : it(ros::NodeHandle("~")) {
00566
00567
00568 _client = _n.serviceClient<posedetection_msgs::Feature0DDetect>("Feature0DDetect");
00569 _pub = _n.advertise<posedetection_msgs::ObjectDetection>("ObjectDetection", 10);
00570 _pub_agg = _n.advertise<posedetection_msgs::ObjectDetection>("ObjectDetection_agg", 10);
00571 _pub_pose = _n.advertise<geometry_msgs::PoseStamped>("object_pose", 10);
00572 _debug_pub = it.advertise("debug_image", 1);
00573 _server = _n.advertiseService("SetTemplate", &PointPoseExtractor::settemplate_cb, this);
00574 _initialized = false;
00575 }
00576
00577 virtual ~PointPoseExtractor(){
00578 _sub.shutdown();
00579 _client.shutdown();
00580 _pub.shutdown();
00581 _pub_agg.shutdown();
00582 }
00583
00584 static void make_template_from_mousecb(Matching_Template *mt){
00585 cv::Mat H;
00586 cv::Mat tmp_template, tmp_warp_template;
00587 std::vector<cv::Point2f>pt1, pt2;
00588 double width, height;
00589 std::string filename;
00590 std::cout << "input template's [width]" << std::endl;
00591 std::cin >> width;
00592 std::cout << "input template's [height]" << std::endl;
00593 std::cin >> height;
00594 std::cout << "input template's [filename]" << std::endl;
00595 std::cin >> filename;
00596
00597 for (int i = 0; i < 4; i++){
00598 pt1.push_back(cv::Point2d((int)mt->_correspondances.at(i).x,
00599 (int)mt->_correspondances.at(i).y + mt->_template_img.size().height));
00600 }
00601 cv::Rect rect = cv::boundingRect(cv::Mat(pt1));
00602 double scale = std::max(width, height) / 500.0;
00603 int iwidth = width / scale, iheight = height / scale;
00604 pt2.push_back(cv::Point2d(0,0));
00605 pt2.push_back(cv::Point2d(iwidth,0));
00606 pt2.push_back(cv::Point2d(iwidth,iheight));
00607 pt2.push_back(cv::Point2d(0, iheight));
00608 H = cv::findHomography(cv::Mat(pt1), cv::Mat(pt2));
00609
00610 cv::getRectSubPix(mt->_previous_stack_img, rect.size(),
00611 cv::Point2f((rect.tl().x + rect.br().x)/2.0,(rect.tl().y + rect.br().y)/2.0),
00612 tmp_template);
00613 cv::warpPerspective(mt->_previous_stack_img, tmp_warp_template, H, cv::Size(iwidth, iheight));
00614
00615 try {
00616 cv::imwrite(filename,tmp_template);
00617 boost::filesystem::path fname(filename);
00618 std::stringstream ss;
00619 ss << fname.stem() << "_wrap" << fname.extension();
00620 cv::imwrite(ss.str(),tmp_warp_template);
00621 }catch (cv::Exception e) {
00622 std::cerr << e.what() << std::endl;
00623 }
00624
00625 for (int i = 0; i < (int)pt1.size(); i++){
00626 pt2.push_back(cv::Point2d((int)pt1.at(i).x - rect.x,
00627 (int)pt1.at(i).y - rect.y - mt->_template_img.size().height));
00628 }
00629
00630
00631
00632
00633
00634
00635 cv::Mat M = (cv::Mat_<double>(3,3) << 1,0,0, 0,1,0, 0,0,1);
00636 std::string window_name = "sample" + boost::lexical_cast<std::string>((int)_templates.size());
00637
00638 Matching_Template* tmplt =
00639 new Matching_Template (tmp_warp_template, "sample",
00640 tmp_warp_template.size().width, tmp_warp_template.size().height,
00641 width, height,
00642 tf::Transform(tf::Quaternion(0, 0, 0, 1), tf::Vector3(0, 0, 0)),
00643 M,
00644 mt->_reprojection_threshold,
00645 mt->_distanceratio_threshold,
00646 _first_sample_change ? window_name : mt->_window_name,
00647 cv::getWindowProperty(mt->_window_name, CV_WND_PROP_AUTOSIZE));
00648
00649 mt->_correspondances.clear();
00650 _templates.push_back(tmplt);
00651 cv::namedWindow(_first_sample_change ? window_name : mt->_window_name,
00652 cv::getWindowProperty(mt->_window_name, CV_WND_PROP_AUTOSIZE));
00653 cvSetMouseCallback (_first_sample_change ? window_name.c_str() : mt->_window_name.c_str(),
00654 &cvmousecb, static_cast<void *>(_templates.back()));
00655 _first_sample_change = true;
00656 }
00657
00658 static void cvmousecb (int event, int x, int y, int flags, void* param){
00659 Matching_Template *mt = (Matching_Template*)param;
00660
00661 switch (event){
00662 case CV_EVENT_LBUTTONUP: {
00663 cv::Point2d pt(x,y - (int)mt->_template_img.size().height);
00664 ROS_INFO("add correspondence (%d, %d)", (int)pt.x, (int)pt.y);
00665 mt->_correspondances.push_back(pt);
00666 if ((int)mt->_correspondances.size() >= 4){
00667 make_template_from_mousecb(mt);
00668 mt->_correspondances.clear();
00669 ROS_INFO("reset");
00670 }
00671 break;
00672 }
00673 case CV_EVENT_RBUTTONUP: {
00674 mt->_correspondances.clear();
00675 ROS_INFO("reset");
00676 break;
00677 }
00678 }
00679 }
00680
00681
00682 void initialize () {
00683 std::string matching_frame;
00684 std::string _pose_str;
00685 double template_width;
00686 double template_height;
00687 std::string template_filename;
00688 std::string window_name;
00689 ros::NodeHandle local_nh("~");
00690
00691 local_nh.param("child_frame_id", matching_frame, std::string("matching"));
00692 local_nh.param("object_width", template_width, 0.06);
00693 local_nh.param("object_height", template_height, 0.0739);
00694 local_nh.param("relative_pose", _pose_str, std::string("0 0 0 0 0 0 1"));
00695 std::string default_template_file_name;
00696 try {
00697 #ifdef ROSPACK_EXPORT
00698 rospack::ROSPack rp;
00699 rospack::Package *p = rp.get_pkg("jsk_perception");
00700 if (p!=NULL) default_template_file_name = p->path + std::string("/sample/opencv-logo2.png");
00701 #else
00702 rospack::Rospack rp;
00703 std::vector<std::string> search_path;
00704 rp.getSearchPathFromEnv(search_path);
00705 rp.crawl(search_path, 1);
00706 std::string path;
00707 if (rp.find("jsk_perception",path)==true) default_template_file_name = path + std::string("/sample/opencv-logo2.png");
00708 #endif
00709 } catch (std::runtime_error &e) {
00710 }
00711 local_nh.param("template_filename", template_filename, default_template_file_name);
00712 local_nh.param("reprojection_threshold", _reprojection_threshold, 3.0);
00713 local_nh.param("distanceratio_threshold", _distanceratio_threshold, 0.49);
00714 local_nh.param("error_threshold", _err_thr, 50.0);
00715 local_nh.param("theta_step", _th_step, 5.0);
00716 local_nh.param("phi_step", _phi_step, 5.0);
00717 local_nh.param("viewer_window", _viewer, true);
00718 local_nh.param("window_name", window_name, std::string("sample1"));
00719 local_nh.param("autosize", _autosize, false);
00720 local_nh.param("publish_null_object_detection", pnod, false);
00721
00722 _first_sample_change = false;
00723
00724
00725 cv::Mat template_img;
00726 template_img = cv::imread (template_filename, 1);
00727 if ( template_img.empty()) {
00728 ROS_ERROR ("template picture <%s> cannot read. template picture is not found or uses unsuported format.", template_filename.c_str());
00729 return;
00730 }
00731
00732
00733 std::vector<double> rv(7);
00734 std::istringstream iss(_pose_str);
00735 tf::Transform transform;
00736 for(int i=0; i<6; i++)
00737 iss >> rv[i];
00738
00739 if (iss.eof()) {
00740 transform = tf::Transform(tf::createQuaternionFromRPY(rv[3], rv[4], rv[5]),
00741 tf::Vector3(rv[0], rv[1], rv[2]));
00742 } else {
00743 iss >> rv[6];
00744 transform = tf::Transform(tf::Quaternion(rv[3], rv[4], rv[5], rv[6]),
00745 tf::Vector3(rv[0], rv[1], rv[2]));
00746 }
00747
00748
00749 add_new_template(template_img, window_name, transform,
00750 template_width, template_height, _th_step, _phi_step);
00751
00752 }
00753
00754
00755 cv::Mat make_homography(cv::Mat src, cv::Mat rvec, cv::Mat tvec,
00756 double template_width, double template_height, cv::Size &size){
00757
00758 cv::Point3f coner[4] = {cv::Point3f(-(template_width/2.0), -(template_height/2.0),0),
00759 cv::Point3f(template_width/2.0,-(template_height/2.0),0),
00760 cv::Point3f(template_width/2.0,template_height/2.0,0),
00761 cv::Point3f(-(template_width/2.0),template_height/2.0,0)};
00762 cv::Mat coner_mat (cv::Size(4, 1), CV_32FC3, coner);
00763 std::vector<cv::Point2f> coner_img_points;
00764
00765 cv::Mat zero_distortion_mat = cv::Mat::zeros(4, 1, CV_64FC1);
00766 cv::projectPoints(coner_mat, rvec, tvec,
00767 pcam.intrinsicMatrix(),
00768 zero_distortion_mat,
00769 coner_img_points);
00770 float x_min = 10000, x_max = 0;
00771 float y_min = 10000, y_max = 0;
00772 for (int i = 0; i < (int)coner_img_points.size(); i++){
00773 x_min = std::min(x_min, coner_img_points.at(i).x);
00774 x_max = std::max(x_max, coner_img_points.at(i).x);
00775 y_min = std::min(y_min, coner_img_points.at(i).y);
00776 y_max = std::max(y_max, coner_img_points.at(i).y);
00777 }
00778
00779 std::vector<cv::Point2f> coner_img_points_trans;
00780 for (int i = 0; i < (int)coner_img_points.size(); i++){
00781 cv::Point2f pt_tmp(coner_img_points.at(i).x - x_min,
00782 coner_img_points.at(i).y - y_min);
00783 coner_img_points_trans.push_back(pt_tmp);
00784 }
00785
00786 cv::Point2f template_points[4] = {cv::Point2f(0,0),
00787 cv::Point2f(src.size().width,0),
00788 cv::Point2f(src.size().width,src.size().height),
00789 cv::Point2f(0,src.size().height)};
00790 cv::Mat template_points_mat (cv::Size(4, 1), CV_32FC2, template_points);
00791
00792 size = cv::Size(x_max - x_min, y_max - y_min);
00793 return cv::findHomography(template_points_mat, cv::Mat(coner_img_points_trans), 0, 3);
00794 }
00795
00796
00797 int make_warped_images (cv::Mat src, std::vector<cv::Mat> &imgs,
00798 std::vector<cv:: Mat> &Mvec,
00799 double template_width, double template_height,
00800 double th_step, double phi_step){
00801
00802 std::vector<cv::Size> sizevec;
00803
00804 for (int i = (int)((-3.14/4.0)/th_step); i*th_step < 3.14/4.0; i++){
00805 for (int j = (int)((-3.14/4.0)/phi_step); j*phi_step < 3.14/4.0; j++){
00806 double fR3[3], fT3[3];
00807 cv::Mat rvec(3, 1, CV_64FC1, fR3);
00808 cv::Mat tvec(3, 1, CV_64FC1, fT3);
00809
00810 tf::Quaternion quat;
00811 quat.setEuler(0, th_step*i, phi_step*j);
00812 fR3[0] = quat.getAxis().x() * quat.getAngle();
00813 fR3[1] = quat.getAxis().y() * quat.getAngle();
00814 fR3[2] = quat.getAxis().z() * quat.getAngle();
00815 fT3[0] = 0;
00816 fT3[1] = 0;
00817 fT3[2] = 0.5;
00818
00819 cv::Mat M;
00820 cv::Size size;
00821 M = make_homography(src, rvec, tvec, template_width, template_height, size);
00822 Mvec.push_back(M);
00823 sizevec.push_back(size);
00824 }
00825 }
00826
00827 for (int i = 0; i < (int)Mvec.size(); i++){
00828 cv::Mat dst;
00829 cv::warpPerspective(src, dst, Mvec.at(i), sizevec.at(i),
00830 CV_INTER_LINEAR, IPL_BORDER_CONSTANT, 0);
00831 imgs.push_back(dst);
00832 }
00833 return 0;
00834 }
00835
00836 bool add_new_template(cv::Mat img, std::string typestr, tf::Transform relative_pose,
00837 double template_width, double template_height,
00838 double theta_step=5.0, double phi_step=5.0)
00839 {
00840 std::vector<cv::Mat> imgs;
00841 std::vector<cv::Mat> Mvec;
00842 make_warped_images(img, imgs, Mvec,
00843 template_width, template_height, theta_step, phi_step);
00844
00845 for (int i = 0; i < (int)imgs.size(); i++){
00846 std::string type = typestr;
00847 if(imgs.size() > 1) {
00848 char chr[20];
00849 sprintf(chr, "%d", i);
00850 type += "_" + std::string(chr);
00851 }
00852
00853 Matching_Template * tmplt =
00854 new Matching_Template(imgs.at(i), type,
00855 img.size().width, img.size().height,
00856 template_width, template_height,
00857 relative_pose, Mvec.at(i),
00858 _reprojection_threshold,
00859 _distanceratio_threshold,
00860 (_viewer ? type : ""), _autosize);
00861 _templates.push_back(tmplt);
00862 if( _viewer )
00863 cv::namedWindow(type, _autosize ? CV_WINDOW_AUTOSIZE : 0);
00864 cvSetMouseCallback (type.c_str(), &cvmousecb, static_cast<void *>(_templates.back()));
00865 }
00866 return true;
00867 }
00868
00869 bool settemplate_cb (jsk_perception::SetTemplate::Request &req,
00870 jsk_perception::SetTemplate::Response &res){
00871 cv_bridge::CvImagePtr cv_ptr;
00872 cv_ptr = cv_bridge::toCvCopy(req.image, enc::BGR8);
00873 cv::Mat img(cv_ptr->image);
00874
00875 tf::Transform transform(tf::Quaternion(req.relativepose.orientation.x,
00876 req.relativepose.orientation.y,
00877 req.relativepose.orientation.z,
00878 req.relativepose.orientation.w),
00879 tf::Vector3(req.relativepose.position.x,
00880 req.relativepose.position.y,
00881 req.relativepose.position.z));
00882
00883
00884 add_new_template(img, req.type, transform,
00885 req.dimx, req.dimy, 1.0, 1.0);
00886 return true;
00887 }
00888
00889
00890 void imagefeature_cb (const posedetection_msgs::ImageFeature0DConstPtr& msg){
00891 std::vector<cv::KeyPoint> sourceimg_keypoints;
00892 cv::Mat sourceimg_descriptors;
00893 std::vector<posedetection_msgs::Object6DPose> vo6p;
00894 posedetection_msgs::ObjectDetection od;
00895
00896 cv_bridge::CvImagePtr cv_ptr;
00897 cv_ptr = cv_bridge::toCvCopy(msg->image, enc::BGR8);
00898 cv::Mat src_img(cv_ptr->image);
00899
00900
00901 features2keypoint (msg->features, sourceimg_keypoints, sourceimg_descriptors);
00902 pcam.fromCameraInfo(msg->info);
00903 if ( cv::countNonZero(pcam.intrinsicMatrix()) == 0 ) {
00904 ROS_FATAL("intrinsic matrix is zero, your camera info looks invalid");
00905 }
00906 if ( !_initialized ) {
00907
00908 initialize();
00909 std::cerr << "initialize templates done" << std::endl;
00910 _initialized = true;
00911 }
00912
00913 if ( sourceimg_keypoints.size () < 2 ) {
00914 ROS_INFO ("The number of keypoints in source image is less than 2");
00915 }
00916 else {
00917
00918 cv::flann::Index *ft = new cv::flann::Index(sourceimg_descriptors, cv::flann::KDTreeIndexParams(1));
00919
00920
00921 ROS_INFO("_templates size: %d", (int)_templates.size());
00922 for (int i = 0; i < (int)_templates.size(); i++){
00923 posedetection_msgs::Object6DPose o6p;
00924
00925 cv::Mat debug_img;
00926 if (_templates.at(i)->estimate_od(_client, src_img, sourceimg_keypoints, pcam, _err_thr, debug_img, ft, &o6p))
00927 vo6p.push_back(o6p);
00928
00929
00930 cv_bridge::CvImage out_msg;
00931 out_msg.header = msg->image.header;
00932 out_msg.encoding = "bgr8";
00933 out_msg.image = debug_img;
00934 _debug_pub.publish(out_msg.toImageMsg());
00935 }
00936 delete ft;
00937 if (((int)vo6p.size() != 0) || pnod) {
00938 od.header.stamp = msg->image.header.stamp;
00939 od.header.frame_id = msg->image.header.frame_id;
00940 od.objects = vo6p;
00941 _pub.publish(od);
00942 _pub_agg.publish(od);
00943
00944 geometry_msgs::PoseStamped pose_msg;
00945 pose_msg.header = od.header;
00946 pose_msg.pose = od.objects[0].pose;
00947 _pub_pose.publish(pose_msg);
00948 }
00949 }
00950
00951
00952
00953 cv::waitKey( 10 );
00954 }
00955
00956
00957
00958
00959 void check_subscribers()
00960 {
00961 if(_pub.getNumSubscribers() == 0 && _initialized) {
00962 if(_sub)
00963 _sub.shutdown();
00964 static int i = 0;
00965 if ( i++ % 100 == 0 ) {
00966 ROS_INFO("wait for subscriberes ... %s", _pub.getTopic().c_str());
00967 }
00968 } else {
00969 if(!_sub)
00970 _sub = _n.subscribe("ImageFeature0D", 1,
00971 &PointPoseExtractor::imagefeature_cb, this);
00972 }
00973 }
00974
00975
00976 void dyn_conf_callback(jsk_perception::point_pose_extractorConfig &config,
00977 uint32_t level) {
00978 std::cout << "id = " << config.template_id << std::endl;
00979 std::cout << "lvl = " << level << std::endl;
00980 if((int)_templates.size() <= config.template_id) {
00981 ROS_WARN("template_id is invalid");
00982 config.template_id = 0;
00983 if(_templates.size() != 0)
00984 config.frame_id = _templates[0]->_matching_frame;
00985 } else {
00986 Matching_Template* tmpl = _templates[config.template_id];
00987 if(config.frame_id == tmpl->_matching_frame) {
00988 ROS_WARN("update params");
00989 tmpl->_reprojection_threshold = config.reprojection_threshold;
00990 tmpl->_distanceratio_threshold = config.distanceratio_threshold;
00991 _err_thr = config.error_threshold;
00992 } else {
00993 ROS_WARN("get params");
00994 config.frame_id = tmpl->_matching_frame;
00995 config.reprojection_threshold = tmpl->_reprojection_threshold;
00996 config.distanceratio_threshold = tmpl->_distanceratio_threshold;
00997 config.error_threshold = _err_thr;
00998 }
00999 }
01000 }
01001
01002 };
01003
01004
01005 std::vector<Matching_Template *> PointPoseExtractor::_templates;
01006
01007 int main (int argc, char **argv){
01008 ros::init (argc, argv, "PointPoseExtractor");
01009
01010 PointPoseExtractor matcher;
01011
01012 dynamic_reconfigure::Server<jsk_perception::point_pose_extractorConfig> server;
01013 dynamic_reconfigure::Server<jsk_perception::point_pose_extractorConfig>::CallbackType f;
01014 f = boost::bind(&PointPoseExtractor::dyn_conf_callback, &matcher, _1, _2);
01015 server.setCallback(f);
01016
01017 ros::Rate r(10);
01018 while(ros::ok()) {
01019 ros::spinOnce();
01020 matcher.check_subscribers();
01021 r.sleep();
01022 }
01023
01024 return 0;
01025 }