00001 #include "surface_perception/shape_extraction.h"
00002
00003 #include <limits.h>
00004
00005 #include "Eigen/Eigen"
00006 #include "geometry_msgs/Pose.h"
00007 #include "geometry_msgs/Vector3.h"
00008 #include "pcl/ModelCoefficients.h"
00009 #include "pcl/filters/project_inliers.h"
00010 #include "pcl/point_cloud.h"
00011 #include "pcl/point_types.h"
00012 #include "pcl/surface/convex_hull.h"
00013 #include "ros/ros.h"
00014
00015 #include "pcl/features/normal_3d.h"
00016 #include "surface_perception/surface.h"
00017 #include "surface_perception/typedefs.h"
00018
00019 namespace {
00024 bool hasHullOnXYPlane(const pcl::ModelCoefficients::Ptr& model,
00025 const PointCloudC::Ptr& flat_projected) {
00026 Eigen::Vector3d x_axis_(1.0, 0.0, 0.0);
00027 Eigen::Vector3d y_axis_(0.0, 1.0, 0.0);
00028 Eigen::Vector3d z_axis_(0.0, 0.0, 1.0);
00029
00030 std::vector<int>* indices_ = new std::vector<int>();
00031 for (size_t i = 0; i < flat_projected->points.size(); i++) {
00032 indices_->push_back(i);
00033 }
00034 double projection_angle_thresh_ = cos(0.174532925);
00035 int dimension = 2;
00036 bool xy_proj_safe = true;
00037 bool yz_proj_safe = true;
00038 bool xz_proj_safe = true;
00039
00040
00041 PointC p0 = flat_projected->points[(*indices_)[0]];
00042 PointC p1 = flat_projected->points[(*indices_)[indices_->size() - 1]];
00043 PointC p2 = flat_projected->points[(*indices_)[indices_->size() / 2]];
00044
00045 Eigen::Array4f dy1dy2 = (p1.getArray4fMap() - p0.getArray4fMap()) /
00046 (p2.getArray4fMap() - p0.getArray4fMap());
00047 while (!((dy1dy2[0] != dy1dy2[1]) || (dy1dy2[2] != dy1dy2[1]))) {
00048 p0 = flat_projected->points[(*indices_)[rand() % indices_->size()]];
00049 p1 = flat_projected->points[(*indices_)[rand() % indices_->size()]];
00050 p2 = flat_projected->points[(*indices_)[rand() % indices_->size()]];
00051 dy1dy2 = (p1.getArray4fMap() - p0.getArray4fMap()) /
00052 (p2.getArray4fMap() - p0.getArray4fMap());
00053 }
00054
00055 pcl::PointCloud<PointC> normal_calc_cloud;
00056 normal_calc_cloud.points.resize(3);
00057 normal_calc_cloud.points[0] = p0;
00058 normal_calc_cloud.points[1] = p1;
00059 normal_calc_cloud.points[2] = p2;
00060
00061 Eigen::Vector4d normal_calc_centroid;
00062 Eigen::Matrix3d normal_calc_covariance;
00063 pcl::computeMeanAndCovarianceMatrix(normal_calc_cloud, normal_calc_covariance,
00064 normal_calc_centroid);
00065
00066 Eigen::Vector3d::Scalar eigen_value;
00067 Eigen::Vector3d plane_params;
00068 pcl::eigen33(normal_calc_covariance, eigen_value, plane_params);
00069 float theta_x = fabsf(static_cast<float>(plane_params.dot(x_axis_)));
00070 float theta_y = fabsf(static_cast<float>(plane_params.dot(y_axis_)));
00071 float theta_z = fabsf(static_cast<float>(plane_params.dot(z_axis_)));
00072
00073 std::vector<PointC> points;
00074 points.push_back(p0);
00075 points.push_back(p1);
00076 points.push_back(p2);
00077
00078
00079
00080 if (theta_z > projection_angle_thresh_) {
00081 xz_proj_safe = false;
00082 yz_proj_safe = false;
00083 }
00084 if (theta_x > projection_angle_thresh_) {
00085 xz_proj_safe = false;
00086 xy_proj_safe = false;
00087 }
00088 if (theta_y > projection_angle_thresh_) {
00089 xy_proj_safe = false;
00090 yz_proj_safe = false;
00091 }
00092
00093 if (!xy_proj_safe) {
00094 ROS_WARN(
00095 "Warning: could not use the following three points to calculate convex "
00096 "hull!");
00097 ROS_WARN("P0 (%f, %f, %f)", p0.x, p0.y, p0.z);
00098 ROS_WARN("P1 (%f, %f, %f)", p1.x, p1.y, p1.z);
00099 ROS_WARN("P2 (%f, %f, %f)", p2.x, p2.y, p2.z);
00100
00101 return false;
00102 }
00103
00104 return true;
00105 }
00106 }
00107
00108 namespace surface_perception {
00109 bool FitBox(const PointCloudC::Ptr& input,
00110 const pcl::PointIndices::Ptr& indices,
00111 const pcl::ModelCoefficients::Ptr& model, geometry_msgs::Pose* pose,
00112 geometry_msgs::Vector3* dimensions) {
00113
00114 double min_volume = std::numeric_limits<double>::max();
00115 Eigen::Matrix3f transformation;
00116
00117
00118 double height = 0.0;
00119 for (size_t i = 0; i < indices->indices.size(); ++i) {
00120 int index = indices->indices[i];
00121 const PointC& pt = input->at(index);
00122 const Eigen::Vector4f pp(pt.x, pt.y, pt.z, 1);
00123 Eigen::Vector4f m(model->values[0], model->values[1], model->values[2],
00124 model->values[3]);
00125 double distance_to_plane = fabs(pp.dot(m));
00126 if (distance_to_plane > height) {
00127 height = distance_to_plane;
00128 }
00129 }
00130
00131
00132 pcl::PointCloud<pcl::PointXYZRGB>::Ptr flat(
00133 new pcl::PointCloud<pcl::PointXYZRGB>);
00134 pcl::ProjectInliers<pcl::PointXYZRGB> projection;
00135 projection.setModelType(pcl::SACMODEL_PLANE);
00136 projection.setInputCloud(input);
00137 if (indices && indices->indices.size() > 0) {
00138 projection.setIndices(indices);
00139 }
00140 projection.setModelCoefficients(model);
00141 projection.filter(*flat);
00142
00143
00144 pcl::PointCloud<pcl::PointXYZRGB>::Ptr flat_projected(
00145 new pcl::PointCloud<pcl::PointXYZRGB>);
00146 Eigen::Vector3f normal(model->values[0], model->values[1], model->values[2]);
00147 Eigen::Quaternionf qz;
00148 qz.setFromTwoVectors(normal, Eigen::Vector3f::UnitZ());
00149 Eigen::Matrix3f plane_rotation = qz.toRotationMatrix();
00150 Eigen::Matrix3f inv_plane_rotation = plane_rotation.inverse();
00151
00152 for (size_t i = 0; i < flat->size(); ++i) {
00153 pcl::PointXYZRGB p;
00154 p.getVector3fMap() = plane_rotation * (*flat)[i].getVector3fMap();
00155 flat_projected->push_back(p);
00156 }
00157
00158
00159 pcl::PointCloud<pcl::PointXYZRGB> hull;
00160 pcl::ConvexHull<pcl::PointXYZRGB> convex_hull;
00161
00162
00163 if (hasHullOnXYPlane(model, flat_projected)) {
00164 convex_hull.setInputCloud(flat_projected);
00165 convex_hull.setDimension(2);
00166 convex_hull.reconstruct(hull);
00167 } else {
00168 return false;
00169 }
00170
00171
00172 double best_x_dim = 0.0;
00173 double best_y_dim = 0.0;
00174
00175
00176 for (size_t i = 0; i + 1 < hull.size(); ++i) {
00177
00178 double rise = hull[i + 1].y - hull[i].y;
00179 double run = hull[i + 1].x - hull[i].x;
00180
00181 {
00182 double l = sqrt((rise * rise) + (run * run));
00183 rise = rise / l;
00184 run = run / l;
00185 }
00186
00187
00188 Eigen::Matrix3f rotation;
00189 rotation(0, 0) = run;
00190 rotation(0, 1) = rise;
00191 rotation(0, 2) = 0.0;
00192 rotation(1, 0) = -rise;
00193 rotation(1, 1) = run;
00194 rotation(1, 2) = 0.0;
00195 rotation(2, 0) = 0.0;
00196 rotation(2, 1) = 0.0;
00197 rotation(2, 2) = 1.0;
00198 Eigen::Matrix3f inv_rotation = rotation.inverse();
00199
00200
00201 pcl::PointCloud<pcl::PointXYZRGB> projected_cloud;
00202 for (size_t j = 0; j < hull.size(); ++j) {
00203 pcl::PointXYZRGB p;
00204 p.getVector3fMap() = rotation * hull[j].getVector3fMap();
00205 projected_cloud.push_back(p);
00206 }
00207
00208
00209 double x_min = std::numeric_limits<double>::max();
00210 double x_max = -std::numeric_limits<double>::max();
00211 double y_min = std::numeric_limits<double>::max();
00212 double y_max = -std::numeric_limits<double>::max();
00213 for (size_t j = 0; j < projected_cloud.size(); ++j) {
00214 if (projected_cloud[j].x < x_min) x_min = projected_cloud[j].x;
00215 if (projected_cloud[j].x > x_max) x_max = projected_cloud[j].x;
00216
00217 if (projected_cloud[j].y < y_min) y_min = projected_cloud[j].y;
00218 if (projected_cloud[j].y > y_max) y_max = projected_cloud[j].y;
00219 }
00220
00221
00222 double area = (x_max - x_min) * (y_max - y_min);
00223
00224 if (area * height < min_volume) {
00225 transformation = inv_plane_rotation * inv_rotation;
00226
00227 Eigen::Vector3f pose3f((x_max + x_min) / 2.0, (y_max + y_min) / 2.0,
00228 projected_cloud[0].z + height / 2.0);
00229 pose3f = transformation * pose3f;
00230 pose->position.x = pose3f(0);
00231 pose->position.y = pose3f(1);
00232 pose->position.z = pose3f(2);
00233
00234 best_x_dim = x_max - x_min;
00235 best_y_dim = y_max - y_min;
00236
00237 min_volume = area * height;
00238 }
00239 }
00240
00241 Eigen::Matrix3f adjusted_transformation =
00242 StandardizeBoxOrientation(transformation, best_x_dim, best_y_dim,
00243 &(dimensions->x), &(dimensions->y));
00244
00245 dimensions->z = height;
00246
00247 Eigen::Quaternionf q(adjusted_transformation);
00248 pose->orientation.x = q.x();
00249 pose->orientation.y = q.y();
00250 pose->orientation.z = q.z();
00251 pose->orientation.w = q.w();
00252
00253 return true;
00254 }
00255
00256 bool FitBoxOnSurface(const pcl::PointCloud<pcl::PointXYZRGB>::Ptr& input,
00257 const pcl::PointIndicesPtr& indices,
00258 const Surface& surface, geometry_msgs::Pose* pose,
00259 geometry_msgs::Vector3* dimensions) {
00260 bool success = FitBox(input, indices, surface.coefficients, pose, dimensions);
00261 if (!success) {
00262 return false;
00263 }
00264
00265
00266
00267 dimensions->z -= surface.dimensions.z / 2;
00268
00269 Eigen::Quaternionf q;
00270 q.w() = pose->orientation.w;
00271 q.x() = pose->orientation.x;
00272 q.y() = pose->orientation.y;
00273 q.z() = pose->orientation.z;
00274 Eigen::Matrix3f mat(q);
00275 Eigen::Vector3f up = mat.col(2) * surface.dimensions.z / 4;
00276 pose->position.x += up.x();
00277 pose->position.y += up.y();
00278 pose->position.z += up.z();
00279
00280 return true;
00281 }
00282
00283 Eigen::Matrix3f StandardizeBoxOrientation(
00284 const Eigen::Matrix3f& rotation_matrix, double x_dim, double y_dim,
00285 double* updated_x_dim, double* updated_y_dim) {
00286
00287 Eigen::Matrix3f output_matrix;
00288
00289
00290 if (x_dim > y_dim) {
00291 Eigen::Vector3f y_axis = rotation_matrix.col(1);
00292
00293
00294 if (y_axis.x() < 0) {
00295 y_axis = -1 * rotation_matrix.col(1);
00296 }
00297 output_matrix.col(0) = y_axis;
00298 output_matrix.col(1) = rotation_matrix.col(2).cross(y_axis);
00299 } else {
00300 output_matrix.col(0) = rotation_matrix.col(0);
00301 output_matrix.col(1) = rotation_matrix.col(1);
00302 }
00303 output_matrix.col(2) = rotation_matrix.col(2);
00304
00305
00306 if (x_dim > y_dim) {
00307 *updated_x_dim = y_dim;
00308 *updated_y_dim = x_dim;
00309 } else {
00310 *updated_x_dim = x_dim;
00311 *updated_y_dim = y_dim;
00312 }
00313
00314
00315
00316
00317
00318 Eigen::Vector3f x_axis(1.0, 0.0, 0.0);
00319 if (output_matrix.col(0).dot(x_axis) < 0.0) {
00320 output_matrix.col(0) = output_matrix.col(0) * -1.0;
00321
00322 output_matrix.col(1) = output_matrix.col(2).cross(output_matrix.col(0));
00323 }
00324
00325 return output_matrix;
00326 }
00327 }