Point Cloud Library (PCL)  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
integral_image_normal.hpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2011, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Willow Garage, Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  */
00035 
00036 #ifndef PCL_FEATURES_INTEGRALIMAGE_BASED_IMPL_NORMAL_ESTIMATOR_H_
00037 #define PCL_FEATURES_INTEGRALIMAGE_BASED_IMPL_NORMAL_ESTIMATOR_H_
00038 
00039 #include "pcl/features/integral_image_normal.h"
00040 
00042 template <typename PointInT, typename PointOutT>
00043 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::~IntegralImageNormalEstimation ()
00044 {
00045   if (diff_x_ != NULL) delete diff_x_;
00046   if (diff_y_ != NULL) delete diff_y_;
00047   if (depth_data_ != NULL) delete depth_data_;
00048 }
00049 
00051 template <typename PointInT, typename PointOutT> void
00052 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::initData ()
00053 {
00054   // compute derivatives
00055   if (diff_x_ != NULL) delete diff_x_;
00056   if (diff_y_ != NULL) delete diff_y_;
00057   if (depth_data_ != NULL) delete depth_data_;
00058 
00059   if (normal_estimation_method_ == COVARIANCE_MATRIX)
00060     initCovarianceMatrixMethod ();
00061   else if (normal_estimation_method_ == AVERAGE_3D_GRADIENT)
00062     initAverage3DGradientMethod ();
00063   else if (normal_estimation_method_ == AVERAGE_DEPTH_CHANGE)
00064     initAverageDepthChangeMethod ();
00065 }
00066 
00067 
00069 template <typename PointInT, typename PointOutT> void
00070 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::setRectSize (const int width, const int height)
00071 {
00072   rect_width_ = width;
00073   rect_height_ = height;
00074 }
00075 
00077 template <typename PointInT, typename PointOutT> void
00078 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::initCovarianceMatrixMethod ()
00079 {
00080   // number of DataType entries per element (equal or bigger than dimensions)
00081   int element_stride = sizeof (input_->points[0]) / sizeof (float);
00082   // number of DataType entries per row (equal or bigger than element_stride number of elements per row)
00083   int row_stride     = element_stride * input_->width;
00084 
00085   float *data_ = reinterpret_cast<float*>((PointInT*)(&(input_->points[0])));
00086 
00087   integral_image_XYZ_.setInput (data_, input_->width, input_->height, element_stride, row_stride);
00088 
00089   init_covariance_matrix_ = true;
00090   init_average_3d_gradient_ = init_depth_change_ = false;
00091 }
00092 
00094 template <typename PointInT, typename PointOutT> void
00095 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::initAverage3DGradientMethod ()
00096 {
00097   float *data_ = reinterpret_cast<float*>((PointInT*)(&(input_->points[0])));
00098   size_t nr_points = 4 * input_->points.size ();
00099   diff_x_ = new float[nr_points];
00100   diff_y_ = new float[nr_points];
00101 
00102   // number of DataType entries per element (equal or bigger than dimensions)
00103   int element_stride = sizeof (input_->points[0]) / sizeof (float);
00104   // number of DataType entries per row (equal or bigger than element_stride number of elements per row)
00105   int row_stride     = element_stride * input_->width;
00106 
00107   memset (diff_x_, 0, sizeof(float) * nr_points);
00108   memset (diff_y_, 0, sizeof(float) * nr_points);
00109 
00110   for (size_t ri = 1; ri < input_->height - 1; ++ri)
00111   {
00112     float *data_pointer_y_up    = data_ + (ri-1)*row_stride + element_stride;
00113     float *data_pointer_y_down  = data_ + (ri+1)*row_stride + element_stride;
00114     float *data_pointer_x_left  = data_ + ri*row_stride;
00115     float *data_pointer_x_right = data_ + ri*row_stride + 2*element_stride;
00116 
00117     float * diff_x_pointer = diff_x_ + ri * 4 * input_->width + 4;
00118     float * diff_y_pointer = diff_y_ + ri * 4 * input_->width + 4;
00119 
00120     for (size_t ci = 1; ci < input_->width - 1; ++ci)
00121     {
00122       diff_x_pointer[0] = data_pointer_x_right[0] - data_pointer_x_left[0];
00123       diff_x_pointer[1] = data_pointer_x_right[1] - data_pointer_x_left[1];
00124       diff_x_pointer[2] = data_pointer_x_right[2] - data_pointer_x_left[2];
00125 
00126       diff_y_pointer[0] = data_pointer_y_down[0] - data_pointer_y_up[0];
00127       diff_y_pointer[1] = data_pointer_y_down[1] - data_pointer_y_up[1];
00128       diff_y_pointer[2] = data_pointer_y_down[2] - data_pointer_y_up[2];
00129 
00130       diff_x_pointer += 4;
00131       diff_y_pointer += 4;
00132 
00133       data_pointer_y_up    += element_stride;
00134       data_pointer_y_down  += element_stride;
00135       data_pointer_x_left  += element_stride;
00136       data_pointer_x_right += element_stride;
00137     }
00138   }
00139 
00140   // Compute integral images
00141   integral_image_X_.setInput (diff_x_, input_->width, input_->height, 4, 4 * input_->width);
00142   integral_image_Y_.setInput (diff_y_, input_->width, input_->height, 4, 4 * input_->width);
00143   init_covariance_matrix_ = init_depth_change_ = false;
00144   init_average_3d_gradient_ = true;
00145 }
00146 
00148 template <typename PointInT, typename PointOutT> void
00149 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::initAverageDepthChangeMethod ()
00150 {
00151   // number of DataType entries per element (equal or bigger than dimensions)
00152   int element_stride = sizeof (input_->points[0]) / sizeof (float);
00153   // number of DataType entries per row (equal or bigger than element_stride number of elements per row)
00154   int row_stride     = element_stride * input_->width;
00155 
00156   float *data_ = reinterpret_cast<float*>((PointInT*)(&(input_->points[0])));
00157   // compute integral image
00158   //integral_image_ = new pcl::IntegralImage2D<float, double>(
00159   //  &(data_[2]), input_->width, input_->height, 1, false, element_stride, row_stride);
00160 
00161   integral_image_depth_.setInput (&(data_[2]), input_->width, input_->height, element_stride, row_stride);
00162   init_depth_change_ = true;
00163   init_covariance_matrix_ = init_average_3d_gradient_ = false;
00164 }
00165 
00167 template <typename PointInT, typename PointOutT> void
00168 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::computePointNormal (
00169     const int pos_x, const int pos_y, PointOutT &normal)
00170 {
00171   float bad_point = std::numeric_limits<float>::quiet_NaN ();
00172 
00173   if (normal_estimation_method_ == COVARIANCE_MATRIX)
00174   {
00175     if (!init_covariance_matrix_)
00176       initCovarianceMatrixMethod ();
00177 
00178     EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
00179     Eigen::Vector3f center;
00180     typename IntegralImage2D<float, 3>::SecondOrderType so_elements;
00181 
00182     center = integral_image_XYZ_.getFirstOrderSum(pos_x - (rect_width_ >> 1), pos_y - (rect_height_ >> 1), rect_width_, rect_height_).cast<float> ();
00183     so_elements = integral_image_XYZ_.getSecondOrderSum(pos_x - (rect_width_ >> 1), pos_y - (rect_height_ >> 1), rect_width_, rect_height_);
00184     covariance_matrix.coeffRef (0) = so_elements [0];
00185     covariance_matrix.coeffRef (1) = covariance_matrix.coeffRef (3) = so_elements [1];
00186     covariance_matrix.coeffRef (2) = covariance_matrix.coeffRef (6) = so_elements [2];
00187     covariance_matrix.coeffRef (4) = so_elements [3];
00188     covariance_matrix.coeffRef (5) = covariance_matrix.coeffRef (7) = so_elements [4];
00189     covariance_matrix.coeffRef (8) = so_elements [5];
00190     covariance_matrix -= (center * center.transpose ()) / (rect_width_ * rect_height_);
00191     float eigen_value;
00192     Eigen::Vector3f eigen_vector;
00193     pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector);
00194     if (eigen_vector [2] < 0.0f)
00195       normal.getNormalVector4fMap () = Eigen::Vector4f (eigen_vector [0], eigen_vector [1], eigen_vector [2], 0);
00196     else
00197       normal.getNormalVector4fMap () = Eigen::Vector4f (-eigen_vector [0], -eigen_vector [1], -eigen_vector [2], 0);
00198 
00199     // Compute the curvature surface change
00200     if (eigen_value > 0.0)
00201       normal.curvature = fabs ( eigen_value / (covariance_matrix.coeff (0) + covariance_matrix.coeff (4) + covariance_matrix.coeff (8)) );
00202     else
00203       normal.curvature = 0;
00204 
00205     return;
00206   }
00207   else if (normal_estimation_method_ == AVERAGE_3D_GRADIENT)
00208   {
00209     if (!init_average_3d_gradient_)
00210       initAverage3DGradientMethod ();
00211     const float mean_x_x = integral_image_X_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [0];
00212     const float mean_x_y = integral_image_X_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [1];
00213     const float mean_x_z = integral_image_X_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [2];
00214 
00215     const float mean_y_x = integral_image_Y_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [0];
00216     const float mean_y_y = integral_image_Y_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [1];
00217     const float mean_y_z = integral_image_Y_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-rect_height_/2, rect_width_, rect_height_) [2];
00218 
00219     const float normal_x = mean_x_y * mean_y_z - mean_x_z * mean_y_y;
00220     const float normal_y = mean_x_z * mean_y_x - mean_x_x * mean_y_z;
00221     const float normal_z = mean_x_x * mean_y_y - mean_x_y * mean_y_x;
00222 
00223 
00224     const float normal_length = sqrt (normal_x * normal_x + normal_y * normal_y + normal_z * normal_z);
00225 
00226     if (normal_length == 0.0f)
00227     {
00228       normal.getNormalVector4fMap ().setConstant (bad_point);
00229       normal.curvature = bad_point;
00230       return;
00231     }
00232 
00233     const float scale = -1.0f / normal_length;
00234 
00235     normal.normal_x = normal_x * scale;
00236     normal.normal_y = normal_y * scale;
00237     normal.normal_z = normal_z * scale;
00238     normal.curvature = bad_point;
00239     return;
00240   }
00241   else if (normal_estimation_method_ == AVERAGE_DEPTH_CHANGE)
00242   {
00243     if (!init_depth_change_)
00244       initAverageDepthChangeMethod ();
00245 
00246     PointInT pointL = input_->points[pos_y * input_->width + pos_x-rect_width_/2];
00247     PointInT pointR = input_->points[pos_y * input_->width + pos_x+rect_width_/2];
00248     PointInT pointU = input_->points[(pos_y-rect_height_/2) * input_->width+pos_x];
00249     PointInT pointD = input_->points[(pos_y+rect_height_/2) * input_->width+pos_x];
00250 
00251     const float mean_L_z = integral_image_depth_.getFirstOrderSum (pos_x-1-rect_width_/2, pos_y-rect_height_/2, rect_width_-1, rect_height_-1)/((rect_width_-1)*(rect_height_-1));
00252     const float mean_R_z = integral_image_depth_.getFirstOrderSum (pos_x+1-rect_width_/2, pos_y-rect_height_/2, rect_width_-1, rect_height_-1)/((rect_width_-1)*(rect_height_-1));
00253     const float mean_U_z = integral_image_depth_.getFirstOrderSum (pos_x-rect_width_/2, pos_y-1-rect_height_/2, rect_width_-1, rect_height_-1)/((rect_width_-1)*(rect_height_-1));
00254     const float mean_D_z = integral_image_depth_.getFirstOrderSum (pos_x-rect_width_/2, pos_y+1-rect_height_/2, rect_width_-1, rect_height_-1)/((rect_width_-1)*(rect_height_-1));
00255 
00256     const float mean_x_z = (mean_R_z - mean_L_z)/2.0f;
00257     const float mean_y_z = (mean_D_z - mean_U_z)/2.0f;
00258 
00259     const float mean_x_x = (pointR.x - pointL.x)/(rect_width_);
00260     const float mean_x_y = (pointR.y - pointL.y)/(rect_height_);
00261     const float mean_y_x = (pointD.x - pointU.x)/(rect_width_);
00262     const float mean_y_y = (pointD.y - pointU.y)/(rect_height_);
00263 
00264     const float normal_x = mean_x_y * mean_y_z - mean_x_z * mean_y_y;
00265     const float normal_y = mean_x_z * mean_y_x - mean_x_x * mean_y_z;
00266     const float normal_z = mean_x_x * mean_y_y - mean_x_y * mean_y_x;
00267 
00268     const float normal_length = sqrt (normal_x * normal_x + normal_y * normal_y + normal_z * normal_z);
00269 
00270     if (normal_length == 0.0f)
00271     {
00272       normal.getNormalVector4fMap ().setConstant (bad_point);
00273       normal.curvature = bad_point;
00274       return;
00275     }
00276 
00277     const float scale = -1.0f / normal_length;
00278 
00279     normal.normal_x = normal_x*scale;
00280     normal.normal_y = normal_y*scale;
00281     normal.normal_z = normal_z*scale;
00282     normal.curvature = bad_point;
00283 
00284     return;
00285   }
00286   normal.getNormalVector4fMap ().setConstant (bad_point);
00287   normal.curvature = bad_point;
00288   return;
00289 }
00290 
00292 template <typename PointInT, typename PointOutT> void
00293 pcl::IntegralImageNormalEstimation<PointInT, PointOutT>::computeFeature (PointCloudOut &output)
00294 {
00295   float bad_point = std::numeric_limits<float>::quiet_NaN ();
00296 
00297   // compute depth-change map
00298   unsigned char * depthChangeMap = new unsigned char[input_->points.size ()];
00299   memset (depthChangeMap, 255, input_->points.size ());
00300 
00301   for (unsigned int ri = 0; ri < input_->height-1; ++ri)
00302   {
00303     for (unsigned int ci = 0; ci < input_->width-1; ++ci)
00304     {
00305       const float depth  = (*input_)(ci,     ri    ).z;
00306       const float depthR = (*input_)(ci + 1, ri    ).z;
00307       const float depthD = (*input_)(ci,     ri + 1).z;
00308 
00309       const float depthDependendDepthChange = (max_depth_change_factor_ * (fabs(depth)+1.0f))/(500.0f*0.001f);
00310 
00311       if (abs (depth - depthR) > depthDependendDepthChange
00312         || !pcl_isfinite (depth) || !pcl_isfinite (depthR))
00313       {
00314         depthChangeMap[ri*input_->width + ci] = 0;
00315         depthChangeMap[ri*input_->width + ci+1] = 0;
00316       }
00317       if (abs (depth - depthD) > depthDependendDepthChange
00318         || !pcl_isfinite (depth) || !pcl_isfinite (depthD))
00319       {
00320         depthChangeMap[ri*input_->width + ci] = 0;
00321         depthChangeMap[(ri+1)*input_->width + ci] = 0;
00322       }
00323     }
00324   }
00325 
00326 
00327   // compute distance map
00328   float *distanceMap = new float[input_->points.size ()];
00329   for (size_t index = 0; index < input_->points.size (); ++index)
00330   {
00331     if (depthChangeMap[index] == 0)
00332       distanceMap[index] = 0.0f;
00333     else
00334       distanceMap[index] = 640.0f;
00335   }
00336 
00337   // first pass
00338   for (size_t ri = 1; ri < input_->height; ++ri)
00339   {
00340     for (size_t ci = 1; ci < input_->width; ++ci)
00341     {
00342       const float upLeft = distanceMap[(ri-1)*input_->width + ci-1] + 1.4f;
00343       const float up = distanceMap[(ri-1)*input_->width + ci] + 1.0f;
00344       const float upRight = distanceMap[(ri-1)*input_->width + ci+1] + 1.4f;
00345       const float left = distanceMap[ri*input_->width + ci-1] + 1.0f;
00346       const float center = distanceMap[ri*input_->width + ci];
00347 
00348       const float minValue = std::min (std::min (upLeft, up), std::min (left, upRight));
00349 
00350       if (minValue < center)
00351         distanceMap[ri * input_->width + ci] = minValue;
00352     }
00353   }
00354 
00355   // second pass
00356   for (int ri = input_->height-2; ri >= 0; --ri)
00357   {
00358     for (int ci = input_->width-2; ci >= 0; --ci)
00359     {
00360       const float lowerLeft = distanceMap[(ri+1)*input_->width + ci-1] + 1.4f;
00361       const float lower = distanceMap[(ri+1)*input_->width + ci] + 1.0f;
00362       const float lowerRight = distanceMap[(ri+1)*input_->width + ci+1] + 1.4f;
00363       const float right = distanceMap[ri*input_->width + ci+1] + 1.0f;
00364       const float center = distanceMap[ri*input_->width + ci];
00365 
00366       const float minValue = std::min (std::min (lowerLeft, lower), std::min (right, lowerRight));
00367 
00368       if (minValue < center)
00369         distanceMap[ri*input_->width + ci] = minValue;
00370     }
00371   }
00372 
00373   // Set all normals that we do not touch to NaN
00374   for (size_t ri = 0; ri < normal_smoothing_size_; ++ri)
00375   {
00376     for (size_t ci = 0; ci < input_->width; ++ci)
00377     {
00378       output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00379       output (ci, ri).curvature = bad_point;
00380     }
00381   }
00382   for (size_t ri = normal_smoothing_size_; ri < input_->height; ++ri)
00383   {
00384     for (size_t ci = 0; ci < normal_smoothing_size_; ++ci)
00385     {
00386       output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00387       output (ci, ri).curvature = bad_point;
00388     }
00389   }
00390 
00391   for (size_t ri = input_->height - normal_smoothing_size_; ri < input_->height; ++ri)
00392   {
00393     for (size_t ci = normal_smoothing_size_; ci < input_->width; ++ci)
00394     {
00395       output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00396       output (ci, ri).curvature = bad_point;
00397     }
00398   }
00399 
00400   for (size_t ri = normal_smoothing_size_; ri < input_->height - normal_smoothing_size_; ++ri)
00401   {
00402     for (size_t ci = input_->width - normal_smoothing_size_; ci < input_->width; ++ci)
00403     {
00404       output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00405       output (ci, ri).curvature = bad_point;
00406     }
00407   }
00408 
00409   if (use_depth_dependent_smoothing_)
00410   {
00411     for (int ri = normal_smoothing_size_; ri < input_->height-normal_smoothing_size_; ++ri)
00412     {
00413       for (int ci = normal_smoothing_size_; ci < input_->width-normal_smoothing_size_; ++ci)
00414       {
00415         const float depth = (*input_)(ci, ri).z;
00416         if (!pcl_isfinite (depth))
00417         {
00418           output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00419           output (ci, ri).curvature = bad_point;
00420           continue;
00421         }
00422 
00423         float smoothing = (std::min)(distanceMap[ri*input_->width + ci], normal_smoothing_size_ + static_cast<float>(depth)/10.0f);
00424 
00425         if (smoothing > 2.0f)
00426         {
00427           setRectSize (smoothing, smoothing);
00428           computePointNormal (ci, ri, output (ci, ri));
00429         }
00430         else
00431         {
00432           output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00433           output (ci, ri).curvature = bad_point;
00434         }
00435       }
00436     }
00437   }
00438   else
00439   {
00440     float smoothing_constant = normal_smoothing_size_ * static_cast<float>(1.0f) / (500.0f * 0.001f);
00441     for (int ri = normal_smoothing_size_; ri < input_->height-normal_smoothing_size_; ++ri)
00442     {
00443       for (int ci = normal_smoothing_size_; ci < input_->width-normal_smoothing_size_; ++ci)
00444       {
00445         if (!pcl_isfinite ((*input_) (ci, ri).z))
00446         {
00447           output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00448           output (ci, ri).curvature = bad_point;
00449           continue;
00450         }
00451 
00452         float smoothing = (std::min)(distanceMap[ri*input_->width + ci], smoothing_constant);
00453 
00454         if (smoothing > 2.0f)
00455         {
00456           setRectSize (smoothing, smoothing);
00457           computePointNormal (ci, ri, output (ci, ri));
00458         }
00459         else
00460         {
00461           output (ci, ri).getNormalVector4fMap ().setConstant (bad_point);
00462           output (ci, ri).curvature = bad_point;
00463         }
00464       }
00465     }
00466   }
00467 
00468   delete[] depthChangeMap;
00469   delete[] distanceMap;
00470 }
00471 
00472 #define PCL_INSTANTIATE_IntegralImageNormalEstimation(T,NT) template class PCL_EXPORTS pcl::IntegralImageNormalEstimation<T,NT>;
00473 
00474 #endif
00475 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines