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 <cv_bridge/cv_bridge.h>
00038 #include <pluginlib/class_list_macros.h>
00039 #include <pcl/filters/extract_indices.h>
00040 #include <pcl/common/centroid.h>
00041 #include <pcl/common/common.h>
00042 #include <boost/format.hpp>
00043 #include <boost/range/adaptors.hpp>
00044 #include <boost/range/algorithm.hpp>
00045 #include <boost/range/irange.hpp>
00046 #include <pcl/registration/ia_ransac.h>
00047 #include <pcl/filters/project_inliers.h>
00048 #include <pcl/common/pca.h>
00049 #include <sensor_msgs/image_encodings.h>
00050 #include <jsk_topic_tools/color_utils.h>
00051 #include <Eigen/Geometry> 
00052 #include <geometry_msgs/PoseArray.h>
00053 
00054 #include "jsk_recognition_utils/geo_util.h"
00055 #include "jsk_recognition_utils/pcl_conversion_util.h"
00056 #include "jsk_recognition_utils/pcl_util.h"
00057 
00058 namespace jsk_pcl_ros
00059 {
00060   void ClusterPointIndicesDecomposerZAxis::onInit()
00061   {
00062     ClusterPointIndicesDecomposer::onInit();
00063     sort_by_ = "z_axis";
00064   }
00065 
00066   void ClusterPointIndicesDecomposer::onInit()
00067   {
00068     DiagnosticNodelet::onInit();
00069     pnh_->param("publish_tf", publish_tf_, false);
00070     if (publish_tf_) {
00071       br_.reset(new tf::TransformBroadcaster);
00072     }
00073     if (!pnh_->getParam("tf_prefix", tf_prefix_))
00074     {
00075       if (publish_tf_) {
00076         NODELET_WARN("~tf_prefix is not specified, using %s", getName().c_str());
00077       }
00078       tf_prefix_ = getName();
00079     }
00080 
00081     // fixed parameters
00082     pnh_->param("approximate_sync", use_async_, false);
00083     pnh_->param("queue_size", queue_size_, 100);
00084     pnh_->param("publish_clouds", publish_clouds_, false);
00085     if (publish_clouds_) {
00086       NODELET_WARN("~output%%02d are not published before subscribed, you should subscribe ~debug_output in debuging.");
00087     }
00088     pnh_->param("align_boxes", align_boxes_, false);
00089     if (align_boxes_) {
00090       pnh_->param("align_boxes_with_plane", align_boxes_with_plane_, true);
00091     } else if (pnh_->hasParam("align_boxes_with_plane")) {
00092       NODELET_WARN("Rosparam ~align_boxes_with_plane is used only with ~align_boxes:=true, so ignoring it.");
00093     }
00094     if (align_boxes_ && !align_boxes_with_plane_) {
00095       tf_listener_ = jsk_recognition_utils::TfListenerSingleton::getInstance();
00096       if (!pnh_->getParam("target_frame_id", target_frame_id_)) {
00097         NODELET_FATAL("~target_frame_id is not specified");
00098         return;
00099       }
00100       NODELET_INFO("Aligning bboxes with '%s' using tf transform.", target_frame_id_.c_str());
00101     } else if (pnh_->hasParam("target_frame_id")) {
00102       NODELET_WARN("Rosparam ~target_frame_id is used only with ~align_boxes:=true and ~align_boxes_with_plane:=true, so ignoring it.");
00103     }
00104     pnh_->param("use_pca", use_pca_, false);
00105     pnh_->param("force_to_flip_z_axis", force_to_flip_z_axis_, true);
00106     pnh_->param<std::string>("sort_by", sort_by_, "input_indices");
00107     // dynamic_reconfigure
00108     srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> >(*pnh_);
00109     dynamic_reconfigure::Server<Config>::CallbackType f =
00110       boost::bind(&ClusterPointIndicesDecomposer::configCallback, this, _1, _2);
00111     srv_->setCallback(f);
00112 
00113     negative_indices_pub_ = advertise<pcl_msgs::PointIndices>(*pnh_, "negative_indices", 1);
00114     pc_pub_ = advertise<sensor_msgs::PointCloud2>(*pnh_, "debug_output", 1);
00115     box_pub_ = advertise<jsk_recognition_msgs::BoundingBoxArray>(*pnh_, "boxes", 1);
00116     mask_pub_ = advertise<sensor_msgs::Image>(*pnh_, "mask", 1);
00117     label_pub_ = advertise<sensor_msgs::Image>(*pnh_, "label", 1);
00118     centers_pub_ = advertise<geometry_msgs::PoseArray>(*pnh_, "centroid_pose_array", 1);
00119     indices_pub_ = advertise<jsk_recognition_msgs::ClusterPointIndices>(*pnh_, "cluster_indices", 1);
00120 
00121     onInitPostProcess();
00122   }
00123 
00124   void ClusterPointIndicesDecomposer::configCallback(Config &config, uint32_t level)
00125   {
00126     boost::mutex::scoped_lock(mutex_);
00127     max_size_ = config.max_size;
00128     min_size_ = config.min_size;
00129   }
00130 
00131   void ClusterPointIndicesDecomposer::subscribe()
00132   {
00133     sub_input_.subscribe(*pnh_, "input", 1);
00134     sub_target_.subscribe(*pnh_, "target", 1);
00135     if (align_boxes_ && align_boxes_with_plane_) {
00136       sync_align_ = boost::make_shared<message_filters::Synchronizer<SyncAlignPolicy> >(queue_size_);
00137       sub_polygons_.subscribe(*pnh_, "align_planes", 1);
00138       sub_coefficients_.subscribe(*pnh_, "align_planes_coefficients", 1);
00139       sync_align_->connectInput(sub_input_, sub_target_, sub_polygons_, sub_coefficients_);
00140       sync_align_->registerCallback(boost::bind(&ClusterPointIndicesDecomposer::extract, this, _1, _2, _3, _4));
00141     }
00142     else if (use_async_) {
00143       async_ = boost::make_shared<message_filters::Synchronizer<ApproximateSyncPolicy> >(queue_size_);
00144       async_->connectInput(sub_input_, sub_target_);
00145       async_->registerCallback(boost::bind(&ClusterPointIndicesDecomposer::extract, this, _1, _2));
00146     }
00147     else {
00148       sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(queue_size_);
00149       sync_->connectInput(sub_input_, sub_target_);
00150       sync_->registerCallback(boost::bind(&ClusterPointIndicesDecomposer::extract, this, _1, _2));
00151     }
00152   }
00153 
00154   void ClusterPointIndicesDecomposer::unsubscribe()
00155   {
00156     sub_input_.unsubscribe();
00157     sub_target_.unsubscribe();
00158     if (align_boxes_ && align_boxes_with_plane_) {
00159       sub_polygons_.unsubscribe();
00160       sub_coefficients_.unsubscribe();
00161     }
00162   }
00163 
00164   void ClusterPointIndicesDecomposer::sortIndicesOrder(
00165     const pcl::PointCloud<pcl::PointXYZ>::Ptr input,
00166     const std::vector<pcl::IndicesPtr> indices_array,
00167     std::vector<size_t>* argsort)
00168   {
00169     std::string sort_by = sort_by_;
00170     bool reverse = false;
00171     if (sort_by.compare(0, 1, "-") == 0)
00172     {
00173       reverse = true;
00174       sort_by = sort_by.substr(1, sort_by.size() - 1);
00175     }
00176     if (sort_by == "input_indices")
00177     {
00178       sortIndicesOrderByIndices(input, indices_array, argsort);
00179     }
00180     else if (sort_by == "z_axis")
00181     {
00182       sortIndicesOrderByZAxis(input, indices_array, argsort);
00183     }
00184     else if (sort_by == "cloud_size")
00185     {
00186       sortIndicesOrderByCloudSize(input, indices_array, argsort);
00187     }
00188     else
00189     {
00190       NODELET_WARN_ONCE("Unsupported ~sort_by param is specified '%s', "
00191                         "so using the default: 'input_indices'", sort_by_.c_str());
00192       sortIndicesOrderByIndices(input, indices_array, argsort);
00193       return;
00194     }
00195     if (reverse)
00196     {
00197       std::reverse((*argsort).begin(), (*argsort).end());
00198     }
00199   }
00200 
00201   void ClusterPointIndicesDecomposer::sortIndicesOrderByIndices(
00202     const pcl::PointCloud<pcl::PointXYZ>::Ptr input,
00203     const std::vector<pcl::IndicesPtr> indices_array,
00204     std::vector<size_t>* argsort)
00205   {
00206     (*argsort).resize(indices_array.size());
00207     for (size_t i = 0; i < indices_array.size(); i++)
00208     {
00209       (*argsort)[i] = i;
00210     }
00211   }
00212 
00213   void ClusterPointIndicesDecomposer::sortIndicesOrderByZAxis(
00214     const pcl::PointCloud<pcl::PointXYZ>::Ptr input,
00215     const std::vector<pcl::IndicesPtr> indices_array,
00216     std::vector<size_t>* argsort)
00217   {
00218     std::vector<double> z_values;
00219     pcl::ExtractIndices<pcl::PointXYZ> ex;
00220     ex.setInputCloud(input);
00221     for (size_t i = 0; i < indices_array.size(); i++)
00222     {
00223       Eigen::Vector4f center;
00224       ex.setIndices(indices_array[i]);
00225       pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
00226       ex.filter(*cloud);
00227       //
00228       std::vector<int> nan_indices;
00229       pcl::removeNaNFromPointCloud(*cloud, *cloud, nan_indices);
00230       //
00231       pcl::compute3DCentroid(*cloud, center);
00232       z_values.push_back(center[2]); // only focus on z value
00233     }
00234 
00235     // sort centroids
00236     // https://stackoverflow.com/a/12399290
00237     (*argsort).resize(indices_array.size());
00238     std::iota(argsort->begin(), argsort->end(), 0);
00239     std::sort(argsort->begin(), argsort->end(),
00240               [&z_values](size_t i1, size_t i2) {return z_values[i1] < z_values[i2];});
00241   }
00242 
00243   void ClusterPointIndicesDecomposer::sortIndicesOrderByCloudSize(
00244     const pcl::PointCloud<pcl::PointXYZ>::Ptr input,
00245     const std::vector<pcl::IndicesPtr> indices_array,
00246     std::vector<size_t>* argsort)
00247   {
00248     std::vector<double> cloud_sizes;
00249     pcl::ExtractIndices<pcl::PointXYZ> ex;
00250     ex.setInputCloud(input);
00251     for (size_t i = 0; i < indices_array.size(); i++)
00252     {
00253       Eigen::Vector4f center;
00254       ex.setIndices(indices_array[i]);
00255       pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
00256       ex.filter(*cloud);
00257       //
00258       std::vector<int> nan_indices;
00259       pcl::removeNaNFromPointCloud(*cloud, *cloud, nan_indices);
00260       //
00261       double cloud_size = static_cast<double>(cloud->points.size());
00262       cloud_sizes.push_back(cloud_size);
00263     }
00264 
00265     // sort clouds
00266     (*argsort).resize(indices_array.size());
00267     std::iota(argsort->begin(), argsort->end(), 0);
00268     std::sort(argsort->begin(), argsort->end(),
00269               [&cloud_sizes](size_t i1, size_t i2) {return cloud_sizes[i1] < cloud_sizes[i2];});
00270   }
00271 
00272   void ClusterPointIndicesDecomposer::updateDiagnostic(
00273     diagnostic_updater::DiagnosticStatusWrapper &stat)
00274   {
00275     if (vital_checker_->isAlive()) {
00276       stat.summary(diagnostic_msgs::DiagnosticStatus::OK,
00277                    "ClusterPointIndicesDecomposer running");
00278       jsk_topic_tools::addDiagnosticBooleanStat(
00279         "publish_clouds", publish_clouds_, stat);
00280       jsk_topic_tools::addDiagnosticBooleanStat(
00281         "publish_tf", publish_tf_, stat);
00282       jsk_topic_tools::addDiagnosticBooleanStat(
00283         "use_pca", use_pca_, stat);
00284       jsk_topic_tools::addDiagnosticBooleanStat(
00285         "align_boxes", align_boxes_, stat);
00286       stat.add("tf_prefix", tf_prefix_);
00287       stat.add("Clusters (Ave.)", cluster_counter_.mean());
00288     }
00289     DiagnosticNodelet::updateDiagnostic(stat);
00290   }
00291   
00292   int ClusterPointIndicesDecomposer::findNearestPlane(
00293     const Eigen::Vector4f& center,
00294     const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00295     const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients)
00296   {
00297     double min_distance = DBL_MAX;
00298     int nearest_index = -1;
00299     for (size_t i = 0; i < coefficients->coefficients.size(); i++) {
00300       geometry_msgs::PolygonStamped polygon_msg = planes->polygons[i];
00301       jsk_recognition_utils::Vertices vertices;
00302       for (size_t j = 0; j < polygon_msg.polygon.points.size(); j++) {
00303         jsk_recognition_utils::Vertex v;
00304         v[0] = polygon_msg.polygon.points[j].x;
00305         v[1] = polygon_msg.polygon.points[j].y;
00306         v[2] = polygon_msg.polygon.points[j].z;
00307         vertices.push_back(v);
00308       }
00309       jsk_recognition_utils::ConvexPolygon p(vertices, coefficients->coefficients[i].values);
00310       double distance = p.distanceToPoint(center);
00311       if (distance < min_distance) {
00312         min_distance = distance;
00313         nearest_index = i;
00314       }
00315     }
00316     return nearest_index;
00317   }
00318 
00319   bool ClusterPointIndicesDecomposer::transformPointCloudToAlignWithPlane(
00320     const pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud,
00321     pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud_transformed,
00322     const Eigen::Vector4f center,
00323     const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00324     const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients,
00325     Eigen::Matrix4f& m4,
00326     Eigen::Quaternionf& q)
00327   {
00328     int nearest_plane_index = findNearestPlane(center, planes, coefficients);
00329     if (nearest_plane_index == -1) {
00330       segmented_cloud_transformed = segmented_cloud;
00331       NODELET_ERROR("no planes to align boxes are given");
00332     }
00333     else {
00334       Eigen::Vector3f normal, z_axis;
00335       if (force_to_flip_z_axis_) {
00336         normal[0] = - coefficients->coefficients[nearest_plane_index].values[0];
00337         normal[1] = - coefficients->coefficients[nearest_plane_index].values[1];
00338         normal[2] = - coefficients->coefficients[nearest_plane_index].values[2];
00339       }
00340       else {
00341         normal[0] = coefficients->coefficients[nearest_plane_index].values[0];
00342         normal[1] = coefficients->coefficients[nearest_plane_index].values[1];
00343         normal[2] = coefficients->coefficients[nearest_plane_index].values[2];
00344       }
00345       normal = normal.normalized();
00346       Eigen::Quaternionf rot;
00347       rot.setFromTwoVectors(Eigen::Vector3f::UnitZ(), normal);
00348       Eigen::AngleAxisf rotation_angle_axis(rot);
00349       Eigen::Vector3f rotation_axis = rotation_angle_axis.axis();
00350       double theta = rotation_angle_axis.angle();
00351       if (std::isnan(theta) ||
00352           std::isnan(rotation_axis[0]) ||
00353           std::isnan(rotation_axis[1]) ||
00354           std::isnan(rotation_axis[2])) {
00355         segmented_cloud_transformed = segmented_cloud;
00356         NODELET_ERROR("cannot compute angle to align the point cloud: [%f, %f, %f], [%f, %f, %f]",
00357                       z_axis[0], z_axis[1], z_axis[2],
00358                       normal[0], normal[1], normal[2]);
00359       }
00360       else {
00361         Eigen::Matrix3f m = Eigen::Matrix3f::Identity() * rot;
00362         if (use_pca_) {
00363           // first project points to the plane
00364           pcl::PointCloud<pcl::PointXYZRGB>::Ptr projected_cloud
00365             (new pcl::PointCloud<pcl::PointXYZRGB>);
00366           pcl::ProjectInliers<pcl::PointXYZRGB> proj;
00367           proj.setModelType (pcl::SACMODEL_PLANE);
00368           pcl::ModelCoefficients::Ptr
00369             plane_coefficients (new pcl::ModelCoefficients);
00370           plane_coefficients->values
00371             = coefficients->coefficients[nearest_plane_index].values;
00372           proj.setModelCoefficients(plane_coefficients);
00373           proj.setInputCloud(segmented_cloud);
00374           proj.filter(*projected_cloud);
00375           if (projected_cloud->points.size() >= 3) {
00376             pcl::PCA<pcl::PointXYZRGB> pca;
00377             pca.setInputCloud(projected_cloud);
00378             Eigen::Matrix3f eigen = pca.getEigenVectors();
00379             m.col(0) = eigen.col(0);
00380             m.col(1) = eigen.col(1);
00381             // flip axis to satisfy right-handed system
00382             if (m.col(0).cross(m.col(1)).dot(m.col(2)) < 0) {
00383               m.col(0) = - m.col(0);
00384             }
00385             if (m.col(0).dot(Eigen::Vector3f::UnitX()) < 0) {
00386               // rotate around z
00387               m = m * Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitZ());
00388             }
00389           }
00390           else {
00391             NODELET_ERROR("Too small indices for PCA computation");
00392             return false;
00393           }
00394         }
00395         // m4 <- m
00396         for (size_t row = 0; row < 3; row++) {
00397           for (size_t column = 0; column < 3; column++) {
00398             m4(row, column) = m(row, column);
00399           }
00400         }
00401         q = m;
00402         q.normalize();
00403         Eigen::Matrix4f inv_m = m4.inverse();
00404         pcl::transformPointCloud(*segmented_cloud, *segmented_cloud_transformed, inv_m);
00405       }
00406     }
00407     return true;
00408   }
00409 
00410   bool ClusterPointIndicesDecomposer::computeCenterAndBoundingBox
00411   (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud,
00412    const std_msgs::Header header,
00413    const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00414    const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients,
00415    geometry_msgs::Pose& center_pose_msg,
00416    jsk_recognition_msgs::BoundingBox& bounding_box)
00417   {
00418     bounding_box.header = header;
00419     if (segmented_cloud->points.size() == 0) {
00420       NODELET_WARN("segmented cloud size is zero");
00421       return true;
00422     }
00423 
00424     bool is_center_valid = false;
00425     Eigen::Vector4f center;
00426     pcl::PointCloud<pcl::PointXYZRGB>::Ptr
00427       segmented_cloud_transformed (new pcl::PointCloud<pcl::PointXYZRGB>);
00428     segmented_cloud_transformed->is_dense = segmented_cloud->is_dense;
00429     // align boxes if possible
00430     Eigen::Matrix4f m4 = Eigen::Matrix4f::Identity();
00431     Eigen::Quaternionf q = Eigen::Quaternionf::Identity();
00432     if (align_boxes_) {
00433       if (align_boxes_with_plane_) {
00434         is_center_valid = pcl::compute3DCentroid(*segmented_cloud, center) != 0;
00435         bool success = transformPointCloudToAlignWithPlane(segmented_cloud, segmented_cloud_transformed,
00436                                                            center, planes, coefficients, m4, q);
00437         if (!success) {
00438           return false;
00439         }
00440       }
00441       else {
00442         // transform point cloud to target frame
00443         tf::StampedTransform tf_transform;
00444         try {
00445           tf_transform = jsk_recognition_utils::lookupTransformWithDuration(
00446             /*listener=*/tf_listener_,
00447             /*to_frame=*/header.frame_id,                // box origin
00448             /*from_frame=*/target_frame_id_,  // sensor origin
00449             /*time=*/header.stamp,
00450             /*duration=*/ros::Duration(1.0));
00451         }
00452         catch (tf2::TransformException &e) {
00453           NODELET_ERROR("Transform error: %s", e.what());
00454           return false;
00455         }
00456         Eigen::Affine3f transform;
00457         tf::transformTFToEigen(tf_transform, transform);
00458         pcl::transformPointCloud(*segmented_cloud, *segmented_cloud, transform);
00459 
00460         is_center_valid = pcl::compute3DCentroid(*segmented_cloud, center) != 0;
00461 
00462         // compute planes from target frame
00463         pcl::PointXYZRGB min_pt, max_pt;
00464         pcl::getMinMax3D(*segmented_cloud, min_pt, max_pt);
00465         //
00466         pcl_msgs::ModelCoefficients coef_by_frame;
00467         coef_by_frame.values.push_back(0);
00468         coef_by_frame.values.push_back(0);
00469         coef_by_frame.values.push_back(1);
00470         coef_by_frame.values.push_back(- min_pt.z);
00471         jsk_recognition_msgs::ModelCoefficientsArray::Ptr coefficients_by_frame(
00472           new jsk_recognition_msgs::ModelCoefficientsArray);
00473         coefficients_by_frame->header.frame_id = target_frame_id_;
00474         coefficients_by_frame->coefficients.push_back(coef_by_frame);
00475         //
00476         geometry_msgs::PolygonStamped plane_by_frame;
00477         plane_by_frame.header.frame_id = target_frame_id_;
00478         geometry_msgs::Point32 point;
00479         point.z = min_pt.z;
00480         point.x = min_pt.x;
00481         point.y = min_pt.y;
00482         plane_by_frame.polygon.points.push_back(point);
00483         point.x = max_pt.x;
00484         point.y = min_pt.y;
00485         plane_by_frame.polygon.points.push_back(point);
00486         point.x = max_pt.x;
00487         point.y = max_pt.y;
00488         plane_by_frame.polygon.points.push_back(point);
00489         point.x = min_pt.x;
00490         point.y = max_pt.y;
00491         plane_by_frame.polygon.points.push_back(point);
00492         jsk_recognition_msgs::PolygonArray::Ptr planes_by_frame(
00493           new jsk_recognition_msgs::PolygonArray);
00494         planes_by_frame->header.frame_id = target_frame_id_;
00495         planes_by_frame->polygons.push_back(plane_by_frame);
00496         //
00497         bool success = transformPointCloudToAlignWithPlane(segmented_cloud, segmented_cloud_transformed,
00498                                                            center, planes_by_frame, coefficients_by_frame, m4, q);
00499         if (!success) {
00500           return false;
00501         }
00502       }
00503     }
00504     else {
00505       segmented_cloud_transformed = segmented_cloud;
00506       is_center_valid = pcl::compute3DCentroid(*segmented_cloud_transformed, center) != 0;
00507     }
00508       
00509     // create a bounding box
00510     Eigen::Vector4f minpt, maxpt;
00511     pcl::getMinMax3D<pcl::PointXYZRGB>(*segmented_cloud_transformed, minpt, maxpt);
00512 
00513     double xwidth = maxpt[0] - minpt[0];
00514     double ywidth = maxpt[1] - minpt[1];
00515     double zwidth = maxpt[2] - minpt[2];
00516     if (!pcl_isfinite(xwidth) || !pcl_isfinite(ywidth) || !pcl_isfinite(zwidth))
00517     {
00518       // all points in cloud are nan or its size is 0
00519       xwidth = ywidth = zwidth = 0;
00520     }
00521     
00522     Eigen::Vector4f center2((maxpt[0] + minpt[0]) / 2.0, (maxpt[1] + minpt[1]) / 2.0, (maxpt[2] + minpt[2]) / 2.0, 1.0);
00523     Eigen::Vector4f center_transformed = m4 * center2;
00524       
00525     // set centroid pose msg
00526     if (is_center_valid) {
00527       center_pose_msg.position.x = center[0];
00528       center_pose_msg.position.y = center[1];
00529       center_pose_msg.position.z = center[2];
00530       center_pose_msg.orientation.x = 0;
00531       center_pose_msg.orientation.y = 0;
00532       center_pose_msg.orientation.z = 0;
00533       center_pose_msg.orientation.w = 1;
00534     }
00535     else {
00536       // set invalid pose for invalid center
00537       center_pose_msg = geometry_msgs::Pose();
00538     }
00539     
00540     // set bounding_box msg
00541     bounding_box.pose.position.x = center_transformed[0];
00542     bounding_box.pose.position.y = center_transformed[1];
00543     bounding_box.pose.position.z = center_transformed[2];
00544     bounding_box.pose.orientation.x = q.x();
00545     bounding_box.pose.orientation.y = q.y();
00546     bounding_box.pose.orientation.z = q.z();
00547     bounding_box.pose.orientation.w = q.w();
00548     bounding_box.dimensions.x = xwidth;
00549     bounding_box.dimensions.y = ywidth;
00550     bounding_box.dimensions.z = zwidth;
00551     return true;
00552   }
00553 
00554   void ClusterPointIndicesDecomposer::addToDebugPointCloud
00555   (const pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud,
00556    size_t i,
00557    pcl::PointCloud<pcl::PointXYZRGB>& debug_output)
00558   {
00559     uint32_t rgb = colorRGBAToUInt32(jsk_topic_tools::colorCategory20(i));
00560     for (size_t j = 0; j < segmented_cloud->points.size(); j++) {
00561       pcl::PointXYZRGB p;
00562       p.x= segmented_cloud->points[j].x;
00563       p.y= segmented_cloud->points[j].y;
00564       p.z= segmented_cloud->points[j].z;
00565       p.rgb = *reinterpret_cast<float*>(&rgb);
00566       debug_output.points.push_back(p);
00567     }
00568   }
00569 
00570   void ClusterPointIndicesDecomposer::publishNegativeIndices(
00571     const sensor_msgs::PointCloud2ConstPtr &input,
00572     const jsk_recognition_msgs::ClusterPointIndicesConstPtr &indices_input)
00573   {
00574     if (negative_indices_pub_.getNumSubscribers() <= 0) {
00575       return;
00576     }
00577     std::vector<int> all_indices;
00578 
00579     boost::copy(
00580       boost::irange(0, (int)(input->width * input->height)),
00581       std::inserter(all_indices, all_indices.begin()));
00582 
00583     for (size_t i = 0; i < indices_input->cluster_indices.size(); i++) {
00584       for (size_t j = 0; j < indices_input->cluster_indices[i].indices.size(); ++j) {
00585         all_indices[indices_input->cluster_indices[i].indices[j]] = -1;
00586       }
00587     }
00588     all_indices.erase(std::remove(all_indices.begin(), all_indices.end(), -1), all_indices.end());
00589 
00590     // publish all_indices
00591     pcl_msgs::PointIndices ros_indices;
00592     ros_indices.indices = std::vector<int>(all_indices.begin(), all_indices.end());
00593     ros_indices.header = input->header;
00594     negative_indices_pub_.publish(ros_indices);
00595   }
00596     
00597   void ClusterPointIndicesDecomposer::extract(
00598     const sensor_msgs::PointCloud2ConstPtr &input,
00599     const jsk_recognition_msgs::ClusterPointIndicesConstPtr &indices_input,
00600     const jsk_recognition_msgs::PolygonArrayConstPtr& planes,
00601     const jsk_recognition_msgs::ModelCoefficientsArrayConstPtr& coefficients)
00602   {
00603     vital_checker_->poke();
00604     if (publish_clouds_) {
00605       allocatePublishers(indices_input->cluster_indices.size());
00606     }
00607     publishNegativeIndices(input, indices_input);
00608     pcl::ExtractIndices<pcl::PointXYZRGB> extract;
00609     pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
00610     pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz (new pcl::PointCloud<pcl::PointXYZ>);
00611     pcl::fromROSMsg(*input, *cloud);
00612     pcl::fromROSMsg(*input, *cloud_xyz);
00613     cluster_counter_.add(indices_input->cluster_indices.size());
00614     
00615     std::vector<pcl::IndicesPtr> converted_indices;
00616     
00617     for (size_t i = 0; i < indices_input->cluster_indices.size(); i++)
00618     {
00619       pcl::IndicesPtr vindices;
00620       // skip indices with points size
00621       if (min_size_ > 0 &&
00622           indices_input->cluster_indices[i].indices.size() < min_size_) {
00623         vindices.reset (new std::vector<int> ());
00624         converted_indices.push_back(vindices);
00625         continue;
00626       }
00627       if (max_size_ > 0 &&
00628           indices_input->cluster_indices[i].indices.size() > max_size_) {
00629         vindices.reset (new std::vector<int> ());
00630         converted_indices.push_back(vindices);
00631         continue;
00632       }
00633       vindices.reset (new std::vector<int> (indices_input->cluster_indices[i].indices));
00634       converted_indices.push_back(vindices);
00635     }
00636 
00637     std::vector<size_t> argsort;
00638     sortIndicesOrder(cloud_xyz, converted_indices, &argsort);
00639     extract.setInputCloud(cloud);
00640 
00641     // point cloud from camera not laser
00642     bool is_sensed_with_camera = (input->height != 1);
00643 
00644     cv::Mat mask = cv::Mat::zeros(input->height, input->width, CV_8UC1);
00645     cv::Mat label = cv::Mat::zeros(input->height, input->width, CV_32SC1);
00646     pcl::PointCloud<pcl::PointXYZRGB> debug_output;
00647     jsk_recognition_msgs::BoundingBoxArray bounding_box_array;
00648     jsk_recognition_msgs::ClusterPointIndices out_cluster_indices;
00649     bounding_box_array.header = input->header;
00650     geometry_msgs::PoseArray center_pose_array;
00651     center_pose_array.header = input->header;
00652     out_cluster_indices.header = input->header;
00653     for (size_t i = 0; i < argsort.size(); i++)
00654     {
00655       pcl::PointCloud<pcl::PointXYZRGB>::Ptr segmented_cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
00656       segmented_cloud->is_dense = cloud->is_dense;
00657       
00658       pcl_msgs::PointIndices out_indices_msg;
00659       out_indices_msg.header = input->header;
00660       out_indices_msg.indices = *(converted_indices[argsort[i]]);
00661       out_cluster_indices.cluster_indices.push_back(out_indices_msg);
00662 
00663       pcl::PointIndices::Ptr segmented_indices (new pcl::PointIndices);
00664       extract.setIndices(converted_indices[argsort[i]]);
00665       extract.filter(*segmented_cloud);
00666       if (publish_clouds_) {
00667         sensor_msgs::PointCloud2::Ptr out_cloud(new sensor_msgs::PointCloud2);
00668         pcl::toROSMsg(*segmented_cloud, *out_cloud);
00669         out_cloud->header = input->header;
00670         publishers_[i].publish(out_cloud);
00671       }
00672       // label
00673       if (is_sensed_with_camera) {
00674         // create mask & label image from cluster indices
00675         for (size_t j = 0; j < converted_indices[argsort[i]]->size(); j++) {
00676           int index = converted_indices[argsort[i]]->data()[j];
00677           int width_index = index % input->width;
00678           int height_index = index / input->width;
00679           mask.at<uchar>(height_index, width_index) = 255;
00680           // 0 should be skipped,
00681           // because it is to label image as the black region is to mask image
00682           label.at<int>(height_index, width_index) = (int)i + 1;
00683         }
00684       }
00685       // adding the pointcloud into debug_output
00686       addToDebugPointCloud(segmented_cloud, i, debug_output);
00687 
00688       // compute centoid and bounding box
00689       geometry_msgs::Pose pose_msg;
00690       jsk_recognition_msgs::BoundingBox bounding_box;
00691       bounding_box.label = static_cast<int>(argsort[i]);
00692 
00693       if (!segmented_cloud->is_dense) {
00694         std::vector<int> nan_indices;
00695         pcl::removeNaNFromPointCloud(*segmented_cloud, *segmented_cloud, nan_indices);
00696       }
00697 
00698       bool successp = computeCenterAndBoundingBox(
00699         segmented_cloud, input->header, planes, coefficients, pose_msg, bounding_box);
00700       if (!successp) {
00701         return;
00702       }
00703       std::string target_frame;
00704       if (align_boxes_ && !align_boxes_with_plane_) {
00705         target_frame = target_frame_id_;
00706       }
00707       else {
00708         target_frame = input->header.frame_id;
00709       }
00710       center_pose_array.poses.push_back(pose_msg);
00711       bounding_box.header.frame_id = target_frame;
00712       bounding_box_array.boxes.push_back(bounding_box);
00713       if (publish_tf_) {
00714         tf::Transform transform;
00715         transform.setOrigin(tf::Vector3(pose_msg.position.x, pose_msg.position.y, pose_msg.position.z));
00716         transform.setRotation(tf::createIdentityQuaternion());
00717         br_->sendTransform(tf::StampedTransform(transform, input->header.stamp, target_frame,
00718                                                 tf_prefix_ + (boost::format("output%02u") % (i)).str()));
00719       }
00720     } // for each indices
00721 
00722     // Both bounding box and centroid are computed with transformed point cloud for the target frame.
00723     if (align_boxes_ && !align_boxes_with_plane_) {
00724       bounding_box_array.header.frame_id = target_frame_id_;
00725       center_pose_array.header.frame_id = target_frame_id_;
00726     }
00727 
00728     if (is_sensed_with_camera) {
00729       // publish mask
00730       cv_bridge::CvImage mask_bridge(indices_input->header,
00731                                     sensor_msgs::image_encodings::MONO8,
00732                                     mask);
00733       mask_pub_.publish(mask_bridge.toImageMsg());
00734       // publish label
00735       cv_bridge::CvImage label_bridge(indices_input->header,
00736                                       sensor_msgs::image_encodings::TYPE_32SC1,
00737                                       label);
00738       label_pub_.publish(label_bridge.toImageMsg());
00739     }
00740     
00741     sensor_msgs::PointCloud2 debug_ros_output;
00742     pcl::toROSMsg(debug_output, debug_ros_output);
00743     debug_ros_output.header = input->header;
00744     debug_ros_output.is_dense = false;
00745     pc_pub_.publish(debug_ros_output);
00746     centers_pub_.publish(center_pose_array);
00747     box_pub_.publish(bounding_box_array);
00748     indices_pub_.publish(out_cluster_indices);
00749   }
00750   
00751   void ClusterPointIndicesDecomposer::extract
00752   (const sensor_msgs::PointCloud2ConstPtr &input,
00753    const jsk_recognition_msgs::ClusterPointIndicesConstPtr &indices_input)
00754   {
00755     extract(input, indices_input,
00756             jsk_recognition_msgs::PolygonArrayConstPtr(),
00757             jsk_recognition_msgs::ModelCoefficientsArrayConstPtr());
00758   }
00759 
00760   void ClusterPointIndicesDecomposer::allocatePublishers(size_t num)
00761   {
00762     if (num > publishers_.size())
00763     {
00764         for (size_t i = publishers_.size(); i < num; i++)
00765         {
00766             std::string topic_name = (boost::format("output%02u") % (i)).str();
00767             NODELET_INFO("advertising %s", topic_name.c_str());
00768             ros::Publisher publisher = pnh_->advertise<sensor_msgs::PointCloud2>(topic_name, 1);
00769             publishers_.push_back(publisher);
00770         }
00771     }
00772   }
00773 
00774 }  // namespace jsk_pcl_ros
00775 
00776 PLUGINLIB_EXPORT_CLASS(jsk_pcl_ros::ClusterPointIndicesDecomposer, nodelet::Nodelet);
00777 // TODO: deprecate below export. because sorting by z_axis is achieved in parent class with sort_by_ = "z_axis"
00778 PLUGINLIB_EXPORT_CLASS(jsk_pcl_ros::ClusterPointIndicesDecomposerZAxis, nodelet::Nodelet);


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