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 
00037 
00038 #include <gtest/gtest.h>
00039 
00040 #include <pcl/point_types.h>
00041 #include <pcl/features/normal_3d.h>
00042 #include <pcl/features/integral_image_normal.h>
00043 
00044 #include <iostream>
00045 
00046 using namespace pcl;
00047 using namespace std;
00048 
00049 typedef search::KdTree<PointXYZ>::Ptr KdTreePtr;
00050 PointCloud<PointXYZ> cloud;
00051 KdTreePtr tree;
00052 
00053 NormalEstimation<PointXYZ, Normal> n;
00054 IntegralImageNormalEstimation<PointXYZ, Normal> ne;
00055 
00057 TEST(PCL, IntegralImage1D)
00058 {
00059   const unsigned width = 640;
00060   const unsigned height = 480;
00061   const unsigned max_window_size = 5;
00062   const unsigned min_window_size = 1;
00063   IntegralImage2D<float,1> integral_image1(true); 
00064   IntegralImage2D<float,1> integral_image2(false);
00065 
00066   
00067   float* data = new float[width * height];
00068   for(unsigned yIdx = 0; yIdx < height; ++yIdx)
00069   {
00070     for(unsigned xIdx = 0; xIdx < width; ++xIdx)
00071     {
00072       data[width * yIdx + xIdx] = static_cast<float> (xIdx);
00073     }
00074   }
00075 
00076   
00077   integral_image1.setInput (data, width, height, 1, width);
00078   integral_image2.setInput (data, width, height, 1, width);
00079 
00080   
00081   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00082   {
00083     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00084     {
00085       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00086       {
00087         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00088         {
00089           
00090           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00091           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00092           EXPECT_EQ (window_height * window_width, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00093           EXPECT_EQ (window_height * window_width, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00094 
00095           int w = window_width + xIdx - 1;
00096           long result = w * (w + 1) * (2*w + 1) - xIdx * (xIdx - 1) * (2*xIdx - 1);
00097           EXPECT_EQ (window_height * result, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height) * 6);
00098         }
00099       }
00100     }
00101   }
00102   delete[] data;
00103 
00104   
00105   unsigned element_stride = 2;
00106   unsigned row_stride = width * element_stride;
00107   data = new float[row_stride * height];
00108   for(unsigned yIdx = 0; yIdx < height; ++yIdx)
00109   {
00110     for(unsigned xIdx = 0; xIdx < row_stride; xIdx += element_stride)
00111     {
00112       data[row_stride * yIdx + xIdx] = static_cast<float> (xIdx >> 1);
00113       data[row_stride * yIdx + xIdx + 1] = -1;
00114     }
00115   }
00116   integral_image1.setInput (data, width, height, element_stride, row_stride);
00117   integral_image2.setInput (data, width, height, element_stride, row_stride);
00118   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00119   {
00120     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00121     {
00122       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00123       {
00124         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00125         {
00126           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00127           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00128           EXPECT_EQ (window_height * window_width, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00129           EXPECT_EQ (window_height * window_width, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00130 
00131           int w = window_width + xIdx - 1;
00132           long result = w * (w + 1) * (2*w + 1) - xIdx * (xIdx - 1) * (2*xIdx - 1);
00133           EXPECT_EQ (window_height * result, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height) * 6);
00134         }
00135       }
00136     }
00137   }
00138   delete[] data;
00139 
00140   
00141   element_stride = 3;
00142   row_stride = width * element_stride + 1; 
00143   data = new float[row_stride * height];
00144   for (unsigned yIdx = 0; yIdx < height; ++yIdx)
00145   {
00146     for (unsigned xIdx = 0; xIdx < width; ++xIdx)
00147     {
00148       data[row_stride * yIdx + element_stride * xIdx] = 1.0f;
00149       data[row_stride * yIdx + element_stride * xIdx + 1] = 2.0f;
00150       data[row_stride * yIdx + element_stride * xIdx + 2] = static_cast<float> (xIdx);
00151     }
00152   }
00153   integral_image1.setInput (data, width, height, element_stride, row_stride);
00154   integral_image2.setInput (data, width, height, element_stride, row_stride);
00155   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00156   {
00157     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00158     {
00159       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00160       {
00161         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00162         {
00163           EXPECT_EQ (window_width * window_height, integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00164           EXPECT_EQ (window_width * window_height, integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00165           EXPECT_EQ (window_width * window_height, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00166           EXPECT_EQ (window_width * window_height, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00167 
00168           EXPECT_EQ (window_width * window_height, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height));
00169         }
00170       }
00171     }
00172   }
00173   
00174   integral_image1.setInput (data + 1, width, height, element_stride, row_stride);
00175   integral_image2.setInput (data + 1, width, height, element_stride, row_stride);
00176   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00177   {
00178     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00179     {
00180       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00181       {
00182         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00183         {
00184           EXPECT_EQ (window_width * window_height * 2, integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00185           EXPECT_EQ (window_width * window_height * 2, integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00186           EXPECT_EQ (window_width * window_height, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00187           EXPECT_EQ (window_width * window_height, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00188 
00189           EXPECT_EQ (window_width * window_height * 4, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height));
00190         }
00191       }
00192     }
00193   }
00194 
00195   
00196   integral_image1.setInput (data + 2, width, height, element_stride, row_stride);
00197   integral_image2.setInput (data + 2, width, height, element_stride, row_stride);
00198   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00199   {
00200     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00201     {
00202       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00203       {
00204         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00205         {
00206           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00207           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height) * 2);
00208           EXPECT_EQ (window_width * window_height, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00209           EXPECT_EQ (window_width * window_height, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00210 
00211           int w = window_width + xIdx - 1;
00212           long result = w * (w + 1) * (2*w + 1) - xIdx * (xIdx - 1) * (2*xIdx - 1);
00213           EXPECT_EQ (window_height * result, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height) * 6);
00214         }
00215       }
00216     }
00217   }
00218   delete[] data;
00219 
00220   
00221   element_stride = 3;
00222   row_stride = width * element_stride + 1; 
00223   data = new float[row_stride * height];
00224   for(unsigned yIdx = 0; yIdx < height; ++yIdx)
00225   {
00226     for(unsigned xIdx = 0; xIdx < width; ++xIdx)
00227     {
00228       
00229       if (xIdx & 1)
00230       {
00231         data[row_stride * yIdx + element_stride * xIdx]     = std::numeric_limits<float>::quiet_NaN ();;
00232         data[row_stride * yIdx + element_stride * xIdx + 1] = std::numeric_limits<float>::quiet_NaN ();;
00233         data[row_stride * yIdx + element_stride * xIdx + 2] = std::numeric_limits<float>::quiet_NaN ();;
00234       }
00235       else
00236       {
00237         data[row_stride * yIdx + element_stride * xIdx] = 3.0f;
00238         data[row_stride * yIdx + element_stride * xIdx + 1] = 2.0f;
00239         data[row_stride * yIdx + element_stride * xIdx + 2] = static_cast<float> (xIdx);
00240       }
00241     }
00242   }
00243   integral_image1.setInput (data, width, height, element_stride, row_stride);
00244   integral_image2.setInput (data, width, height, element_stride, row_stride);
00245   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00246   {
00247     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00248     {
00249       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00250       {
00251         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00252         {
00253           int count = window_height * ((window_width - (xIdx&1) + 1) >> 1);
00254 
00255           EXPECT_EQ (count * 3, integral_image1.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00256           EXPECT_EQ (count * 3, integral_image2.getFirstOrderSum (xIdx, yIdx, window_width, window_height));
00257           EXPECT_EQ (count, integral_image1.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00258           EXPECT_EQ (count, integral_image2.getFiniteElementsCount (xIdx, yIdx, window_width, window_height));
00259           EXPECT_EQ (count * 9, integral_image1.getSecondOrderSum (xIdx, yIdx, window_width, window_height));
00260         }
00261       }
00262     }
00263   }
00264   delete[] data;
00265 
00266 
00267 }
00268 
00270 TEST(PCL, IntegralImage3D)
00271 {
00272   const unsigned width = 640;
00273   const unsigned height = 480;
00274   const unsigned max_window_size = 5;
00275   const unsigned min_window_size = 1;
00276   IntegralImage2D<float, 3> integral_image3(true);
00277   unsigned element_stride = 4;
00278   unsigned row_stride = width * element_stride + 1;
00279   float* data = new float[row_stride * height];
00280   for(unsigned yIdx = 0; yIdx < height; ++yIdx)
00281   {
00282     for(unsigned xIdx = 0; xIdx < width; ++xIdx)
00283     {
00284       data[row_stride * yIdx + xIdx * element_stride] = static_cast<float> (xIdx);
00285       data[row_stride * yIdx + xIdx * element_stride + 1] = static_cast<float> (yIdx);
00286       data[row_stride * yIdx + xIdx * element_stride + 2] = static_cast<float> (xIdx + yIdx);
00287       data[row_stride * yIdx + xIdx * element_stride + 3] = -1000.0f;
00288     }
00289   }
00290   integral_image3.setInput (data, width, height, element_stride, row_stride);
00291   for (unsigned window_width = min_window_size; window_width < max_window_size; ++window_width)
00292   {
00293     for (unsigned window_height = min_window_size; window_height < max_window_size; ++window_height)
00294     {
00295       for(unsigned yIdx = 0; yIdx < height - window_height; ++yIdx)
00296       {
00297         for(unsigned xIdx = 0; xIdx < width - window_width; ++xIdx)
00298         {
00299           IntegralImage2D<float, 3>::ElementType sum = integral_image3.getFirstOrderSum (xIdx, yIdx, window_width, window_height);
00300 
00301           EXPECT_EQ (window_height * window_width * (window_width + 2 * xIdx - 1), sum[0] * 2);
00302           EXPECT_EQ (window_width * window_height * (window_height + 2 * yIdx - 1), sum[1] * 2);
00303           EXPECT_EQ (window_width * window_height * (window_height + 2 * yIdx - 1) + window_height * window_width * (window_width + 2 * xIdx - 1), sum[2] * 2);
00304 
00305           IntegralImage2D<float, 3>::SecondOrderType sumSqr = integral_image3.getSecondOrderSum (xIdx, yIdx, window_width, window_height);
00306 
00307           IntegralImage2D<float, 3>::SecondOrderType ground_truth;
00308           ground_truth.setZero ();
00309           for (unsigned wy = yIdx; wy < yIdx + window_height; ++wy)
00310           {
00311             for (unsigned wx = xIdx; wx < xIdx + window_width; ++wx)
00312             {
00313               float* val = data + (wy * row_stride + wx * element_stride);
00314               
00315               ground_truth[1] += val[0] * val[1];
00316               ground_truth[2] += val[0] * val[2];
00317               
00318               ground_truth[4] += val[1] * val[2];
00319               ground_truth[5] += val[2] * val[2];
00320             }
00321           }
00322 
00323           
00324           EXPECT_EQ (ground_truth [1], sumSqr[1]);
00325           EXPECT_EQ (ground_truth [2], sumSqr[2]);
00326           
00327           EXPECT_EQ (ground_truth [4], sumSqr[4]);
00328           EXPECT_EQ (ground_truth [5], sumSqr[5]);
00329 
00330           int w = window_width + xIdx - 1;
00331           long result = w * (w + 1) * (2*w + 1) - xIdx * (xIdx - 1) * (2*xIdx - 1);
00332 
00333           EXPECT_EQ (window_height * result, sumSqr[0] * 6);
00334 
00335           int h = window_height + yIdx - 1;
00336           result = h * (h + 1) * (2*h + 1) - yIdx * (yIdx - 1) * (2*yIdx - 1);
00337           EXPECT_EQ (window_width * result, sumSqr[3] * 6);
00338         }
00339       }
00340     }
00341   }
00342   delete[] data;
00343 }
00344 
00346 TEST (PCL, NormalEstimation)
00347 {
00348   tree.reset (new search::KdTree<PointXYZ> (false));
00349   n.setSearchMethod (tree);
00350   n.setKSearch (10);
00351 
00352   n.setInputCloud (cloud.makeShared ());
00353 
00354   PointCloud<Normal> output;
00355   n.compute (output);
00356 
00357   EXPECT_EQ (output.points.size (), cloud.points.size ());
00358   EXPECT_EQ (output.width, cloud.width);
00359   EXPECT_EQ (output.height, cloud.height);
00360 
00361   for (size_t i = 0; i < cloud.points.size (); ++i)
00362   {
00363     EXPECT_NEAR (fabs (output.points[i].normal_x),   0, 1e-2);
00364     EXPECT_NEAR (fabs (output.points[i].normal_y),   0, 1e-2);
00365     EXPECT_NEAR (fabs (output.points[i].normal_z), 1.0, 1e-2);
00366   }
00367 }
00368 
00370 TEST (PCL, IINormalEstimationCovariance)
00371 {
00372   PointCloud<Normal> output;
00373   ne.setRectSize (3, 3);
00374   ne.setNormalEstimationMethod (ne.COVARIANCE_MATRIX);
00375   ne.compute (output);
00376 
00377   EXPECT_EQ (output.points.size (), cloud.points.size ());
00378   EXPECT_EQ (output.width, cloud.width);
00379   EXPECT_EQ (output.height, cloud.height);
00380 
00381   for (size_t v = 0; v < cloud.height; ++v)
00382   {
00383     for (size_t u = 0; u < cloud.width; ++u)
00384     {
00385       if (!pcl_isfinite(output (u, v).normal_x) &&
00386           !pcl_isfinite(output (u, v).normal_y) &&
00387           !pcl_isfinite(output (u, v).normal_z))
00388         continue;
00389 
00390       EXPECT_NEAR (fabs (output (u, v).normal_x),   0, 1e-2);
00391       EXPECT_NEAR (fabs (output (u, v).normal_y),   0, 1e-2);
00392       EXPECT_NEAR (fabs (output (u, v).normal_z), 1.0, 1e-2);
00393     }
00394   }
00395 }
00396 
00398 TEST (PCL, IINormalEstimationAverage3DGradient)
00399 {
00400   PointCloud<Normal> output;
00401   ne.setRectSize (3, 3);
00402   ne.setNormalEstimationMethod (ne.AVERAGE_3D_GRADIENT);
00403   ne.compute (output);
00404 
00405   EXPECT_EQ (output.points.size (), cloud.points.size ());
00406   EXPECT_EQ (output.width, cloud.width);
00407   EXPECT_EQ (output.height, cloud.height);
00408 
00409   for (size_t v = 0; v < cloud.height; ++v)
00410   {
00411     for (size_t u = 0; u < cloud.width; ++u)
00412     {
00413       if (!pcl_isfinite(output (u, v).normal_x) &&
00414           !pcl_isfinite(output (u, v).normal_y) &&
00415           !pcl_isfinite(output (u, v).normal_z))
00416         continue;
00417 
00418       if (fabs(fabs (output (u, v).normal_z) - 1) > 1e-2)
00419       {
00420         std::cout << "T:" << u << " , " << v << " : " << output (u, v).normal_x << " , " << output (u, v).normal_y << " , " << output (u, v).normal_z <<std::endl;
00421       }
00422       EXPECT_NEAR (fabs (output (u, v).normal_x),   0, 1e-2);
00423       EXPECT_NEAR (fabs (output (u, v).normal_y),   0, 1e-2);
00424       
00425     }
00426   }
00427 }
00428 
00430 TEST (PCL, IINormalEstimationAverageDepthChange)
00431 {
00432   PointCloud<Normal> output;
00433   ne.setRectSize (3, 3);
00434   ne.setNormalEstimationMethod (ne.AVERAGE_DEPTH_CHANGE);
00435   ne.compute (output);
00436 
00437   EXPECT_EQ (output.points.size (), cloud.points.size ());
00438   EXPECT_EQ (output.width, cloud.width);
00439   EXPECT_EQ (output.height, cloud.height);
00440 
00441   for (size_t v = 0; v < cloud.height; ++v)
00442   {
00443     for (size_t u = 0; u < cloud.width; ++u)
00444     {
00445       if (!pcl_isfinite(output (u, v).normal_x) &&
00446           !pcl_isfinite(output (u, v).normal_y) &&
00447           !pcl_isfinite(output (u, v).normal_z))
00448         continue;
00449 
00450       if (fabs(fabs (output (u, v).normal_z) - 1) > 1e-2)
00451       {
00452         std::cout << "T:" << u << " , " << v << " : " << output (u, v).normal_x << " , " << output (u, v).normal_y << " , " << output (u, v).normal_z <<std::endl;
00453       }
00454       EXPECT_NEAR (fabs (output (u, v).normal_x),   0, 1e-2);
00455       EXPECT_NEAR (fabs (output (u, v).normal_y),   0, 1e-2);
00456       
00457     }
00458   }
00459 }
00460 
00462 TEST (PCL, IINormalEstimationSimple3DGradient)
00463 {
00464   PointCloud<Normal> output;
00465   ne.setRectSize (3, 3);
00466   ne.setNormalEstimationMethod (ne.SIMPLE_3D_GRADIENT);
00467   ne.compute (output);
00468 
00469   EXPECT_EQ (output.points.size (), cloud.points.size ());
00470   EXPECT_EQ (output.width, cloud.width);
00471   EXPECT_EQ (output.height, cloud.height);
00472 
00473   for (size_t v = 0; v < cloud.height; ++v)
00474   {
00475     for (size_t u = 0; u < cloud.width; ++u)
00476     {
00477       if (!pcl_isfinite(output (u, v).normal_x) &&
00478           !pcl_isfinite(output (u, v).normal_y) &&
00479           !pcl_isfinite(output (u, v).normal_z))
00480         continue;
00481 
00482       if (fabs(fabs (output (u, v).normal_z) - 1) > 1e-2)
00483       {
00484         std::cout << "T:" << u << " , " << v << " : " << output (u, v).normal_x << " , " << output (u, v).normal_y << " , " << output (u, v).normal_z <<std::endl;
00485       }
00486       EXPECT_NEAR (fabs (output (u, v).normal_x),   0, 1e-2);
00487       EXPECT_NEAR (fabs (output (u, v).normal_y),   0, 1e-2);
00488       
00489     }
00490   }
00491 }
00492 
00494 TEST (PCL, IINormalEstimationSimple3DGradientUnorganized)
00495 {
00496   PointCloud<Normal> output;
00497   cloud.height = 1;
00498   cloud.points.resize (cloud.height * cloud.width);
00499   ne.setInputCloud (cloud.makeShared ());
00500   ne.setRectSize (3, 3);
00501   ne.setNormalEstimationMethod (ne.SIMPLE_3D_GRADIENT);
00502   ne.compute (output);
00503 
00504   EXPECT_EQ (output.points.size (), 0);
00505   EXPECT_EQ (output.width, 0);
00506   EXPECT_EQ (output.height, 0);
00507 }
00508 
00509 
00510 int
00511 main (int argc, char** argv)
00512 {
00513   cloud.width = 640;
00514   cloud.height = 480;
00515   cloud.points.resize (cloud.width * cloud.height);
00516   cloud.is_dense = true;
00517   for (size_t v = 0; v < cloud.height; ++v)
00518   {
00519     for (size_t u = 0; u < cloud.width; ++u)
00520     {
00521       cloud (u, v).x = static_cast<float> (u);
00522       cloud (u, v).y = static_cast<float> (v);
00523       cloud (u, v).z = 10.0f;
00524     }
00525   }
00526 
00527   ne.setInputCloud (cloud.makeShared ());
00528   testing::InitGoogleTest (&argc, argv);
00529   return (RUN_ALL_TESTS ());
00530 
00531   return 1;
00532 }
00533 
00534