cluster_point_indices_decomposer_nodelet.cpp
Go to the documentation of this file.
00001 // -*- mode: C++ -*-
00002 /*********************************************************************
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2013, Ryohei Ueda and 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/cluster_point_indices_decomposer.h"
00037 #include <pluginlib/class_list_macros.h>
00038 #include <pcl/filters/extract_indices.h>
00039 #include <pcl/common/centroid.h>
00040 #include <pcl/common/common.h>
00041 #include <boost/format.hpp>
00042 #include <pcl/registration/ia_ransac.h>
00043 #include <pcl/filters/project_inliers.h>
00044 #include <pcl/common/pca.h>
00045 #include <jsk_topic_tools/color_utils.h>
00046 #include <Eigen/Geometry> 
00047 
00048 #include "jsk_pcl_ros/geo_util.h"
00049 #include "jsk_pcl_ros/pcl_conversion_util.h"
00050 #include "jsk_pcl_ros/pcl_util.h"
00051 
00052 namespace jsk_pcl_ros
00053 {
00054   void ClusterPointIndicesDecomposer::onInit()
00055   {
00056     DiagnosticNodelet::onInit();
00057     pnh_->param("publish_tf", publish_tf_, false);
00058     if (!pnh_->getParam("tf_prefix", tf_prefix_))
00059     {
00060       if (publish_tf_) {
00061         JSK_ROS_WARN("~tf_prefix is not specified, using %s", getName().c_str());
00062       }
00063       tf_prefix_ = getName();
00064     }
00065 
00066     pnh_->param("publish_clouds", publish_clouds_, false);
00067     pnh_->param("align_boxes", align_boxes_, false);
00068     pnh_->param("use_pca", use_pca_, false);
00069     pnh_->param("force_to_flip_z_axis", force_to_flip_z_axis_, true);
00070     pc_pub_ = advertise<sensor_msgs::PointCloud2>(*pnh_, "debug_output", 1);
00071     box_pub_ = advertise<jsk_recognition_msgs::BoundingBoxArray>(*pnh_, "boxes", 1);
00072   }
00073 
00074   void ClusterPointIndicesDecomposer::subscribe()
00075   {
00076     sub_input_.subscribe(*pnh_, "input", 1);
00077     sub_target_.subscribe(*pnh_, "target", 1);
00078     if (align_boxes_) {
00079       sync_align_ = boost::make_shared<message_filters::Synchronizer<SyncAlignPolicy> >(100);
00080       sub_polygons_.subscribe(*pnh_, "align_planes", 1);
00081       sub_coefficients_.subscribe(*pnh_, "align_planes_coefficients", 1);
00082       sync_align_->connectInput(sub_input_, sub_target_, sub_polygons_, sub_coefficients_);
00083       sync_align_->registerCallback(boost::bind(&ClusterPointIndicesDecomposer::extract, this, _1, _2, _3, _4));
00084     }
00085     else {
00086       sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(100);
00087       sync_->connectInput(sub_input_, sub_target_);
00088       sync_->registerCallback(boost::bind(&ClusterPointIndicesDecomposer::extract, this, _1, _2));
00089     }
00090   }
00091 
00092   void ClusterPointIndicesDecomposer::unsubscribe()
00093   {
00094     sub_input_.unsubscribe();
00095     sub_target_.unsubscribe();
00096     if (align_boxes_) {
00097       sub_polygons_.unsubscribe();
00098       sub_coefficients_.unsubscribe();
00099     }
00100   }
00101   
00102   void ClusterPointIndicesDecomposer::sortIndicesOrder
00103   (pcl::PointCloud<pcl::PointXYZ>::Ptr input,
00104    std::vector<pcl::IndicesPtr> indices_array,
00105    std::vector<pcl::IndicesPtr> &output_array)
00106   {
00107     output_array.resize(indices_array.size());
00108     for (size_t i = 0; i < indices_array.size(); i++)
00109     {
00110       output_array[i] = indices_array[i];
00111     }
00112   }
00113 
00114   void ClusterPointIndicesDecomposer::updateDiagnostic(
00115     diagnostic_updater::DiagnosticStatusWrapper &stat)
00116   {
00117     if (vital_checker_->isAlive()) {
00118       stat.summary(diagnostic_msgs::DiagnosticStatus::OK,
00119                    "ClusterPointIndicesDecomposer running");
00120       jsk_topic_tools::addDiagnosticBooleanStat(
00121         "publish_clouds", publish_clouds_, stat);
00122       jsk_topic_tools::addDiagnosticBooleanStat(
00123         "publish_tf", publish_tf_, stat);
00124       jsk_topic_tools::addDiagnosticBooleanStat(
00125         "use_pca", use_pca_, stat);
00126       jsk_topic_tools::addDiagnosticBooleanStat(
00127         "align_boxes", align_boxes_, stat);
00128       stat.add("tf_prefix", tf_prefix_);
00129       stat.add("Clusters (Ave.)", cluster_counter_.mean());
00130     }
00131     else {
00132       jsk_topic_tools::addDiagnosticErrorSummary(
00133         "ClusterPointIndicesDecomposer", vital_checker_, stat);
00134     }
00135   }
00136   
00137   int ClusterPointIndicesDecomposer::findNearestPlane(
00138     const Eigen::Vector4f& center,
00139     const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00140     const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients)
00141   {
00142     double min_distance = DBL_MAX;
00143     int nearest_index = -1;
00144     for (size_t i = 0; i < coefficients->coefficients.size(); i++) {
00145       geometry_msgs::PolygonStamped polygon_msg = planes->polygons[i];
00146       Vertices vertices;
00147       for (size_t j = 0; j < polygon_msg.polygon.points.size(); j++) {
00148         Vertex v;
00149         v[0] = polygon_msg.polygon.points[j].x;
00150         v[1] = polygon_msg.polygon.points[j].y;
00151         v[2] = polygon_msg.polygon.points[j].z;
00152         vertices.push_back(v);
00153       }
00154       ConvexPolygon p(vertices, coefficients->coefficients[i].values);
00155       double distance = p.distanceToPoint(center);
00156       if (distance < min_distance) {
00157         min_distance = distance;
00158         nearest_index = i;
00159       }
00160     }
00161     return nearest_index;
00162   }
00163 
00164   bool ClusterPointIndicesDecomposer::computeBoundingBox
00165   (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud,
00166    const std_msgs::Header header,
00167    const Eigen::Vector4f center,
00168    const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00169    const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients,
00170    jsk_recognition_msgs::BoundingBox& bounding_box)
00171   {
00172     pcl::PointCloud<pcl::PointXYZRGB>::Ptr
00173       segmented_cloud_transformed (new pcl::PointCloud<pcl::PointXYZRGB>);
00174     // align boxes if possible
00175     Eigen::Matrix4f m4 = Eigen::Matrix4f::Identity();
00176     Eigen::Quaternionf q = Eigen::Quaternionf::Identity();
00177     if (align_boxes_) {
00178       int nearest_plane_index = findNearestPlane(center, planes, coefficients);
00179       if (nearest_plane_index == -1) {
00180         segmented_cloud_transformed = segmented_cloud;
00181         JSK_NODELET_ERROR("no planes to align boxes are given");
00182       }
00183       else {
00184         Eigen::Vector3f normal, z_axis;
00185         if (force_to_flip_z_axis_) {
00186           normal[0] = - coefficients->coefficients[nearest_plane_index].values[0];
00187           normal[1] = - coefficients->coefficients[nearest_plane_index].values[1];
00188           normal[2] = - coefficients->coefficients[nearest_plane_index].values[2];
00189         }
00190         else {
00191           normal[0] = coefficients->coefficients[nearest_plane_index].values[0];
00192           normal[1] = coefficients->coefficients[nearest_plane_index].values[1];
00193           normal[2] = coefficients->coefficients[nearest_plane_index].values[2];
00194         }
00195         normal = normal.normalized();
00196         Eigen::Quaternionf rot;
00197         rot.setFromTwoVectors(Eigen::Vector3f::UnitZ(), normal);
00198         Eigen::AngleAxisf rotation_angle_axis(rot);
00199         Eigen::Vector3f rotation_axis = rotation_angle_axis.axis();
00200         double theta = rotation_angle_axis.angle();
00201         if (isnan(theta) ||
00202             isnan(rotation_axis[0]) ||
00203             isnan(rotation_axis[1]) ||
00204             isnan(rotation_axis[2])) {
00205           segmented_cloud_transformed = segmented_cloud;
00206           JSK_NODELET_ERROR("cannot compute angle to align the point cloud: [%f, %f, %f], [%f, %f, %f]",
00207                         z_axis[0], z_axis[1], z_axis[2],
00208                         normal[0], normal[1], normal[2]);
00209         }
00210         else {
00211           Eigen::Matrix3f m = Eigen::Matrix3f::Identity() * rot;
00212           if (use_pca_) {
00213             // first project points to the plane
00214             pcl::PointCloud<pcl::PointXYZRGB>::Ptr projected_cloud
00215               (new pcl::PointCloud<pcl::PointXYZRGB>);
00216             pcl::ProjectInliers<pcl::PointXYZRGB> proj;
00217             proj.setModelType (pcl::SACMODEL_PLANE);
00218             pcl::ModelCoefficients::Ptr
00219               plane_coefficients (new pcl::ModelCoefficients);
00220             plane_coefficients->values
00221               = coefficients->coefficients[nearest_plane_index].values;
00222             proj.setModelCoefficients(plane_coefficients);
00223             proj.setInputCloud(segmented_cloud);
00224             proj.filter(*projected_cloud);
00225             if (projected_cloud->points.size() >= 3) {
00226               pcl::PCA<pcl::PointXYZRGB> pca;
00227               pca.setInputCloud(projected_cloud);
00228               Eigen::Matrix3f eigen = pca.getEigenVectors();
00229               m.col(0) = eigen.col(0);
00230               m.col(1) = eigen.col(1);
00231               // flip axis to satisfy right-handed system
00232               if (m.col(0).cross(m.col(1)).dot(m.col(2)) < 0) {
00233                 m.col(0) = - m.col(0);
00234               }
00235               if (m.col(0).dot(Eigen::Vector3f::UnitX()) < 0) {
00236                 // rotate around z
00237                 m = m * Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitZ());
00238               }
00239             }
00240             else {
00241               JSK_NODELET_ERROR("Too small indices for PCA computation");
00242               return false;
00243             }
00244           }
00245             
00246           // m4 <- m
00247           for (size_t row = 0; row < 3; row++) {
00248             for (size_t column = 0; column < 3; column++) {
00249               m4(row, column) = m(row, column);
00250             }
00251           }
00252           q = m;
00253           Eigen::Matrix4f inv_m = m4.inverse();
00254           pcl::transformPointCloud(*segmented_cloud, *segmented_cloud_transformed, inv_m);
00255         }
00256       }
00257     }
00258     else {
00259       segmented_cloud_transformed = segmented_cloud;
00260     }
00261       
00262     // create a bounding box
00263     Eigen::Vector4f minpt, maxpt;
00264     pcl::getMinMax3D<pcl::PointXYZRGB>(*segmented_cloud_transformed, minpt, maxpt);
00265 
00266     double xwidth = maxpt[0] - minpt[0];
00267     double ywidth = maxpt[1] - minpt[1];
00268     double zwidth = maxpt[2] - minpt[2];
00269     
00270     Eigen::Vector4f center2((maxpt[0] + minpt[0]) / 2.0, (maxpt[1] + minpt[1]) / 2.0, (maxpt[2] + minpt[2]) / 2.0, 1.0);
00271     Eigen::Vector4f center_transformed = m4 * center2;
00272       
00273     bounding_box.header = header;
00274     
00275     bounding_box.pose.position.x = center_transformed[0];
00276     bounding_box.pose.position.y = center_transformed[1];
00277     bounding_box.pose.position.z = center_transformed[2];
00278     bounding_box.pose.orientation.x = q.x();
00279     bounding_box.pose.orientation.y = q.y();
00280     bounding_box.pose.orientation.z = q.z();
00281     bounding_box.pose.orientation.w = q.w();
00282     bounding_box.dimensions.x = xwidth;
00283     bounding_box.dimensions.y = ywidth;
00284     bounding_box.dimensions.z = zwidth;
00285     return true;
00286   }
00287 
00288   void ClusterPointIndicesDecomposer::addToDebugPointCloud
00289   (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud,
00290    size_t i,
00291    pcl::PointCloud<pcl::PointXYZRGB>& debug_output)
00292   {
00293     uint32_t rgb = colorRGBAToUInt32(jsk_topic_tools::colorCategory20(i));
00294     for (size_t j = 0; j < segmented_cloud->points.size(); j++) {
00295       pcl::PointXYZRGB p;
00296       p.x= segmented_cloud->points[j].x;
00297       p.y= segmented_cloud->points[j].y;
00298       p.z= segmented_cloud->points[j].z;
00299       p.rgb = *reinterpret_cast<float*>(&rgb);
00300       debug_output.points.push_back(p);
00301     }
00302   }
00303   
00304   void ClusterPointIndicesDecomposer::extract
00305   (const sensor_msgs::PointCloud2ConstPtr &input,
00306    const jsk_recognition_msgs::ClusterPointIndicesConstPtr &indices_input,
00307    const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00308    const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients)
00309   {
00310     if (publish_clouds_) {
00311       allocatePublishers(indices_input->cluster_indices.size());
00312     }
00313     vital_checker_->poke();
00314     pcl::ExtractIndices<pcl::PointXYZRGB> extract;
00315     pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
00316     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz (new pcl::PointCloud<pcl::PointXYZ>);
00317     pcl::fromROSMsg(*input, *cloud);
00318     pcl::fromROSMsg(*input, *cloud_xyz);
00319     cluster_counter_.add(indices_input->cluster_indices.size());
00320     
00321     std::vector<pcl::IndicesPtr> converted_indices;
00322     std::vector<pcl::IndicesPtr> sorted_indices;
00323     
00324     for (size_t i = 0; i < indices_input->cluster_indices.size(); i++)
00325     {
00326       pcl::IndicesPtr vindices;
00327       vindices.reset (new std::vector<int> (indices_input->cluster_indices[i].indices));
00328       converted_indices.push_back(vindices);
00329     }
00330     
00331     sortIndicesOrder(cloud_xyz, converted_indices, sorted_indices);
00332     extract.setInputCloud(cloud);
00333 
00334     pcl::PointCloud<pcl::PointXYZRGB> debug_output;
00335     jsk_recognition_msgs::BoundingBoxArray bounding_box_array;
00336     bounding_box_array.header = input->header;
00337     for (size_t i = 0; i < sorted_indices.size(); i++)
00338     {
00339       pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
00340       
00341       pcl::PointIndices::Ptr segmented_indices (new pcl::PointIndices);
00342       extract.setIndices(sorted_indices[i]);
00343       extract.filter(*segmented_cloud);
00344       if (publish_clouds_) {
00345         sensor_msgs::PointCloud2::Ptr out_cloud(new sensor_msgs::PointCloud2);
00346         pcl::toROSMsg(*segmented_cloud, *out_cloud);
00347         out_cloud->header = input->header;
00348         publishers_[i].publish(out_cloud);
00349       }
00350       // publish tf
00351       Eigen::Vector4f center;
00352       pcl::compute3DCentroid(*segmented_cloud, center);
00353       if (publish_tf_) {
00354         tf::Transform transform;
00355         transform.setOrigin(tf::Vector3(center[0], center[1], center[2]));
00356         transform.setRotation(tf::createIdentityQuaternion());
00357         br_.sendTransform(tf::StampedTransform(transform, input->header.stamp,
00358                                                input->header.frame_id,
00359                                                tf_prefix_ + (boost::format("output%02u") % (i)).str()));
00360       }
00361       // adding the pointcloud into debug_output
00362       addToDebugPointCloud(segmented_cloud, i, debug_output);
00363       
00364       jsk_recognition_msgs::BoundingBox bounding_box;
00365       bool successp = computeBoundingBox(
00366         segmented_cloud, input->header, center, planes, coefficients, bounding_box);
00367       if (!successp) {
00368         return;
00369       }
00370       bounding_box_array.boxes.push_back(bounding_box);
00371     }
00372     
00373     sensor_msgs::PointCloud2 debug_ros_output;
00374     pcl::toROSMsg(debug_output, debug_ros_output);
00375     debug_ros_output.header = input->header;
00376     debug_ros_output.is_dense = false;
00377     pc_pub_.publish(debug_ros_output);
00378     box_pub_.publish(bounding_box_array);
00379   }
00380   
00381   void ClusterPointIndicesDecomposer::extract
00382   (const sensor_msgs::PointCloud2ConstPtr &input,
00383    const jsk_recognition_msgs::ClusterPointIndicesConstPtr &indices_input)
00384   {
00385     extract(input, indices_input,
00386             jsk_recognition_msgs::PolygonArrayConstPtr(),
00387             jsk_recognition_msgs::ModelCoefficientsArrayConstPtr());
00388   }
00389 
00390   void ClusterPointIndicesDecomposer::allocatePublishers(size_t num)
00391   {
00392     if (num > publishers_.size())
00393     {
00394         for (size_t i = publishers_.size(); i < num; i++)
00395         {
00396             std::string topic_name = (boost::format("output%02u") % (i)).str();
00397             JSK_ROS_INFO("advertising %s", topic_name.c_str());
00398             ros::Publisher publisher = pnh_->advertise<sensor_msgs::PointCloud2>(topic_name, 1);
00399             publishers_.push_back(publisher);
00400         }
00401     }
00402   }
00403   
00404 }
00405 
00406 PLUGINLIB_EXPORT_CLASS (jsk_pcl_ros::ClusterPointIndicesDecomposer,
00407                         nodelet::Nodelet);
00408 


jsk_pcl_ros
Author(s): Yohei Kakiuchi
autogenerated on Wed Sep 16 2015 04:36:47