pixel_2_3d.cpp
Go to the documentation of this file.
00001 //#include <numeric>
00002 #include <ros/ros.h>
00003 #include <algorithm>
00004 
00005 #include <geometry_msgs/PointStamped.h>
00006 #include <geometry_msgs/PoseStamped.h>
00007 #include <pcl/point_types.h>
00008 #include <pcl_ros/point_cloud.h>
00009 #include <pcl/point_cloud.h>
00010 #include <pcl/kdtree/kdtree_flann.h>
00011 #include <pcl/filters/passthrough.h>
00012 #include <pcl/surface/mls.h>
00013 #include <pcl/filters/conditional_removal.h>
00014 #include <tf/transform_listener.h>
00015 #include <opencv/cv.h>
00016 #include <image_transport/image_transport.h>
00017 #include <image_geometry/pinhole_camera_model.h>
00018 #include <pcl_ros/transforms.h>
00019 
00020 #include <pixel_2_3d/Pixel23d.h>
00021 
00022 #define DIST3(x1,y1,z1,x2,y2,z2) (std::sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)))
00023 #define SQ(x) ((x) * (x))
00024 typedef pcl::PointXYZRGB PRGB;
00025 
00026 namespace pixel_2_3d {
00027 
00028     class Pixel23dServer {
00029         public:
00030             ros::NodeHandle nh;
00031             tf::TransformListener tf_listener;
00032             ros::Subscriber pc_sub, l_click_sub;
00033             ros::Publisher pt3d_pub;
00034             ros::ServiceServer pix_srv;
00035             image_transport::ImageTransport img_trans;
00036             image_transport::CameraSubscriber camera_sub;
00037             pcl::PointCloud<pcl::PointXYZRGB>::Ptr cur_pc;
00038             double normal_search_radius;
00039             std::string output_frame;
00040             uint32_t img_width, img_height;
00041             bool cam_called, pc_called, use_closest_pixel;
00042 
00043             Pixel23dServer();
00044             void onInit();
00045             void cameraCallback(const sensor_msgs::ImageConstPtr& img_msg,
00046                                 const sensor_msgs::CameraInfoConstPtr& info_msg);
00047             void pcCallback(sensor_msgs::PointCloud2::ConstPtr pc_msg);
00048             bool pixCallback(Pixel23d::Request& req, Pixel23d::Response& resp);
00049             void lClickCallback(const geometry_msgs::PointStamped& click_msg);
00050 
00051     };
00052 
00053     Pixel23dServer::Pixel23dServer() : nh("~"), img_trans(nh),
00054                                        cur_pc(new pcl::PointCloud<pcl::PointXYZRGB>),
00055                                        cam_called(false), pc_called(false) {
00056         onInit();
00057     }
00058 
00059     void Pixel23dServer::onInit() {
00060         nh.param<double>("normal_radius", normal_search_radius, 0.03);
00061         nh.param<bool>("use_closest_pixel", use_closest_pixel, false);
00062         nh.param<std::string>("output_frame", output_frame, "");
00063         camera_sub = img_trans.subscribeCamera<Pixel23dServer>
00064                                               ("/image", 1, 
00065                                                &Pixel23dServer::cameraCallback, this);
00066         pc_sub = nh.subscribe("/point_cloud", 1, &Pixel23dServer::pcCallback, this);
00067         pix_srv = nh.advertiseService("/pixel_2_3d", &Pixel23dServer::pixCallback, this);
00068         pt3d_pub = nh.advertise<geometry_msgs::PoseStamped>("/pixel3d", 1);
00069         l_click_sub = nh.subscribe("/l_mouse_click", 1, &Pixel23dServer::lClickCallback, this);
00070         ROS_INFO("[pixel_2_3d] Pixel23dServer loaded");
00071     }
00072 
00073     void Pixel23dServer::cameraCallback(const sensor_msgs::ImageConstPtr& img_msg,
00074                                          const sensor_msgs::CameraInfoConstPtr& info_msg) {
00075         if(!info_msg)
00076             return;
00077         img_width = info_msg->width;
00078         img_height = info_msg->height;
00079         cam_called = true;
00080         camera_sub.shutdown();
00081     }
00082 
00083     void Pixel23dServer::pcCallback(sensor_msgs::PointCloud2::ConstPtr pc_msg) {
00084         pcl::fromROSMsg(*pc_msg, *cur_pc);
00085         pc_called = true;
00086     }
00087 
00088     bool Pixel23dServer::pixCallback(Pixel23d::Request& req, Pixel23d::Response& resp) {
00089         resp.pixel3d.pose.position.x = -10000.0;
00090         resp.pixel3d.pose.position.y = -10000.0;
00091         resp.pixel3d.pose.position.z = -10000.0;
00092 
00093         if(!cam_called) {
00094             ROS_WARN("No camera_info message received.");
00095             resp.error_flag = resp.NO_CAMERA_INFO;
00096             return true;
00097         }
00098         if(!pc_called) {
00099             ROS_WARN("No point cloud message received.");
00100             resp.error_flag = resp.NO_POINT_CLOUD;
00101             return true;
00102         }
00103 
00104         int64_t pc_ind = req.pixel_u + req.pixel_v * img_width;
00105         if(req.pixel_u < 0 || req.pixel_v < 0 || 
00106            req.pixel_u >= (int32_t) img_width || req.pixel_v >= (int32_t) img_height) {
00107             ROS_WARN("Pixel requested is outside image size.");
00108             resp.error_flag = resp.OUTSIDE_IMAGE;
00109             return true;
00110         }
00111         geometry_msgs::PointStamped pt3d, pt3d_trans;
00112         pt3d_trans.header.frame_id = cur_pc->header.frame_id;
00113         pt3d_trans.header.stamp = ros::Time::now();
00114         if(cur_pc->points[pc_ind].x != cur_pc->points[pc_ind].x) {
00115             if(use_closest_pixel) {
00116                 // find the closest pixel that has a point in the PC
00117                 std::vector<double> dists(img_width * img_height, 1e30);
00118                 int64_t cur_pc_ind;
00119                 for(int64_t i=0;i<img_height;i++) {
00120                     for(int64_t j=0;j<img_width;j++) {
00121                         cur_pc_ind = j + i * img_width;
00122                         if(cur_pc->points[cur_pc_ind].x == cur_pc->points[cur_pc_ind].x)
00123                             dists[cur_pc_ind] = SQ(req.pixel_u - j) + SQ(req.pixel_v - i);
00124                     }
00125                 }
00126                 pc_ind = std::min_element(dists.begin(), dists.end()) - dists.begin();
00127             } else {
00128                 ROS_WARN("Point cloud not defined for this region.");
00129                 resp.error_flag = resp.OUTSIDE_POINT_CLOUD;
00130                 return true;
00131             }
00132         }
00133         pt3d_trans.point.x = cur_pc->points[pc_ind].x;
00134         pt3d_trans.point.y = cur_pc->points[pc_ind].y;
00135         pt3d_trans.point.z = cur_pc->points[pc_ind].z;
00136 
00137         // Filter to only points in small voxel range
00138         pcl::ConditionAnd<PRGB>::Ptr near_cond(new pcl::ConditionAnd<PRGB>());
00139         pcl::PointCloud<PRGB>::Ptr near_pts(new pcl::PointCloud<PRGB>());
00140         pcl::ConditionalRemoval<PRGB> near_extract;
00141         double voxel_size = normal_search_radius*2.1;
00142         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00143                                  "x", pcl::ComparisonOps::GT, pt3d_trans.point.x - voxel_size/2)));
00144         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00145                                  "x", pcl::ComparisonOps::LT, pt3d_trans.point.x + voxel_size/2)));
00146         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00147                                  "y", pcl::ComparisonOps::GT, pt3d_trans.point.y - voxel_size/2)));
00148         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00149                                  "y", pcl::ComparisonOps::LT, pt3d_trans.point.y + voxel_size/2)));
00150         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00151                                  "z", pcl::ComparisonOps::GT, pt3d_trans.point.z - voxel_size/2)));
00152         near_cond->addComparison(pcl::FieldComparison<PRGB>::Ptr(new pcl::FieldComparison<PRGB>(
00153                                  "z", pcl::ComparisonOps::LT, pt3d_trans.point.z + voxel_size/2)));
00154         near_extract.setCondition(near_cond);
00155         near_extract.setKeepOrganized(false);
00156         near_extract.setInputCloud(cur_pc);
00157         near_extract.filter(*near_pts);
00158         std::vector<int> inds;
00159         pcl::removeNaNFromPointCloud<PRGB>(*near_pts, *near_pts, inds);
00160 
00161         uint32_t closest_ind;
00162         double closest_dist = 1000, cur_dist;
00163         for(uint32_t i=0;i<inds.size();i++) {
00164             cur_dist = DIST3(pt3d_trans.point.x, pt3d_trans.point.y, pt3d_trans.point.z,
00165                              near_pts->points.at(i).x, near_pts->points.at(i).y, 
00166                              near_pts->points.at(i).z);
00167             if(cur_dist < closest_dist) {
00168                 closest_dist = cur_dist;
00169                 closest_ind = i;
00170             }
00171         }
00172 
00173         // Compute normals
00174         pcl::PointCloud<pcl::Normal>::Ptr normals_ptr(new pcl::PointCloud<pcl::Normal>());
00175         pcl::search::KdTree<PRGB>::Ptr normals_tree (new pcl::search::KdTree<PRGB> ());
00176         pcl::PointCloud<PRGB> mls_points;
00177         pcl::MovingLeastSquares<PRGB, pcl::Normal> mls;
00178         normals_tree->setInputCloud(near_pts);
00179         mls.setOutputNormals(normals_ptr);
00180         mls.setInputCloud(near_pts);
00181         mls.setPolynomialFit(true);
00182         mls.setSearchMethod(normals_tree);
00183         mls.setSearchRadius(normal_search_radius);
00184         mls.reconstruct(mls_points);
00185 
00186         // convert normal to quaternion
00187         double nx = normals_ptr->points[closest_ind].normal[0];
00188         double ny = normals_ptr->points[closest_ind].normal[1];
00189         double nz = normals_ptr->points[closest_ind].normal[2];
00190         double dot = nx*pt3d_trans.point.x + ny*pt3d_trans.point.y + nz*pt3d_trans.point.z;
00191         if(dot > 0) { nx = -nx; ny = -ny; nz = -nz; }
00192        
00193         //Phil's update, now returns direction of pose (x-axis) along normal
00194         btVector3 normal_vec(nx,ny,nz);
00195         btVector3 x_axis(1.0,0.0,0.0);
00196         btVector3 axis = x_axis.cross(normal_vec);
00197         double angle = x_axis.angle(normal_vec);
00198         btQuaternion quat(axis, angle);
00199         
00200         //Kelsey's solution, returns Z-axis of quaternion along normal
00201         //double j = std::sqrt(1/(1+ny*ny/(nz*nz)));
00202         //double k = -ny*j/nz;
00203         //btMatrix3x3 M (0,  ny*k - nz*j,  nx,      
00204         //               j,  -nx*k,        ny,      
00205         //               k,  nx*j,         nz);
00206         //btQuaternion quat;
00207         //M.getRotation(quat);
00208 
00209         geometry_msgs::PoseStamped pt3d_pose;
00210         pt3d_pose.header.frame_id = cur_pc->header.frame_id;
00211         pt3d_pose.header.stamp = ros::Time(0);
00212         pt3d_pose.pose.position.x = pt3d_trans.point.x;
00213         pt3d_pose.pose.position.y = pt3d_trans.point.y;
00214         pt3d_pose.pose.position.z = pt3d_trans.point.z;
00215         pt3d_pose.pose.orientation.x = quat.getX();
00216         pt3d_pose.pose.orientation.y = quat.getY();
00217         pt3d_pose.pose.orientation.z = quat.getZ();
00218         pt3d_pose.pose.orientation.w = quat.getW();
00219         
00220         if(output_frame == "")
00221             output_frame = cur_pc->header.frame_id;
00222         tf_listener.transformPose(output_frame, pt3d_pose, pt3d_pose);
00223         resp.pixel3d.header.frame_id = output_frame;
00224         resp.pixel3d.header.stamp = ros::Time::now();
00225         resp.pixel3d.pose.position.x = pt3d_pose.pose.position.x;
00226         resp.pixel3d.pose.position.y = pt3d_pose.pose.position.y;
00227         resp.pixel3d.pose.position.z = pt3d_pose.pose.position.z;
00228         resp.pixel3d.pose.orientation.x = pt3d_pose.pose.orientation.x;
00229         resp.pixel3d.pose.orientation.y = pt3d_pose.pose.orientation.y;
00230         resp.pixel3d.pose.orientation.z = pt3d_pose.pose.orientation.z;
00231         resp.pixel3d.pose.orientation.w = pt3d_pose.pose.orientation.w;
00232         pt3d_pub.publish(pt3d_pose);
00233         ROS_INFO("[pixel_2_3d] Pixel (%d, %d) converted to pose (%f, %f, %f), (%f, %f, %f, %f) in %s",
00234                  req.pixel_u, req.pixel_v, 
00235                  pt3d_pose.pose.position.x, pt3d_pose.pose.position.y, pt3d_pose.pose.position.z,
00236                  pt3d_pose.pose.orientation.x, pt3d_pose.pose.orientation.y,
00237                  pt3d_pose.pose.orientation.z, pt3d_pose.pose.orientation.w,
00238                  output_frame.c_str());
00239         return true;
00240     }
00241 
00242     void Pixel23dServer::lClickCallback(const geometry_msgs::PointStamped& click_msg) {
00243         Pixel23d::Request req; Pixel23d::Response resp;
00244         req.pixel_u = click_msg.point.x;
00245         req.pixel_v = click_msg.point.y;
00246         pixCallback(req, resp);
00247     }
00248 
00249 };
00250 
00251 
00252 using namespace pixel_2_3d;
00253 
00254 int main(int argc, char **argv)
00255 {
00256     ros::init(argc, argv, "pixel_2_3d");
00257     Pixel23dServer p3d;
00258     ros::spin();
00259     return 0;
00260 }


pixel_2_3d
Author(s): Kelsey Hawkins / kphawkins@gatech.edu, Advisor: Prof. Charlie Kemp (Healthcare Robotics Lab at Georgia Tech)
autogenerated on Wed Nov 27 2013 11:35:21