00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
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     
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     
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]); 
00233     }
00234 
00235     
00236     
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     
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           
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             
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               
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         
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     
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         
00443         tf::StampedTransform tf_transform;
00444         try {
00445           tf_transform = jsk_recognition_utils::lookupTransformWithDuration(
00446             tf_listener_,
00447             header.frame_id,                
00448             target_frame_id_,  
00449             header.stamp,
00450             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         
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     
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       
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     
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       
00537       center_pose_msg = geometry_msgs::Pose();
00538     }
00539     
00540     
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     
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       
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     
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       
00673       if (is_sensed_with_camera) {
00674         
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           
00681           
00682           label.at<int>(height_index, width_index) = (int)i + 1;
00683         }
00684       }
00685       
00686       addToDebugPointCloud(segmented_cloud, i, debug_output);
00687 
00688       
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     } 
00721 
00722     
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       
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       
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 }  
00775 
00776 PLUGINLIB_EXPORT_CLASS(jsk_pcl_ros::ClusterPointIndicesDecomposer, nodelet::Nodelet);
00777 
00778 PLUGINLIB_EXPORT_CLASS(jsk_pcl_ros::ClusterPointIndicesDecomposerZAxis, nodelet::Nodelet);