hector_barrel_detection.cpp
Go to the documentation of this file.
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     //TODO: change to getDistance2d/3d
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     //if(debug_){
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     //Read image with cvbridge
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     //cut image
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     //    cv::imshow("image",img_filtered);
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     //    cv::imshow("blau",blueOnly);
00064     //    cv::waitKey(1000);
00065 
00066     //Perform blob detection
00067     cv::SimpleBlobDetector::Params params;
00068     params.filterByColor = true;
00069     params.blobColor = 255;
00070     params.minDistBetweenBlobs = 0.5;
00071     params.filterByArea = true;
00072     //TODO: tune parameter
00073     params.minArea = (blueOnly.rows * blueOnly.cols) /16;
00074     //    params.minArea = (blueOnly.rows * blueOnly.cols) / (0.5+distance);
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 //    for(unsigned int i=0; i<keypoints.size();i++)
00087 //    {
00088 //        std::cout << keypoints.at(i).pt.x << std::endl;
00089 //    }
00090     //Publish results
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         //        imagePercept_pub_.publish(ip);
00103         ROS_DEBUG("Barrel blob found at image coord: (%f, %f)", ip.x, ip.y);
00104 
00105         tf::Pose pose;
00106 
00107         // retrieve camera model from either the cache or from CameraInfo given in the percept
00108         CameraModelPtr cameraModel;
00109         cameraModel.reset(new image_geometry::PinholeCameraModel());
00110         cameraModel->fromCameraInfo(info);
00111 
00112         // transform Point using the camera model
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         //  pose.setOrigin(tf::Point(direction_cv.z, -direction_cv.x, -direction_cv.y).normalized() * distance);
00118         //  tf::Quaternion direction(atan2(-direction_cv.x, direction_cv.z), atan2(direction_cv.y, sqrt(direction_cv.z*direction_cv.z + direction_cv.x*direction_cv.x)), 0.0);
00119         pose.setOrigin(tf::Point(direction_cv.x, direction_cv.y, direction_cv.z).normalized() * distance);
00120         {
00121             // set rotation of object so that the x-axis points in the direction of the object and y-axis is parallel to the camera's x-z-plane
00122             // Note: d is given in camera coordinates, while the object's x-axis should point away from the camera.
00123             const tf::Point &d(direction); // for readability
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                 //      pose.setBasis(tf::Matrix3x3( c*d.z(), -c*d.x()*d.y(), d.x(),
00135                 //                                         0, 1./c,           d.y(),
00136                 //                                  -c*d.x(), -c*d.y()*d.z(), d.z()));
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         // project image percept to the next obstacle
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         //transformation point to /map
00156         //TODO:: change base_link to /map
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     //transformation cloud to /map
00192     //TODO:: change base_link to /map
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     // Transform to /map
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     // Filtrar area
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     //              // downsample
00223     //              pcl::VoxelGrid<pcl::PointXYZ> vg;
00224     //              vg.setInputCloud (cloud);
00225     //              vg.setLeafSize (0.03f, 0.03f, 0.03f);
00226     //              vg.filter (*cloud);
00227 
00228     // Publish filtered cloud to ROS for debugging
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     //              // remove outliers
00237     //              pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
00238     //              sor.setInputCloud (cloud);
00239     //              sor.setMeanK (20);
00240     //              sor.setStddevMulThresh (0.1);
00241     //              sor.filter (*cloud);
00242 
00243     //              begin = ros::Time::now();
00244 
00245     // trobar cilindre(s)
00246     ROS_DEBUG("Normal Estimation");
00247     //Estimate point normals
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     // Create the segmentation object for cylinder segmentation and set all the parameters
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     // Cylinder Cloud Publisher
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     //publish debug clysinderPose
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         //Publish results
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         // MARKERS ADD
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         // CYLINDER AND TEXT
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         //red
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         // ONLY CYLINDER
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 }


hector_barrel_detection
Author(s):
autogenerated on Thu Aug 27 2015 13:22:42