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 <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
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
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
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
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
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
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
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
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