range_image.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Point Cloud Library (PCL) - www.pointclouds.org
00005  *  Copyright (c) 2010-2012, Willow Garage, Inc.
00006  *
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of the copyright holder(s) nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  */
00036 
00037 #include <cstddef>
00038 #include <iostream>
00039 #include <cmath>
00040 #include <set>
00041 #include <pcl/common/eigen.h>
00042 #include <pcl/range_image/range_image.h>
00043 #include <pcl/common/transformation_from_correspondences.h>
00044 
00045 namespace pcl 
00046 {
00047 
00048 bool RangeImage::debug = false;
00049 int RangeImage::max_no_of_threads = 1;
00050 const int RangeImage::lookup_table_size = 20001;
00051 std::vector<float> RangeImage::asin_lookup_table;
00052 std::vector<float> RangeImage::atan_lookup_table;
00053 std::vector<float> RangeImage::cos_lookup_table;
00054 
00056 void 
00057 RangeImage::createLookupTables ()
00058 {
00059   if (!asin_lookup_table.empty ())
00060     return;
00061   
00062   asin_lookup_table.resize (lookup_table_size);
00063   for (int i=0; i<lookup_table_size; ++i) {
00064     float value = static_cast<float> (i-(lookup_table_size-1)/2)/static_cast<float> ((lookup_table_size-1)/2);
00065     asin_lookup_table[i] = asinf (value);
00066   }
00067   
00068   atan_lookup_table.resize (lookup_table_size);
00069   for (int i=0; i<lookup_table_size; ++i) 
00070   {
00071     float value = static_cast<float> (i-(lookup_table_size-1)/2)/static_cast<float> ((lookup_table_size-1)/2);
00072     atan_lookup_table[i] = atanf (value);
00073   }
00074   
00075   cos_lookup_table.resize (lookup_table_size);
00076   
00077   for (int i = 0; i < lookup_table_size; ++i) 
00078   {
00079     float value = static_cast<float> (i) * 2.0f * static_cast<float> (M_PI) / static_cast<float> (lookup_table_size-1);
00080     cos_lookup_table[i] = cosf (value);
00081   }
00082 }
00083 
00084 
00085 
00087 void 
00088 RangeImage::getCoordinateFrameTransformation (RangeImage::CoordinateFrame coordinate_frame,
00089                                              Eigen::Affine3f& transformation)
00090 {
00091   switch (coordinate_frame)
00092   {
00093     case LASER_FRAME:
00094       transformation (0,0)= 0.0f; transformation (0,1)= 0.0f; transformation (0,2)=1.0f; transformation (0,3)=0.0f;
00095       transformation (1,0)=-1.0f; transformation (1,1)= 0.0f; transformation (1,2)=0.0f; transformation (1,3)=0.0f;
00096       transformation (2,0)= 0.0f; transformation (2,1)=-1.0f; transformation (2,2)=0.0f; transformation (2,3)=0.0f;
00097       transformation (3,0)= 0.0f; transformation (3,1)= 0.0f; transformation (3,2)=0.0f; transformation (3,3)=1.0f;
00098       break;
00099     case CAMERA_FRAME:
00100     default:
00101       transformation.setIdentity ();
00102       break;
00103   }
00104 }
00105 
00107 RangeImage::RangeImage () : 
00108   RangeImage::BaseClass (), 
00109   to_range_image_system_ (Eigen::Affine3f::Identity ()),
00110   to_world_system_ (Eigen::Affine3f::Identity ()),
00111   angular_resolution_x_ (0), angular_resolution_y_ (0),
00112   angular_resolution_x_reciprocal_ (0), angular_resolution_y_reciprocal_ (0),
00113   image_offset_x_ (0), image_offset_y_ (0),
00114   unobserved_point ()
00115 {
00116   createLookupTables ();
00117   reset ();
00118   unobserved_point.x = unobserved_point.y = unobserved_point.z = std::numeric_limits<float>::quiet_NaN ();
00119   unobserved_point.range = -std::numeric_limits<float>::infinity ();
00120 }
00121 
00123 RangeImage::~RangeImage ()
00124 {
00125 }
00126 
00128 void
00129 RangeImage::reset ()
00130 {
00131   is_dense = true;
00132   width = height = 0;
00133   points.clear ();
00134   to_range_image_system_.setIdentity ();
00135   to_world_system_.setIdentity ();
00136   setAngularResolution (deg2rad (0.5f));
00137   image_offset_x_ = image_offset_y_ = 0;
00138 }
00139 
00141 void
00142 RangeImage::createEmpty (float angular_resolution, const Eigen::Affine3f& sensor_pose,
00143                          RangeImage::CoordinateFrame coordinate_frame, float angle_width, float angle_height)
00144 {
00145   createEmpty (angular_resolution, angular_resolution, sensor_pose, coordinate_frame, angle_width, angle_height);
00146 }
00147 
00149 void
00150 RangeImage::createEmpty (float angular_resolution_x, float angular_resolution_y, const Eigen::Affine3f& sensor_pose,
00151                          RangeImage::CoordinateFrame coordinate_frame, float angle_width, float angle_height)
00152 {
00153   setAngularResolution(angular_resolution_x, angular_resolution_y);
00154   width  = static_cast<uint32_t> (pcl_lrint (floor (angle_width * angular_resolution_x_reciprocal_)));
00155   height = static_cast<uint32_t> (pcl_lrint (floor (angle_height * angular_resolution_y_reciprocal_)));
00156 
00157   int full_width  = static_cast<int> (pcl_lrint (floor (pcl::deg2rad (360.0f)*angular_resolution_x_reciprocal_))),
00158       full_height = static_cast<int> (pcl_lrint (floor (pcl::deg2rad (180.0f)*angular_resolution_y_reciprocal_)));
00159   image_offset_x_ = (full_width-width)/2;
00160   image_offset_y_ = (full_height-height)/2;
00161   is_dense = false;
00162   getCoordinateFrameTransformation (coordinate_frame, to_world_system_);
00163   to_world_system_ = sensor_pose * to_world_system_;
00164   to_range_image_system_ = to_world_system_.inverse (Eigen::Isometry);
00165   unsigned int size = width*height;
00166   points.clear ();
00167   points.resize (size, unobserved_point);
00168 }
00169 
00171 void 
00172 RangeImage::cropImage (int borderSize, int top, int right, int bottom, int left) {
00173   //MEASURE_FUNCTION_TIME;
00174   
00175   bool topIsDone=true, rightIsDone=true, bottomIsDone=true, leftIsDone=true;
00176   if (top < 0) {
00177     top=-1;
00178     topIsDone=false;
00179   }
00180   if (right < 0) 
00181   {
00182     right= static_cast<int> (width);
00183     rightIsDone=false;
00184   }
00185   if (bottom < 0) 
00186   {
00187     bottom= static_cast<int> (height);
00188     bottomIsDone=false;
00189   }
00190   if (left < 0) 
00191   {
00192     left = -1;
00193     leftIsDone = false;
00194   }
00195   
00196   // Find top border
00197   while (!topIsDone && top<=bottom) 
00198   {
00199     ++top;
00200     int lineStart = top*width;
00201     int min_x=std::max(0, left), max_x=std::min(static_cast<int> (width)-1, right);
00202     for (int x=min_x; x<=max_x && !topIsDone; ++x)
00203       if (pcl_isfinite (points[lineStart + x].range))
00204         topIsDone = true;
00205   }
00206   // Check if range image is empty
00207   if (top >= static_cast<int> (height)) 
00208   {
00209     points.clear ();
00210     width = height = 0;
00211     return;
00212   }
00213   // Find right border
00214   while (!rightIsDone) 
00215   {
00216     --right;
00217     int min_y=std::max(0, top), max_y=std::min(static_cast<int> (height)-1, bottom);
00218     for (int y=min_y; y<=max_y && !rightIsDone; ++y)
00219       if (pcl_isfinite (points[y*width + right].range))
00220         rightIsDone = true;
00221   }
00222   // Find bottom border
00223   while (!bottomIsDone) 
00224   {
00225     --bottom;
00226     int lineStart = bottom*width;
00227     int min_x=std::max(0, left), max_x=std::min(static_cast<int> (width)-1, right);
00228     for (int x=min_x; x<=max_x && !bottomIsDone; ++x)
00229       if (pcl_isfinite (points[lineStart + x].range))
00230         bottomIsDone = true;
00231   } 
00232   // Find left border
00233   while (!leftIsDone) 
00234   {
00235     ++left;
00236     int min_y=std::max(0, top), max_y=std::min(static_cast<int> (height)-1, bottom);
00237     for (int y=min_y; y<=max_y && !leftIsDone; ++y)
00238       if (pcl_isfinite (points[y*width + left].range))
00239         leftIsDone = true;
00240   } 
00241   left-=borderSize; top-=borderSize; right+=borderSize; bottom+=borderSize;
00242   
00243   // Create copy without copying the old points - vector::swap only copies a few pointers, not the content
00244   PointCloud<PointWithRange>::VectorType tmpPoints;
00245   points.swap (tmpPoints);
00246   RangeImage oldRangeImage = *this;
00247   tmpPoints.swap (oldRangeImage.points);
00248   
00249   width = right-left+1; height = bottom-top+1;
00250   image_offset_x_ = left+oldRangeImage.image_offset_x_;
00251   image_offset_y_ = top+oldRangeImage.image_offset_y_;
00252   points.resize (width*height);
00253   
00254   //std::cout << oldRangeImage.width<<"x"<<oldRangeImage.height<<" -> "<<width<<"x"<<height<<"\n";
00255   
00256   // Copy points
00257   for (int y=0, oldY=top; y< static_cast<int> (height); ++y,++oldY) 
00258   {
00259     for (int x=0, oldX=left; x< static_cast<int> (width); ++x,++oldX) 
00260     {
00261       PointWithRange& currentPoint = points[y*width + x];
00262       if (oldX<0 || oldX>= static_cast<int> (oldRangeImage.width) || oldY<0 || oldY>= static_cast<int> (oldRangeImage.height)) 
00263       {
00264         currentPoint = unobserved_point;
00265         continue;
00266       }
00267       currentPoint = oldRangeImage.points[oldY*oldRangeImage.width + oldX];
00268     }
00269   }
00270 }
00271 
00273 void 
00274 RangeImage::recalculate3DPointPositions () 
00275 {
00276   for (int y = 0; y < static_cast<int> (height); ++y) 
00277   {
00278     for (int x = 0; x < static_cast<int> (width); ++x) 
00279     {
00280       PointWithRange& point = points[y*width + x];
00281       if (!pcl_isinf (point.range)) 
00282         calculate3DPoint (static_cast<float> (x), static_cast<float> (y), point.range, point);
00283     }
00284   }
00285 }
00286 
00288 float* 
00289 RangeImage::getRangesArray () const 
00290 {
00291   int arraySize = width * height;
00292   float* ranges = new float[arraySize];
00293   for (int i=0; i<arraySize; ++i)
00294     ranges[i] = points[i].range;
00295   return ranges;
00296 }
00297 
00298 
00300 void 
00301 RangeImage::getIntegralImage (float*& integral_image, int*& valid_points_num_image) const
00302 {
00303   integral_image = new float[width*height];
00304   float* integral_image_ptr = integral_image;
00305   valid_points_num_image = new int[width*height];
00306   int* valid_points_num_image_ptr = valid_points_num_image;
00307   for (int y = 0; y < static_cast<int> (height); ++y)
00308   {
00309     for (int x = 0; x < static_cast<int> (width); ++x)
00310     {
00311       float& integral_pixel = * (integral_image_ptr++);
00312       integral_pixel = getPoint (x, y).range;
00313       int& valid_points_num = * (valid_points_num_image_ptr++);
00314       valid_points_num = 1;
00315       if (pcl_isinf (integral_pixel))
00316       {
00317         integral_pixel = 0.0f;
00318         valid_points_num = 0;
00319       }
00320       float left_value=0, top_left_value=0, top_value=0;
00321       int left_valid_points=0, top_left_valid_points=0, top_valid_points=0;
00322       if (x>0)
00323       {
00324         left_value = integral_image[y*width+x-1];
00325         left_valid_points = valid_points_num_image[y*width+x-1];
00326         if (y>0)
00327         {
00328           top_left_value = integral_image[ (y-1)*width+x-1];
00329           top_left_valid_points = valid_points_num_image[ (y-1)*width+x-1];
00330         }
00331       }
00332       if (y>0)
00333       {
00334         top_value = integral_image[ (y-1)*width+x];
00335         top_valid_points = valid_points_num_image[ (y-1)*width+x];
00336       }
00337       
00338       integral_pixel += left_value + top_value - top_left_value;
00339       valid_points_num += left_valid_points + top_valid_points - top_left_valid_points;
00340     }
00341   }
00342 }
00343 
00345 void 
00346 RangeImage::setUnseenToMaxRange ()
00347 {
00348   for (unsigned int i=0; i<points.size (); ++i)
00349     if (pcl_isinf (points[i].range))
00350       points[i].range = std::numeric_limits<float>::infinity ();
00351 }
00352 
00354 void 
00355 RangeImage::getHalfImage (RangeImage& half_image) const
00356 {
00357   half_image.setAngularResolution (2.0f*angular_resolution_x_, 2.0f*angular_resolution_y_);
00358   half_image.image_offset_x_ = image_offset_x_/2;
00359   half_image.image_offset_y_ = image_offset_y_/2;
00360   half_image.width  = width/2;
00361   half_image.height = height/2;
00362   half_image.is_dense = is_dense;
00363   half_image.points.clear ();
00364   half_image.points.resize (half_image.width*half_image.height);
00365   
00366   int src_start_x = 2*half_image.image_offset_x_ - image_offset_x_,
00367       src_start_y = 2*half_image.image_offset_y_ - image_offset_y_;
00368   
00369   for (int dst_y=0; dst_y < static_cast<int> (half_image.height); ++dst_y)
00370   {
00371     for (int dst_x=0; dst_x < static_cast<int> (half_image.width); ++dst_x)
00372     {
00373       PointWithRange& dst_point = half_image.getPoint (dst_x, dst_y);
00374       dst_point=unobserved_point;
00375       int src_x_min = src_start_x + 2*dst_x,
00376           src_x_max = src_x_min + 1,
00377           src_y_min = src_start_y + 2*dst_y,
00378           src_y_max = src_y_min + 1;
00379       for (int src_x=src_x_min; src_x<=src_x_max; ++src_x)
00380       {
00381         for (int src_y=src_y_min; src_y<=src_y_max; ++src_y)
00382         {
00383           if (!isObserved (src_x, src_y))
00384             continue;
00385           const PointWithRange& src_point = getPoint (src_x, src_y);
00386           if (pcl_isfinite (dst_point.range) && src_point.range > dst_point.range)
00387             continue;
00388           dst_point = src_point;
00389         }
00390       }
00391     }
00392   }
00393 }
00394 
00396 void 
00397 RangeImage::getSubImage (int sub_image_image_offset_x, int sub_image_image_offset_y, int sub_image_width,
00398                          int sub_image_height, int combine_pixels, RangeImage& sub_image) const
00399 {
00400   sub_image.setAngularResolution (static_cast<float> (combine_pixels)*angular_resolution_x_,
00401                                   static_cast<float> (combine_pixels)*angular_resolution_y_);
00402   sub_image.image_offset_x_ = sub_image_image_offset_x;
00403   sub_image.image_offset_y_ = sub_image_image_offset_y;
00404   sub_image.width = sub_image_width;
00405   sub_image.height = sub_image_height;
00406   sub_image.is_dense = is_dense;
00407   sub_image.points.clear ();
00408   sub_image.points.resize (sub_image.width*sub_image.height);
00409   
00410   int src_start_x = combine_pixels*sub_image.image_offset_x_ - image_offset_x_,
00411       src_start_y = combine_pixels*sub_image.image_offset_y_ - image_offset_y_;
00412   
00413   for (int dst_y=0; dst_y < static_cast<int> (sub_image.height); ++dst_y)
00414   {
00415     for (int dst_x=0; dst_x < static_cast<int> (sub_image.width); ++dst_x)
00416     {
00417       PointWithRange& dst_point = sub_image.getPoint (dst_x, dst_y);
00418       dst_point=unobserved_point;
00419       int src_x_min = src_start_x + combine_pixels*dst_x,
00420           src_x_max = src_x_min + combine_pixels-1,
00421           src_y_min = src_start_y + combine_pixels*dst_y,
00422           src_y_max = src_y_min + combine_pixels-1;
00423       for (int src_x=src_x_min; src_x<=src_x_max; ++src_x)
00424       {
00425         for (int src_y=src_y_min; src_y<=src_y_max; ++src_y)
00426         {
00427           if (!isInImage (src_x, src_y))
00428             continue;
00429           const PointWithRange& src_point = getPoint (src_x, src_y);
00430           if (pcl_isfinite (dst_point.range) && src_point.range > dst_point.range)
00431             continue;
00432           dst_point = src_point;
00433         }
00434       }
00435     }
00436   }
00437 }
00438 
00439 
00441 void 
00442 RangeImage::getMinMaxRanges (float& min_range, float& max_range) const
00443 {
00444   min_range = std::numeric_limits<float>::infinity ();
00445   max_range = -std::numeric_limits<float>::infinity ();
00446   for (unsigned int i=0; i<points.size (); ++i)
00447   {
00448     float range = points[i].range;
00449     if (!pcl_isfinite (range))
00450       continue;
00451     min_range = (std::min) (min_range, range);
00452     max_range = (std::max) (max_range, range);
00453   }
00454 }
00455 
00457 void 
00458 RangeImage::change3dPointsToLocalCoordinateFrame ()
00459 {
00460   to_world_system_.setIdentity ();
00461   to_range_image_system_.setIdentity ();
00462   recalculate3DPointPositions ();
00463 }
00464 
00466 float* 
00467 RangeImage::getInterpolatedSurfaceProjection (const Eigen::Affine3f& pose, int pixel_size, float world_size) const
00468 {
00469   float max_dist = 0.5f*world_size,
00470         cell_size = world_size/float (pixel_size);
00471   float world2cell_factor = 1.0f/cell_size,
00472         world2cell_offset = 0.5f*float (pixel_size)-0.5f;
00473   float cell2world_factor = cell_size,
00474         cell2world_offset = -max_dist + 0.5f*cell_size;
00475   Eigen::Affine3f inverse_pose = pose.inverse (Eigen::Isometry);
00476   
00477   int no_of_pixels = pixel_size*pixel_size;
00478   float* surface_patch = new float[no_of_pixels];
00479   SET_ARRAY (surface_patch, -std::numeric_limits<float>::infinity (), no_of_pixels);
00480   
00481   Eigen::Vector3f position = inverse_pose.translation ();
00482   int middle_x, middle_y;
00483   getImagePoint (position, middle_x, middle_y);
00484   int min_search_radius = 2;
00485   bool still_in_range = true;
00486 
00487   for (int radius=0;  still_in_range;  ++radius) 
00488   {
00489     int x=middle_x-radius-1, y=middle_y-radius;  // Top left - 1
00490     still_in_range = radius<min_search_radius;
00491     for (int i=0; i<8*radius || (radius==0&&i==0); ++i)
00492     {
00493       if (i<=2*radius) ++x; else if (i<=4*radius) ++y; else if (i<=6*radius) --x; else --y;
00494 
00495       Eigen::Vector3f point1, point2, point3;
00496       if (!isValid (x,y) || !isValid (x+1,y+1))
00497         continue;
00498       getPoint (x, y, point1);
00499       point1 = pose*point1;
00500       if (fabs (point1[2]) > max_dist)
00501         continue;
00502       
00503       getPoint (x+1, y+1, point2);
00504       point2 = pose*point2;
00505       if (fabs (point2[2]) > max_dist)
00506         continue;
00507       
00508       for (int triangle_idx=0; triangle_idx<=1; ++triangle_idx)
00509       {
00510         if (triangle_idx==0)  // First triangle
00511         {
00512           if (!isValid (x,y+1))
00513             continue;
00514           getPoint (x, y+1, point3);
00515         }
00516         else  // Second triangle
00517         {
00518           if (!isValid (x+1,y))
00519             continue;
00520           getPoint (x+1, y, point3);
00521         }
00522         point3 = pose*point3;
00523         if (fabs (point3[2]) > max_dist)
00524           continue;
00525         
00526         // Are all the points either left, right, on top or below the bottom of the surface patch?
00527         if ( (point1[0] < -max_dist  &&  point2[0] < -max_dist  &&  point3[0] < -max_dist) ||
00528             (point1[0] >  max_dist  &&  point2[0] >  max_dist  &&  point3[0] >  max_dist) ||
00529             (point1[1] < -max_dist  &&  point2[1] < -max_dist  &&  point3[1] < -max_dist) ||
00530             (point1[1] >  max_dist  &&  point2[1] >  max_dist  &&  point3[1] >  max_dist))
00531         {
00532           continue;
00533         }
00534         
00535         still_in_range = true;
00536         
00537         // Now we have a valid triangle (point1, point2, point3) in our new patch
00538         float cell1_x = world2cell_factor*point1[0] + world2cell_offset,
00539               cell1_y = world2cell_factor*point1[1] + world2cell_offset,
00540               cell1_z = point1[2],
00541               cell2_x = world2cell_factor*point2[0] + world2cell_offset,
00542               cell2_y = world2cell_factor*point2[1] + world2cell_offset,
00543               cell2_z = point2[2],
00544               cell3_x = world2cell_factor*point3[0] + world2cell_offset,
00545               cell3_y = world2cell_factor*point3[1] + world2cell_offset,
00546               cell3_z = point3[2];
00547         
00548         int min_cell_x = (std::max) (0, int (pcl_lrint (ceil ( (std::min) (cell1_x, (std::min) (cell2_x, cell3_x)))))),
00549             max_cell_x = (std::min) (pixel_size-1, int (pcl_lrint (floor ( (std::max) (cell1_x,
00550                                                                        (std::max) (cell2_x, cell3_x)))))),
00551             min_cell_y = (std::max) (0, int (pcl_lrint (ceil ( (std::min) (cell1_y, (std::min) (cell2_y, cell3_y)))))),
00552             max_cell_y = (std::min) (pixel_size-1, int (pcl_lrint (floor ( (std::max) (cell1_y,
00553                                                                        (std::max) (cell2_y, cell3_y))))));
00554         if (max_cell_x<min_cell_x || max_cell_y<min_cell_y)
00555           continue;
00556         
00557         // We will now do the following:
00558         //   For each cell in the rectangle defined by the four values above,
00559         //   we test if it is in the original triangle (in 2D).
00560         //   If this is the case, we calculate the actual point on the 3D triangle, thereby interpolating the result
00561         //   See http://www.blackpawn.com/texts/pointinpoly/default.html
00562         Eigen::Vector2f cell1 (cell1_x, cell1_y),
00563                         cell2 (cell2_x, cell2_y),
00564                         cell3 (cell3_x, cell3_y),
00565                         v0 = cell3 - cell1,
00566                         v1 = cell2 - cell1;
00567         float dot00 = v0.dot (v0),
00568               dot01 = v0.dot (v1),
00569               dot11 = v1.dot (v1),
00570               invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
00571         
00572         for (int cell_x=min_cell_x; cell_x<=max_cell_x; ++cell_x)
00573         {
00574           for (int cell_y=min_cell_y; cell_y<=max_cell_y; ++cell_y)
00575           {
00576             Eigen::Vector2f current_cell (cell_x, cell_y),
00577                             v2 = current_cell - cell1;
00578             float dot02 = v0.dot (v2),
00579                   dot12 = v1.dot (v2),
00580                   u = (dot11 * dot02 - dot01 * dot12) * invDenom,
00581                   v = (dot00 * dot12 - dot01 * dot02) * invDenom;
00582             bool point_in_triangle = (u > -0.01) && (v >= -0.01) && (u + v <= 1.01);
00583             
00584             if (!point_in_triangle)
00585               continue;
00586             
00587             float new_value = cell1_z + u* (cell3_z-cell1_z) + v* (cell2_z-cell1_z);
00588             
00589             float& value = surface_patch[cell_y*pixel_size + cell_x];
00590             if (pcl_isinf (value))
00591               value = new_value;
00592             else
00593               value = (std::min) (value, new_value);
00594           }
00595         }
00596       }
00597     }
00598   }
00599   
00600   // Now find out, if there should be max ranges
00601   for (int cell_y=0; cell_y<pixel_size; ++cell_y)
00602   {
00603     for (int cell_x=0; cell_x<pixel_size; ++cell_x)
00604     {
00605       int index= cell_y*pixel_size + cell_x;
00606       float& value = surface_patch[index];
00607       if (!pcl_isinf (value))
00608         continue;
00609       
00610       // Go through immediate neighbors
00611       bool is_background = false;
00612       for (int cell2_y=cell_y-1; cell2_y<=cell_y+1&&!is_background; ++cell2_y)
00613       {
00614         for (int cell2_x=cell_x-1; cell2_x<=cell_x+1; ++cell2_x)
00615         {
00616           if (cell2_x<0||cell2_x>=pixel_size||cell2_y<0||cell2_y>=pixel_size || (cell2_x==cell_x && cell2_y==cell_y))
00617             continue;
00618           float neighbor_value = surface_patch[cell2_y*pixel_size + cell2_x];
00619           if (pcl_isfinite (neighbor_value))
00620           {
00621             float cell_pos_x = static_cast<float> (cell_x) + 0.6f * static_cast<float> (cell_x - cell2_x),
00622                   cell_pos_y = static_cast<float> (cell_y) + 0.6f * static_cast<float> (cell_y - cell2_y);
00623             Eigen::Vector3f fake_point (cell2world_factor* (cell_pos_x)+cell2world_offset,
00624                                         cell2world_factor*cell_pos_y+cell2world_offset, neighbor_value);
00625             fake_point = inverse_pose*fake_point;
00626             float range_difference = getRangeDifference (fake_point);
00627             if (range_difference > max_dist)
00628             {
00629               value = std::numeric_limits<float>::infinity ();
00630               is_background = true;
00631               break;
00632             }
00633           }
00634         }
00635       }
00636       if (is_background)
00637       {
00638         // Set all -INFINITY neighbors to INFINITY
00639         for (int cell2_y=cell_y-1; cell2_y<=cell_y+1; ++cell2_y)
00640         {
00641           for (int cell2_x=cell_x-1; cell2_x<=cell_x+1; ++cell2_x)
00642           {
00643             if (cell2_x<0||cell2_x>=pixel_size||cell2_y<0||cell2_y>=pixel_size || (cell2_x==cell_x && cell2_y==cell_y))
00644               continue;
00645             int index2 = cell2_y*pixel_size + cell2_x;
00646             float& neighbor_value = surface_patch[index2];
00647             if (pcl_isinf (neighbor_value) && neighbor_value<0)
00648               neighbor_value = std::numeric_limits<float>::infinity ();
00649           }
00650         }
00651       }
00652     }
00653   }
00654   
00655   return surface_patch;
00656 }
00657 
00659 float* RangeImage::getInterpolatedSurfaceProjection (const Eigen::Vector3f& point, int pixel_size,
00660                                                      float world_size) const
00661 {
00662   Eigen::Affine3f pose = getTransformationToViewerCoordinateFrame (point);
00663   return (getInterpolatedSurfaceProjection (pose, pixel_size, world_size));
00664 }
00665 
00667 bool 
00668 RangeImage::getNormalBasedUprightTransformation (const Eigen::Vector3f& point, float max_dist,
00669                                                  Eigen::Affine3f& transformation) const
00670 {
00671   int x, y;
00672   getImagePoint (point, x, y);
00673 
00674   Eigen::Vector3f neighbor;
00675   VectorAverage3f vector_average;
00676   float max_dist_squared=max_dist*max_dist, max_dist_reciprocal=1.0f/max_dist;
00677   
00678   bool still_in_range = true;
00679   for (int radius=1;  still_in_range;  ++radius) 
00680   {
00681     int x2=x-radius-1, y2=y-radius;  // Top left - 1
00682     still_in_range = false;
00683     for (int i=0; i<8*radius; ++i)
00684     {
00685       if (i<=2*radius) ++x2; else if (i<=4*radius) ++y2; else if (i<=6*radius) --x2; else --y2;
00686       if (!isValid (x2, y2))
00687       {
00688         continue;
00689       }
00690       getPoint (x2, y2, neighbor);
00691       float distance_squared = (neighbor-point).squaredNorm ();
00692       if (distance_squared > max_dist_squared)
00693       {
00694         continue;
00695       }
00696       still_in_range = true;
00697       float distance = sqrtf (distance_squared),
00698             weight = distance*max_dist_reciprocal;
00699       vector_average.add (neighbor, weight);
00700     }
00701   }
00702   
00703   Eigen::Vector3f normal, point_on_plane;
00704   if (vector_average.getNoOfSamples () > 10)
00705   {
00706     Eigen::Vector3f eigen_values, eigen_vector2, eigen_vector3;
00707     vector_average.doPCA (eigen_values, normal, eigen_vector2, eigen_vector3);
00708     if (normal.dot ( (vector_average.getMean ()-getSensorPos ()).normalized ()) < 0.0f)
00709       normal *= -1.0f;
00710     point_on_plane = (normal.dot (vector_average.getMean ()) - normal.dot (point))*normal + point;
00711   }
00712   else
00713   {
00714     if (!getNormalForClosestNeighbors (x, y, 2, point, 15, normal, &point_on_plane, 1))
00715       return false;
00716   }
00717   getTransformationFromTwoUnitVectorsAndOrigin (Eigen::Vector3f (0.0f, 1.0f, 0.0f),
00718                                                 normal, point_on_plane, transformation);
00719   
00720   return (true);
00721 }
00722 
00724 void 
00725 RangeImage::getSurfaceAngleChangeImages (int radius, float*& angle_change_image_x, float*& angle_change_image_y) const
00726 {
00727   MEASURE_FUNCTION_TIME;
00728   int size = width*height;
00729   angle_change_image_x = new float[size];
00730   angle_change_image_y = new float[size];
00731   for (int y=0; y<int (height); ++y)
00732   {
00733     for (int x=0; x<int (width); ++x)
00734     {
00735       int index = y*width+x;
00736       getSurfaceAngleChange (x, y, radius, angle_change_image_x[index], angle_change_image_y[index]);
00737     }
00738   }
00739 }
00740 
00742 void 
00743 RangeImage::getAcutenessValueImages (int pixel_distance, float*& acuteness_value_image_x,
00744                                      float*& acuteness_value_image_y) const
00745 {
00746   MEASURE_FUNCTION_TIME;
00747   int size = width*height;
00748   acuteness_value_image_x = new float[size];
00749   acuteness_value_image_y = new float[size];
00750   for (int y=0; y<int (height); ++y)
00751   {
00752     for (int x=0; x<int (width); ++x)
00753     {
00754       int index = y*width+x;
00755       acuteness_value_image_x[index] = getAcutenessValue (x, y, x+pixel_distance, y);
00756       acuteness_value_image_y[index] = getAcutenessValue (x, y, x, y+pixel_distance);
00757     }
00758   }
00759 }
00760 
00762 float* 
00763 RangeImage::getImpactAngleImageBasedOnLocalNormals (int radius) const
00764 {
00765   MEASURE_FUNCTION_TIME;
00766   int size = width*height;
00767   float* impact_angle_image = new float[size];
00768   for (int y=0; y<int (height); ++y)
00769   {
00770     for (int x=0; x<int (width); ++x)
00771     {
00772       impact_angle_image[y*width+x] = getImpactAngleBasedOnLocalNormal (x, y, radius);
00773     }
00774   }
00775   return impact_angle_image;
00776 }
00777 
00779 void 
00780 RangeImage::getRangeImageWithSmoothedSurface (int radius, RangeImage& smoothed_range_image) const
00781 {
00782   int step_size = (std::max) (1, radius/2);
00783   int no_of_nearest_neighbors = static_cast<int> (pow (static_cast<double> (radius / step_size + 1), 2.0));
00784   
00785   smoothed_range_image = *this;
00786   Eigen::Vector3f sensor_pos = getSensorPos ();
00787   for (int y=0; y<int (height); ++y)
00788   {
00789     for (int x=0; x<int (width); ++x)
00790     {
00791       PointWithRange& point = smoothed_range_image.getPoint (x, y);
00792       if (pcl_isinf (point.range))
00793         continue;
00794       Eigen::Vector3f normal, mean, eigen_values;
00795       float used_squared_max_distance;
00796       getSurfaceInformation (x, y, radius, point.getVector3fMap (), no_of_nearest_neighbors,
00797                              step_size, used_squared_max_distance,
00798                              normal, mean, eigen_values);
00799       
00800       Eigen::Vector3f viewing_direction = (point.getVector3fMap ()-sensor_pos).normalized ();
00801       float new_range = normal.dot (mean-sensor_pos) / normal.dot (viewing_direction);
00802       point.range = new_range;
00803       calculate3DPoint (static_cast<float> (x), static_cast<float> (y), point.range, point);
00804       
00805       const PointWithRange& original_point = getPoint (x, y);
00806       float distance_squared = squaredEuclideanDistance (original_point, point);
00807       if (distance_squared > used_squared_max_distance)
00808         point = original_point;
00809     }
00810   }
00811 }
00812 
00814 void 
00815 RangeImage::extractFarRanges (const pcl::PCLPointCloud2& point_cloud_data,
00816                               PointCloud<PointWithViewpoint>& far_ranges)
00817 {
00818   int x_idx = -1, y_idx = -1, z_idx = -1,
00819       vp_x_idx = -1, vp_y_idx = -1, vp_z_idx = -1, distance_idx = -1;
00820   for (int d = 0; d < static_cast<int> (point_cloud_data.fields.size ()); ++d)
00821   {
00822     if (point_cloud_data.fields[d].name == "x") x_idx = d;
00823     if (point_cloud_data.fields[d].name == "y") y_idx = d;
00824     if (point_cloud_data.fields[d].name == "z") z_idx = d;
00825     if (point_cloud_data.fields[d].name == "vp_x") vp_x_idx = d;
00826     if (point_cloud_data.fields[d].name == "vp_y") vp_y_idx = d;
00827     if (point_cloud_data.fields[d].name == "vp_z") vp_z_idx = d;
00828     if (point_cloud_data.fields[d].name == "distance") distance_idx = d;
00829   }
00830   
00831   if (x_idx<0 || y_idx<0 || z_idx<0 || vp_x_idx<0 || vp_y_idx<0 || vp_z_idx<0 || distance_idx<0)
00832   {
00833     return;
00834   }
00835   
00836   int point_step = point_cloud_data.point_step;
00837   const unsigned char* data = &point_cloud_data.data[0];
00838   int x_offset = point_cloud_data.fields[x_idx].offset,
00839       y_offset = point_cloud_data.fields[y_idx].offset,
00840       z_offset = point_cloud_data.fields[z_idx].offset,
00841       vp_x_offset = point_cloud_data.fields[vp_x_idx].offset,
00842       vp_y_offset = point_cloud_data.fields[vp_y_idx].offset,
00843       vp_z_offset = point_cloud_data.fields[vp_z_idx].offset,
00844       distance_offset = point_cloud_data.fields[distance_idx].offset;
00845   
00846   for (size_t point_idx = 0; point_idx < point_cloud_data.width*point_cloud_data.height; ++point_idx)
00847   {
00848     float x = *reinterpret_cast<const float*> (data+x_offset), 
00849           y = *reinterpret_cast<const float*> (data+y_offset), 
00850           z = *reinterpret_cast<const float*> (data+z_offset),
00851           vp_x = *reinterpret_cast<const float*> (data+vp_x_offset),
00852           vp_y = *reinterpret_cast<const float*> (data+vp_y_offset),
00853           vp_z = *reinterpret_cast<const float*> (data+vp_z_offset),
00854           distance = *reinterpret_cast<const float*> (data+distance_offset);
00855     data+=point_step;
00856     
00857     if (!pcl_isfinite (x) && pcl_isfinite (distance))
00858     {
00859       PointWithViewpoint point;
00860       point.x=distance; point.y=y; point.z=z;
00861       point.vp_x=vp_x; point.vp_y=vp_y; point.vp_z=vp_z;
00862       far_ranges.points.push_back (point);
00863     }
00864   }
00865   far_ranges.width= static_cast<uint32_t> (far_ranges.points.size ());  far_ranges.height = 1;
00866   far_ranges.is_dense = false;
00867 }
00868 
00870 float
00871 RangeImage::getOverlap (const RangeImage& other_range_image, const Eigen::Affine3f& relative_transformation,
00872                         int search_radius, float max_distance, int pixel_step) const
00873 {
00874   int hits_counter=0, valid_points_counter=0;
00875   
00876   float max_distance_squared = max_distance*max_distance;
00877   
00878   # pragma omp parallel for num_threads (max_no_of_threads) default (shared) schedule (dynamic, 1) \
00879                         reduction (+ : valid_points_counter) reduction (+ : hits_counter)
00880   for (int other_y=0; other_y<int (other_range_image.height); other_y+=pixel_step)
00881   {
00882     for (int other_x=0; other_x<int (other_range_image.width); other_x+=pixel_step)
00883     {
00884       const PointWithRange& point = other_range_image.getPoint (other_x, other_y);
00885       if (!pcl_isfinite (point.range))
00886         continue;
00887       ++valid_points_counter;
00888       Eigen::Vector3f transformed_point = relative_transformation * point.getVector3fMap ();
00889       int x,y;
00890       getImagePoint (transformed_point, x, y);
00891       float closest_distance = max_distance_squared;
00892       Eigen::Vector3f closest_point (0.0f, 0.0f, 0.0f);
00893       bool found_neighbor = false;
00894       for (int y2=y-pixel_step*search_radius; y2<=y+pixel_step*search_radius; y2+=pixel_step)
00895       {
00896         for (int x2=x-pixel_step*search_radius; x2<=x+pixel_step*search_radius; x2+=pixel_step)
00897         {
00898           const PointWithRange& neighbor = getPoint (x2, y2);
00899           if (!pcl_isfinite (neighbor.range))
00900             continue;
00901           float distance = (transformed_point-neighbor.getVector3fMap ()).squaredNorm ();
00902           if (distance < closest_distance)
00903           {
00904             closest_distance = distance;
00905             closest_point = neighbor.getVector3fMap ();
00906             found_neighbor = true;
00907           }
00908         }
00909       }
00910 
00911       if (found_neighbor)
00912       {
00913         ++hits_counter;
00914       }
00915     }
00916   }
00917   return static_cast<float> (hits_counter)/static_cast<float> (valid_points_counter);
00918 }
00919 
00921 void
00922 RangeImage::getBlurredImageUsingIntegralImage (int blur_radius, float* integral_image, int* valid_points_num_image,
00923                                                RangeImage& blurred_image) const
00924 {
00925   this->copyTo(blurred_image);
00926   for (int y=0; y<int (height); ++y)
00927   {
00928     for (int x=0; x<int (width); ++x)
00929     {
00930       const PointWithRange& old_point = getPoint (x, y);
00931       PointWithRange& new_point = blurred_image.getPoint (x, y);
00932       if (!pcl_isfinite (old_point.range))
00933         continue;
00934       
00935       int top= (std::max) (-1, y-blur_radius-1), right = (std::min) (static_cast<int> (width)-1, x+blur_radius), bottom =
00936           (std::min) (static_cast<int> (height)-1, y+blur_radius), left= (std::max) (-1, x-blur_radius-1);
00937       
00938       float top_left_value=0, top_right_value=0,
00939             bottom_right_value=integral_image[bottom*width+right], bottom_left_value=0;
00940       int top_left_valid_points=0, top_right_valid_points=0,
00941           bottom_right_valid_points=valid_points_num_image[bottom*width+right], bottom_left_valid_points=0;
00942       if (left>=0)
00943       {
00944         bottom_left_value = integral_image[bottom*width+left];
00945         bottom_left_valid_points = valid_points_num_image[bottom*width+left];
00946         if (top>=0)
00947         {
00948           top_left_value = integral_image[top*width+left];
00949           top_left_valid_points = valid_points_num_image[top*width+left];
00950         }
00951       }
00952       if (top>=0)
00953       {
00954         top_right_value = integral_image[top*width+right];
00955         top_right_valid_points = valid_points_num_image[top*width+right];
00956       }
00957       int valid_points_num = bottom_right_valid_points + top_left_valid_points - bottom_left_valid_points -
00958                              top_right_valid_points;
00959       new_point.range = (bottom_right_value + top_left_value - bottom_left_value - top_right_value) / 
00960                         static_cast<float> (valid_points_num);
00961     }
00962   }
00963   blurred_image.recalculate3DPointPositions ();
00964 }
00965 
00967 void
00968 RangeImage::getBlurredImage (int blur_radius, RangeImage& blurred_image) const
00969 {
00970   //MEASURE_FUNCTION_TIME;
00971   
00972   if (blur_radius > 1)  // For a high blur radius it's faster to use integral images
00973   {
00974     float* integral_image;
00975     int* valid_points_num_image;
00976     getIntegralImage (integral_image, valid_points_num_image);
00977     getBlurredImageUsingIntegralImage (blur_radius, integral_image, valid_points_num_image, blurred_image);
00978     delete[] integral_image;
00979     delete[] valid_points_num_image;
00980     return;
00981   }
00982   
00983   this->copyTo(blurred_image);
00984   
00985   if (blur_radius==0)
00986     return;
00987   
00988   for (int y=0; y < static_cast<int> (height); ++y)
00989   {
00990     for (int x=0; x < static_cast<int> (width); ++x)
00991     {
00992       PointWithRange& new_point = blurred_image.getPoint (x, y);
00993       const PointWithRange& original_point = getPoint (x, y);
00994       if (!pcl_isfinite (original_point.range))
00995         continue;
00996       
00997       new_point.range = 0.0f;
00998       float weight_sum = 0.0f;
00999       for (int y2=y-blur_radius; y2<y+blur_radius; ++y2)
01000       {
01001         for (int x2=x-blur_radius; x2<x+blur_radius; ++x2)
01002         {
01003           if (!isValid (x2,y2))
01004             continue;
01005           new_point.range += getPoint (x2, y2).range;
01006           weight_sum += 1.0f;
01007         }
01008       }
01009       new_point.range /= weight_sum;
01010     }
01011   }
01012   blurred_image.recalculate3DPointPositions ();
01013 }
01014 
01016 void
01017 RangeImage::copyTo (RangeImage& other) const
01018 {
01019   other = *this;
01020 }
01021 
01022 }  // namespace end
01023 


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:32:00