00001
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
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
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
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
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
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
00201
00202
00203
00204
00205
00206
00207
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 }