00001 #include <hector_barrel_detection/hector_barrel_detection.h>
00002
00003 namespace barrel_detection{
00004 BarrelDetection::BarrelDetection()
00005 { ROS_INFO("Barreldetection started");
00006 ros::NodeHandle pnh_("~");
00007 ros::NodeHandle nh_("");
00008 image_transport::ImageTransport it_(pnh_);
00009
00010 pnh_.param("r_min", r_min, 0);
00011 pnh_.param("r_max", r_max, 10);
00012 pnh_.param("g_min", g_min, 0);
00013 pnh_.param("g_max", g_max, 10);
00014 pnh_.param("b_min", b_min, 10);
00015 pnh_.param("b_max", b_max, 255);
00016
00017 pcl_sub = nh_.subscribe("/openni/depth/points", 1, &BarrelDetection::PclCallback, this);
00018 image_sub = it_.subscribeCamera("/openni/rgb/image_color", 10, &BarrelDetection::imageCallback, this);
00019 cloud_filtered_publisher_ = pnh_.advertise<sensor_msgs::PointCloud2> ("cloud_filtered_barrel", 0);
00020 pose_publisher_ = pnh_.advertise<geometry_msgs::PoseStamped> ("pose_filtered_barrel", 0);
00021 barrel_marker_publisher_ = pnh_.advertise<visualization_msgs::MarkerArray> ("marker_filtered_barrel", 0);
00022 imagePercept_pub_ = nh_.advertise<hector_worldmodel_msgs::ImagePercept> ("/worldmodel/image_percept", 0);
00023 posePercept_pub_= nh_.advertise<hector_worldmodel_msgs::PosePercept> ("/worldmodel/pose_percept", 0);
00024 pcl_debug_pub_=nh_.advertise<sensor_msgs::PointCloud2> ("barrel_pcl_debug", 0);
00025 debug_imagePoint_pub_=nh_.advertise<geometry_msgs::PointStamped>("blaDebugPoseEstimate",0);
00026
00027
00028 worldmodel_srv_client_=nh_.serviceClient<hector_nav_msgs::GetDistanceToObstacle>("/hector_octomap_server/get_distance_to_obstacle");
00029
00030 }
00031
00032 BarrelDetection::~BarrelDetection()
00033 {}
00034
00035 void BarrelDetection::imageCallback(const sensor_msgs::ImageConstPtr& img, const sensor_msgs::CameraInfoConstPtr& info){
00036
00037 ROS_INFO("image callback startet");
00038
00039
00040 hector_nav_msgs::GetDistanceToObstacle dist_msgs;
00041 dist_msgs.request.point.header= img->header;
00042 dist_msgs.request.point.point.z= 1;
00043
00044 worldmodel_srv_client_.call(dist_msgs);
00045 float distance = dist_msgs.response.distance;
00046
00047
00048 cv_bridge::CvImageConstPtr cv_ptr;
00049 cv_ptr = cv_bridge::toCvShare(img, sensor_msgs::image_encodings::BGR8);
00050 cv::Mat img_filtered(cv_ptr->image);
00051
00052
00053 float cutPercentage= 0.2;
00054 cv::Size size= img_filtered.size();
00055 img_filtered = img_filtered(cv::Rect(size.width*cutPercentage,size.height*cutPercentage,size.width*(1-2*cutPercentage),size.height*(1-2*cutPercentage)));
00056
00057
00058
00059
00060 cv::Mat blueOnly;
00061 cv::inRange(img_filtered, cv::Scalar( b_min, g_min,r_min), cv::Scalar(b_max, g_max, r_max), blueOnly);
00062
00063
00064
00065
00066
00067 cv::SimpleBlobDetector::Params params;
00068 params.filterByColor = true;
00069 params.blobColor = 255;
00070 params.minDistBetweenBlobs = 0.5;
00071 params.filterByArea = true;
00072
00073 params.minArea = (blueOnly.rows * blueOnly.cols) /16;
00074
00075 params.maxArea = blueOnly.rows * blueOnly.cols;
00076 params.filterByCircularity = false;
00077 params.filterByColor = false;
00078 params.filterByConvexity = false;
00079 params.filterByInertia = false;
00080
00081 cv::SimpleBlobDetector blob_detector(params);
00082 std::vector<cv::KeyPoint> keypoints;
00083 keypoints.clear();
00084
00085 blob_detector.detect(blueOnly,keypoints);
00086
00087
00088
00089
00090
00091 hector_worldmodel_msgs::ImagePercept ip;
00092
00093 ip.header= img->header;
00094 ip.info.class_id = "barrel";
00095 ip.info.class_support = 1;
00096 ip.camera_info = *info;
00097
00098 for(unsigned int i=0; i<keypoints.size();i++)
00099 {
00100 ip.x = keypoints.at(i).pt.x;
00101 ip.y = keypoints.at(i).pt.y;
00102
00103 ROS_DEBUG("Barrel blob found at image coord: (%f, %f)", ip.x, ip.y);
00104
00105 tf::Pose pose;
00106
00107
00108 CameraModelPtr cameraModel;
00109 cameraModel.reset(new image_geometry::PinholeCameraModel());
00110 cameraModel->fromCameraInfo(info);
00111
00112
00113 cv::Point2d rectified = cameraModel->rectifyPoint(cv::Point2d(ip.x, ip.y));
00114 cv::Point3d direction_cv = cameraModel->projectPixelTo3dRay(rectified);
00115 tf::Point direction(direction_cv.x, direction_cv.y, direction_cv.z);
00116 direction.normalize();
00117
00118
00119 pose.setOrigin(tf::Point(direction_cv.x, direction_cv.y, direction_cv.z).normalized() * distance);
00120 {
00121
00122
00123 const tf::Point &d(direction);
00124 if (d.y() >= 0.999) {
00125 pose.setBasis(tf::Matrix3x3( 0., -1., 0.,
00126 1., 0., 0.,
00127 0., 0., 1. ));
00128 } else if (d.y() <= -0.999) {
00129 pose.setBasis(tf::Matrix3x3( 0., -1., 0.,
00130 -1., 0., 0.,
00131 0., 0., -1.));
00132 } else {
00133 double c = 1./sqrt(1. - d.y()*d.y());
00134
00135
00136
00137 pose.setBasis(tf::Matrix3x3(d.x(), -c*d.z(), c*d.x()*d.y(),
00138 d.y(), 0, -1./c,
00139 d.z(), c*d.x(), c*d.y()*d.z() ));
00140 }
00141 }
00142
00143
00144
00145 dist_msgs.request.point.header = ip.header;
00146 tf::pointTFToMsg(pose.getOrigin(), dist_msgs.request.point.point);
00147
00148 worldmodel_srv_client_.call(dist_msgs);
00149
00150 distance = std::max(dist_msgs.response.distance, 0.0f);
00151 pose.setOrigin(pose.getOrigin().normalized() * distance);
00152
00153 tf::pointTFToMsg(pose.getOrigin(), dist_msgs.request.point.point);
00154
00155
00156
00157 const geometry_msgs::PointStamped const_point=dist_msgs.request.point;
00158 geometry_msgs::PointStamped point_in_map;
00159 try{
00160 ros::Time time = img->header.stamp;
00161 listener_.waitForTransform("/map", img->header.frame_id,
00162 time, ros::Duration(3.0));
00163 listener_.transformPoint("/map", const_point, point_in_map);
00164 }
00165 catch (tf::TransformException ex){
00166 ROS_ERROR("Lookup Transform failed: %s",ex.what());
00167 return;
00168 }
00169
00170 debug_imagePoint_pub_.publish(point_in_map);
00171
00172 if(current_pc_msg_!=0){
00173 findCylinder(current_pc_msg_, point_in_map.point.x, point_in_map.point.y);
00174 }
00175
00176 }
00177
00178
00179 }
00180 void BarrelDetection::PclCallback(const sensor_msgs::PointCloud2::ConstPtr& pc_msg){
00181 current_pc_msg_= pc_msg;
00182 }
00183
00184 void BarrelDetection::findCylinder(const sensor_msgs::PointCloud2::ConstPtr &pc_msg, float xKey, float yKey){
00185 ROS_DEBUG("started cylinder search");
00186 pcl::PCLPointCloud2 pcl_pc2;
00187 pcl_conversions::toPCL(*pc_msg,pcl_pc2);
00188 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
00189 pcl::fromPCLPointCloud2(pcl_pc2,*cloud);
00190
00191
00192
00193 tf::StampedTransform transform_cloud_to_map;
00194 try{
00195 ros::Time time = pc_msg->header.stamp;
00196 listener_.waitForTransform("/map", pc_msg->header.frame_id,
00197 time, ros::Duration(3.0));
00198 listener_.lookupTransform("/map", pc_msg->header.frame_id,
00199 time, transform_cloud_to_map);
00200 }
00201 catch (tf::TransformException ex){
00202 ROS_ERROR("Lookup Transform failed: %s",ex.what());
00203 return;
00204 }
00205
00206 tf::transformTFToEigen(transform_cloud_to_map, to_map_);
00207
00208
00209 boost::shared_ptr<pcl::PointCloud<pcl::PointXYZ> > cloud_tmp(new pcl::PointCloud<pcl::PointXYZ>);
00210 pcl::transformPointCloud(*cloud, *cloud_tmp, to_map_);
00211 cloud = cloud_tmp;
00212 cloud->header.frame_id= transform_cloud_to_map.frame_id_;
00213
00214
00215
00216 float zmin=0.2,zmax=1.1;
00217 pass_.setInputCloud (cloud);
00218 pass_.setFilterFieldName ("z");
00219 pass_.setFilterLimits (zmin, zmax);
00220 pass_.filter (*cloud);
00221
00222
00223
00224
00225
00226
00227
00228
00229 if (pcl_debug_pub_.getNumSubscribers() > 0){
00230 sensor_msgs::PointCloud2 filtered_msg;
00231 pcl::toROSMsg(*cloud, filtered_msg);
00232 filtered_msg.header.frame_id = cloud->header.frame_id;
00233 pcl_debug_pub_.publish(filtered_msg);
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246 ROS_DEBUG("Normal Estimation");
00247
00248 pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
00249 ROS_DEBUG("building Tree");
00250 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
00251 pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal> ());
00252 ne.setSearchMethod (tree);
00253 ne.setInputCloud (cloud);
00254 ne.setKSearch (50);
00255 ROS_DEBUG("estimate Normals");
00256 ne.compute (*cloud_normals);
00257
00258
00259 ROS_DEBUG("Set Cylinder coefficients");
00260 pcl::SACSegmentationFromNormals<pcl::PointXYZ, pcl::Normal> seg;
00261 pcl::ModelCoefficients::Ptr coefficients_cylinder (new pcl::ModelCoefficients);
00262 pcl::PointIndices::Ptr inliers_cylinder (new pcl::PointIndices);
00263 seg.setOptimizeCoefficients (true);
00264 seg.setModelType (pcl::SACMODEL_CYLINDER);
00265 seg.setMethodType (pcl::SAC_RANSAC);
00266 seg.setNormalDistanceWeight (0.1);
00267 seg.setMaxIterations (50);
00268 seg.setDistanceThreshold (0.05);
00269 seg.setRadiusLimits (0.1,0.4);
00270 seg.setInputCloud (cloud);
00271 seg.setInputNormals (cloud_normals);
00272 ROS_INFO("search cylinders");
00273 Eigen::Vector3f v = Eigen::Vector3f(0.0, 0.0, 1.0);
00274 seg.setAxis(v);
00275 seg.segment (*inliers_cylinder, *coefficients_cylinder);
00276 ROS_DEBUG_STREAM("Cylinder coefficients: " << *coefficients_cylinder);
00277
00278 ROS_DEBUG("extract cylinder potins");
00279 pcl::ExtractIndices<pcl::PointXYZ> extract;
00280 extract.setInputCloud (cloud);
00281 extract.setIndices (inliers_cylinder);
00282 extract.setNegative (false);
00283 extract.filter (*cloud);
00284 ROS_INFO_STREAM("Extracted: " << cloud->points.size ());
00285
00286
00287 if (cloud_filtered_publisher_.getNumSubscribers() > 0){
00288 sensor_msgs::PointCloud2 cyl_msg;
00289 pcl::toROSMsg(*cloud, cyl_msg);
00290 cyl_msg.header.frame_id = cloud->header.frame_id;
00291 cloud_filtered_publisher_.publish(cyl_msg);
00292 }
00293
00294 geometry_msgs::Point possibleCylinderPoint;
00295 bool inRange= false;
00296 float epsilon= 0.25;
00297 if( cloud->points.size()>0){
00298 possibleCylinderPoint.x= coefficients_cylinder->values[0];
00299 possibleCylinderPoint.y= coefficients_cylinder->values[1];
00300 float square_distance= std::abs(possibleCylinderPoint.x - xKey)*std::abs(possibleCylinderPoint.x - xKey) +
00301 std::abs(possibleCylinderPoint.y - yKey)*std::abs(possibleCylinderPoint.y - yKey);
00302 if(square_distance < epsilon){
00303 inRange=true;
00304 }
00305
00306 }
00307
00308
00309 if (pose_publisher_.getNumSubscribers() > 0){
00310 geometry_msgs::PoseStamped pose_msg;
00311 pose_msg.header.frame_id=cloud->header.frame_id;
00312 pose_msg.header.stamp=pc_msg->header.stamp;
00313 pose_msg.pose.position.x=possibleCylinderPoint.x;
00314 pose_msg.pose.position.y=possibleCylinderPoint.y;
00315 pose_publisher_.publish(pose_msg);
00316 }
00317
00318 if( cloud->points.size()>0 && inRange)
00319 { ROS_DEBUG("publish cylinder ");
00320
00321
00322 hector_worldmodel_msgs::PosePercept pp;
00323
00324 pp.header.frame_id= cloud->header.frame_id;
00325 pp.header.stamp= pc_msg->header.stamp;
00326 pp.info.class_id= "barrel";
00327 pp.info.class_support=1;
00328 pp.info.object_support=1;
00329 pp.pose.pose.position.x= coefficients_cylinder->values[0];
00330 pp.pose.pose.position.y= coefficients_cylinder->values[1];
00331 pp.pose.pose.position.z= 0.6;
00332 pp.pose.pose.orientation.x= pp.pose.pose.orientation.y = pp.pose.pose.orientation.z= 0;
00333 pp.pose.pose.orientation.w= 1;
00334
00335 posePercept_pub_.publish(pp);
00336 ROS_INFO("PosePercept published");
00337
00338
00339 ROS_DEBUG("initialize markerArray");
00340 visualization_msgs::MarkerArray markerArray_msg_;
00341 markerArray_msg_.markers.resize(1);
00342 ROS_DEBUG("markerarry created");
00343 markerArray_msg_.markers[0].action = visualization_msgs::Marker::ADD;
00344 ROS_DEBUG("marker added");
00345
00346 markerArray_msg_.markers[0].header.frame_id = cloud->header.frame_id;
00347 markerArray_msg_.markers[0].header.stamp = pc_msg->header.stamp;
00348 markerArray_msg_.markers[0].id = 0;
00349 markerArray_msg_.markers[0].pose.position.x= pp.pose.pose.position.x;
00350 markerArray_msg_.markers[0].pose.position.y = pp.pose.pose.position.y;
00351 markerArray_msg_.markers[0].pose.position.z = pp.pose.pose.position.z;
00352 markerArray_msg_.markers[0].pose.orientation.x=markerArray_msg_.markers[0].pose.orientation.y= markerArray_msg_.markers[0].pose.orientation.z= pp.pose.pose.orientation.x;
00353 markerArray_msg_.markers[0].pose.orientation.w=1;
00354 ROS_DEBUG("cylinder and text added");
00355
00356 markerArray_msg_.markers[0].color.r = 1.0;
00357 markerArray_msg_.markers[0].color.g = 0.0;
00358 markerArray_msg_.markers[0].color.b = 0.0;
00359 ROS_DEBUG("color added");
00360
00361 markerArray_msg_.markers[0].ns = "cylinder";
00362 markerArray_msg_.markers[0].type = visualization_msgs::Marker::CYLINDER;
00363 markerArray_msg_.markers[0].pose.position.z = 0.6;
00364 markerArray_msg_.markers[0].scale.x = 0.6;
00365 markerArray_msg_.markers[0].scale.y = 0.6;
00366 markerArray_msg_.markers[0].scale.z = 1;
00367 markerArray_msg_.markers[0].color.a = 0.4;
00368 ROS_DEBUG("cylinder only added");
00369 ROS_DEBUG("makerArray complete");
00370 barrel_marker_publisher_.publish(markerArray_msg_);
00371 ROS_DEBUG("markerArray published");
00372
00373
00374 }
00375
00376
00377 }
00378
00379 }