organized_edge_detector_nodelet.cpp
Go to the documentation of this file.
00001 // -*- mode: c++ -*-
00002 /*********************************************************************
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2014, JSK Lab
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/o2r other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of the JSK Lab nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
00034  *********************************************************************/
00035 
00036 #include "jsk_pcl_ros/organized_edge_detector.h"
00037 #include "jsk_recognition_utils/pcl_conversion_util.h"
00038 
00039 #include <pcl/features/organized_edge_detection.h>
00040 #include <pcl/features/integral_image_normal.h>
00041 #include "jsk_recognition_utils/pcl_util.h"
00042 #include <cv_bridge/cv_bridge.h>
00043 #include <sensor_msgs/image_encodings.h>
00044 #include <std_msgs/ColorRGBA.h>
00045 #include <jsk_topic_tools/color_utils.h>
00046 
00047 #include <opencv2/opencv.hpp>
00048 #include <jsk_recognition_msgs/ClusterPointIndices.h>
00049 
00050 namespace jsk_pcl_ros
00051 {
00052   void OrganizedEdgeDetector::onInit()
00053   {
00054     ConnectionBasedNodelet::onInit();
00055 
00057     // setup dynamic reconfigure
00059     srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
00060     dynamic_reconfigure::Server<Config>::CallbackType f =
00061       boost::bind (&OrganizedEdgeDetector::configCallback, this, _1, _2);
00062     srv_->setCallback (f);
00064     // indices publishers
00066     pub_nan_boundary_edges_indices_
00067       = advertise<PCLIndicesMsg>(*pnh_, "output_nan_boundary_edge_indices", 1);
00068     pub_occluding_edges_indices_
00069       = advertise<PCLIndicesMsg>(*pnh_, "output_occluding_edge_indices", 1);
00070     pub_occluded_edges_indices_
00071       = advertise<PCLIndicesMsg>(*pnh_, "output_occluded_edge_indices", 1);
00072     pub_curvature_edges_indices_
00073       = advertise<PCLIndicesMsg>(*pnh_, "output_curvature_edge_indices", 1);
00074     pub_rgb_edges_indices_
00075       = advertise<PCLIndicesMsg>(*pnh_, "output_rgb_edge_indices", 1);
00076     pub_all_edges_indices_
00077       = advertise<PCLIndicesMsg>(*pnh_, "output_indices", 1);
00078     pub_straight_edges_indices_
00079       = advertise<jsk_recognition_msgs::ClusterPointIndices>(*pnh_, 
00080         "output_straight_edges_indices", 1);
00082     // pointcloud publishers
00084     pub_normal_ = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_normal", 1);
00085     pub_nan_boundary_edges_
00086       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_nan_boundary_edge", 1);
00087     pub_occluding_edges_
00088       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_occluding_edge", 1);
00089     pub_occluded_edges_
00090       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_occluded_edge", 1);
00091     pub_curvature_edges_
00092       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_curvature_edge", 1);
00093     pub_rgb_edges_
00094       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output_rgb_edge", 1);
00095     pub_all_edges_
00096       = advertise<sensor_msgs::PointCloud2>(*pnh_, "output", 1);
00098     // image publishers
00100     image_transport::ImageTransport it(*pnh_);
00101     pub_edge_image_ = it.advertise("edge_image", 1);
00102     pub_hough_image_ = it.advertise("hough_image", 1);
00103     onInitPostProcess();
00104   }
00105   
00106   void OrganizedEdgeDetector::subscribe()
00107   {
00108     sub_ = pnh_->subscribe("input", 1, &OrganizedEdgeDetector::estimate, this);
00109   }
00110 
00111   void OrganizedEdgeDetector::unsubscribe()
00112   {
00113     sub_.shutdown();
00114   }
00115   
00116   void OrganizedEdgeDetector::estimateNormal(
00117     const pcl::PointCloud<PointT>::Ptr& input,
00118     pcl::PointCloud<pcl::Normal>::Ptr output,
00119     const std_msgs::Header& header)
00120   {
00121     pcl::IntegralImageNormalEstimation<PointT, pcl::Normal> ne;
00122     if (estimation_method_ == 0) {
00123       ne.setNormalEstimationMethod (ne.AVERAGE_3D_GRADIENT);
00124     }
00125     else if (estimation_method_ == 1) {
00126      ne.setNormalEstimationMethod (ne.COVARIANCE_MATRIX);
00127     }
00128     else if (estimation_method_ == 2) {
00129       ne.setNormalEstimationMethod (ne.AVERAGE_DEPTH_CHANGE);
00130     }
00131     else {
00132       NODELET_FATAL("unknown estimation method: %d", estimation_method_);
00133       return;
00134     }
00135 
00136     if (border_policy_ignore_) {
00137       ne.setBorderPolicy(pcl::IntegralImageNormalEstimation<PointT, pcl::Normal>::BORDER_POLICY_IGNORE);
00138     }
00139     else {
00140       ne.setBorderPolicy(pcl::IntegralImageNormalEstimation<PointT, pcl::Normal>::BORDER_POLICY_MIRROR);
00141     }
00142 
00143     ne.setMaxDepthChangeFactor(max_depth_change_factor_);
00144     ne.setNormalSmoothingSize(normal_smoothing_size_);
00145     ne.setDepthDependentSmoothing(depth_dependent_smoothing_);
00146     ne.setInputCloud(input);
00147     ne.compute(*output);
00148     if (publish_normal_) {
00149       sensor_msgs::PointCloud2 ros_output;
00150       pcl::toROSMsg(*output, ros_output);
00151       ros_output.header = header;
00152       pub_normal_.publish(ros_output);
00153     }
00154   }
00155 
00156   void OrganizedEdgeDetector::configCallback (Config &config, uint32_t level)
00157   {
00158     boost::mutex::scoped_lock lock(mutex_);
00159     max_depth_change_factor_ = config.max_depth_change_factor;
00160     normal_smoothing_size_ = config.normal_smoothing_size;
00161     depth_dependent_smoothing_ = config.depth_dependent_smoothing;
00162     estimation_method_ = config.estimation_method;
00163     border_policy_ignore_ = config.border_policy_ignore;
00164     max_search_neighbors_ = config.max_search_neighbors;
00165     depth_discontinuation_threshold_ = config.depth_discontinuation_threshold;
00166     publish_normal_ = config.publish_normal;
00167     use_nan_boundary_ = config.use_nan_boundary;
00168     use_occluding_ = config.use_occluding;
00169     use_occluded_ = config.use_occluded;
00170     use_curvature_ = config.use_curvature;
00171     use_rgb_ = config.use_rgb;
00172     use_straightline_detection_ = config.use_straightline_detection;
00173     rho_ = config.rho;
00174     theta_ = config.theta;
00175     straightline_threshold_ = config.straightline_threshold;
00176     min_line_length_ = config.min_line_length;
00177     max_line_gap_ = config.max_line_gap;
00178     publish_debug_image_ = config.publish_debug_image;
00179   }
00180   
00181   void OrganizedEdgeDetector::estimateEdge(
00182     const pcl::PointCloud<PointT>::Ptr& input,
00183     const pcl::PointCloud<pcl::Normal>::Ptr& normal,
00184     pcl::PointCloud<pcl::Label>::Ptr& output,
00185     std::vector<pcl::PointIndices>& label_indices)
00186   {
00187     pcl::OrganizedEdgeFromRGBNormals<PointT, pcl::Normal, pcl::Label> oed;
00188     oed.setDepthDisconThreshold (depth_discontinuation_threshold_);
00189     oed.setMaxSearchNeighbors (max_search_neighbors_);
00190     int flags = 0;
00191     if (use_nan_boundary_) {
00192       flags |= oed.EDGELABEL_NAN_BOUNDARY;
00193     }
00194     if (use_occluding_) {
00195       flags |= oed.EDGELABEL_OCCLUDING;
00196     }
00197     if (use_occluded_) {
00198       flags |= oed.EDGELABEL_OCCLUDED;
00199     }
00200     if (use_curvature_) {
00201       flags |= oed.EDGELABEL_HIGH_CURVATURE;
00202     }
00203     if (use_rgb_) {
00204       flags |= oed.EDGELABEL_RGB_CANNY;
00205     }
00206     oed.setEdgeType (flags);
00207     oed.setInputNormals(normal);
00208     oed.setInputCloud(input);
00209     oed.compute(*output, label_indices);
00210   }
00211 
00212   void OrganizedEdgeDetector::publishIndices(
00213     ros::Publisher& pub,
00214     ros::Publisher& pub_indices,
00215     const pcl::PointCloud<PointT>::Ptr& cloud,
00216     const std::vector<int>& indices,
00217     const std_msgs::Header& header)
00218   {
00220     // publish indices
00222     PCLIndicesMsg msg;
00223     msg.header = header;
00224     msg.indices = indices;
00225     pub_indices.publish(msg);
00226 
00228     // publish cloud
00230     pcl::PointCloud<PointT>::Ptr output(new pcl::PointCloud<PointT>);
00231     pcl::copyPointCloud(*cloud, indices, *output);
00232     sensor_msgs::PointCloud2 ros_output;
00233     pcl::toROSMsg(*output, ros_output);
00234     ros_output.header = header;
00235     pub.publish(ros_output);
00236   }
00237 
00238   void OrganizedEdgeDetector::publishStraightEdges(
00239     const pcl::PointCloud<PointT>::Ptr& cloud,
00240     const std_msgs::Header& header,
00241     const std::vector<std::vector<int> > indices)
00242   {
00243     // output as cluster indices
00244     jsk_recognition_msgs::ClusterPointIndices ros_msg;
00245     ros_msg.header = header;
00246     ros_msg.cluster_indices.resize(indices.size());
00247     for (size_t i = 0; i < indices.size(); i++) {
00248       PCLIndicesMsg ros_indices;
00249       ros_indices.header = header;
00250       ros_indices.indices = indices[i];
00251       ros_msg.cluster_indices[i] = ros_indices;
00252     }
00253     pub_straight_edges_indices_.publish(ros_msg);
00254   }
00255   
00256   void OrganizedEdgeDetector::estimateStraightEdges(
00257     const pcl::PointCloud<PointT>::Ptr& cloud,
00258     const std::vector<int>& indices,
00259     const std_msgs::Header& header,
00260     std::vector<std::vector<int> >& output_indices)
00261   {
00262     // initialize all the value by 0
00263     cv::Mat mat = cv::Mat(cloud->height, cloud->width, CV_8UC1) * 0;
00264     for (size_t i = 0; i < indices.size(); i++) {
00265       int index = indices[i];
00266       int index_height = index / cloud->width;
00267       int index_width = index % cloud->width;
00268       mat.data[index_height * mat.step + index_width * mat.elemSize()] = 255;
00269     }
00270     
00271     std::vector<cv::Vec4i> lines;
00272     cv::HoughLinesP(mat, lines, rho_, theta_ * CV_PI/180,
00273                     straightline_threshold_, min_line_length_, max_line_gap_);
00274     output_indices.resize(lines.size());
00275     std::set<int> all_indices_set(indices.begin(), indices.end());
00276     for (size_t i_line = 0; i_line < lines.size(); i_line++) {
00277       std::vector<int> pixels;
00278       cv::LineIterator it(mat,
00279                           cv::Point(lines[i_line][0], lines[i_line][1]),
00280                           cv::Point(lines[i_line][2], lines[i_line][3]), 4);
00281       for(int i_pixel = 0; i_pixel < it.count; i_pixel++, ++it) {
00282         cv::Point point = it.pos();
00283         int flatten_index = point.x + point.y * cloud->width;
00284         // check if flatten_index is included in indices or not
00285         if (all_indices_set.find(flatten_index) != all_indices_set.end()) {
00286           pixels.push_back(flatten_index);
00287         }
00288       }
00289       output_indices[i_line] = pixels;
00290     }
00291     if (publish_debug_image_) {
00292       cv::Mat color_dst;
00293       cv::cvtColor(mat, color_dst, CV_GRAY2BGR );
00294       for( size_t i = 0; i < lines.size(); i++ )
00295       {
00296         std_msgs::ColorRGBA c = jsk_topic_tools::colorCategory20(i);
00297         cv::line(color_dst,
00298                  cv::Point(lines[i][0], lines[i][1]),
00299                  cv::Point(lines[i][2], lines[i][3]),
00300                  cv::Scalar((uint8_t)(c.b * 255), (uint8_t)(c.g * 255), (uint8_t)(c.r * 255)), 3, 8);
00301       }
00302       sensor_msgs::Image::Ptr ros_edge_image
00303         = cv_bridge::CvImage(header,
00304                              sensor_msgs::image_encodings::MONO8,
00305                              mat).toImageMsg();
00306       pub_edge_image_.publish(ros_edge_image);
00307       sensor_msgs::Image::Ptr ros_hough_image
00308         = cv_bridge::CvImage(header,
00309                              sensor_msgs::image_encodings::BGR8,
00310                              color_dst).toImageMsg();
00311       pub_hough_image_.publish(ros_hough_image);
00312     }
00313   }
00314   
00315   void OrganizedEdgeDetector::estimate(
00316     const sensor_msgs::PointCloud2::ConstPtr& msg)
00317   {
00318     boost::mutex::scoped_lock lock(mutex_);
00319     if (msg->height == 1) {
00320       NODELET_ERROR("[OrganizedEdgeDetector] organized pointcloud is required");
00321       return;
00322     }
00323     pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
00324     pcl::PointCloud<pcl::Normal>::Ptr normal(new pcl::PointCloud<pcl::Normal>);
00325     pcl::PointCloud<pcl::Label>::Ptr label(new pcl::PointCloud<pcl::Label>);
00326     std::vector<pcl::PointIndices> label_indices;
00327     pcl::fromROSMsg(*msg, *cloud);
00328     
00329     estimateNormal(cloud, normal, msg->header);
00330     estimateEdge(cloud, normal, label, label_indices);
00332     // build indices includes all the indices
00334     std::vector<int> tmp1 = jsk_recognition_utils::addIndices(label_indices[0].indices,
00335                                        label_indices[1].indices);
00336     std::vector<int> tmp2 = jsk_recognition_utils::addIndices(tmp1,
00337                                        label_indices[2].indices);
00338     std::vector<int> tmp3 = jsk_recognition_utils::addIndices(tmp2,
00339                                        label_indices[3].indices);
00340     std::vector<int> all = jsk_recognition_utils::addIndices(tmp3,
00341                                        label_indices[4].indices);
00342     if (use_straightline_detection_) {
00343       std::vector<std::vector<int> > straightline_indices;
00344       estimateStraightEdges(cloud, all, msg->header, straightline_indices);
00345       // publish the result
00346       publishStraightEdges(cloud, msg->header, straightline_indices);
00347     }
00349     // publish result
00351     publishIndices(pub_nan_boundary_edges_, pub_nan_boundary_edges_indices_,
00352                    cloud,
00353                    label_indices[0].indices, msg->header);
00354     publishIndices(pub_occluding_edges_, pub_occluding_edges_indices_,
00355                    cloud,
00356                    label_indices[1].indices, msg->header);
00357     publishIndices(pub_occluded_edges_, pub_occluded_edges_indices_,
00358                    cloud,
00359                    label_indices[2].indices, msg->header);
00360     publishIndices(pub_curvature_edges_, pub_curvature_edges_indices_,
00361                    cloud,
00362                    label_indices[3].indices, msg->header);
00363     publishIndices(pub_rgb_edges_, pub_rgb_edges_indices_,
00364                    cloud,
00365                    label_indices[4].indices, msg->header);
00366     publishIndices(pub_all_edges_, pub_all_edges_indices_,
00367                    cloud,
00368                    all, msg->header);
00369   }
00370 }
00371 
00372 #include <pluginlib/class_list_macros.h>
00373 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::OrganizedEdgeDetector, nodelet::Nodelet);


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Tue Jul 2 2019 19:41:44