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 #include "Gestalt.h"
00036
00037 #include "utils.h"
00038
00039 #include <cmath>
00040
00041
00042 #include "Eigen/QR"
00043 #include "Eigen/Eigenvalues"
00044
00045 #include "PointMatcherPrivate.h"
00046
00047 #include <vector>
00048
00050
00051
00052
00053
00054 template<typename T>
00055 GestaltDataPointsFilter<T>::GestaltDataPointsFilter(const Parameters& params):
00056 PointMatcher<T>::DataPointsFilter("GestaltDataPointsFilter",
00057 GestaltDataPointsFilter::availableParameters(), params),
00058 ratio(Parametrizable::get<T>("ratio")),
00059 radius(Parametrizable::get<T>("radius")),
00060 knn(Parametrizable::get<int>("knn")),
00061 vSizeX(Parametrizable::get<T>("vSizeX")),
00062 vSizeY(Parametrizable::get<T>("vSizeY")),
00063 vSizeZ(Parametrizable::get<T>("vSizeZ")),
00064 maxBoxDim(Parametrizable::get<T>("maxBoxDim")),
00065 maxTimeWindow(Parametrizable::get<T>("maxTimeWindow")),
00066 keepMeans(Parametrizable::get<bool>("keepMeans")),
00067 averageExistingDescriptors(Parametrizable::get<bool>("averageExistingDescriptors")),
00068 keepNormals(Parametrizable::get<bool>("keepNormals")),
00069 keepEigenValues(Parametrizable::get<bool>("keepEigenValues")),
00070 keepEigenVectors(Parametrizable::get<bool>("keepEigenVectors")),
00071 keepCovariances(Parametrizable::get<bool>("keepCovariances")),
00072 keepGestaltFeatures(Parametrizable::get<bool>("keepGestaltFeatures"))
00073 {
00074 }
00075
00076
00077 template<typename T>
00078 typename PointMatcher<T>::DataPoints
00079 GestaltDataPointsFilter<T>::filter(const DataPoints& input)
00080 {
00081 DataPoints output(input);
00082 inPlaceFilter(output);
00083 return output;
00084 }
00085
00086
00087 template<typename T>
00088 void GestaltDataPointsFilter<T>::inPlaceFilter(DataPoints& cloud)
00089 {
00090 typedef typename DataPoints::View View;
00091 typedef typename DataPoints::Label Label;
00092 typedef typename DataPoints::Labels Labels;
00093 typedef typename DataPoints::TimeView TimeView;
00094
00095 const int pointsCount(cloud.features.cols());
00096 const int featDim(cloud.features.rows());
00097 const int descDim(cloud.descriptors.rows());
00098 const unsigned int labelDim(cloud.descriptorLabels.size());
00099
00100 int insertDim(0);
00101 if (averageExistingDescriptors)
00102 {
00103
00104
00105 for(unsigned int i = 0; i < labelDim; ++i)
00106 insertDim += cloud.descriptorLabels[i].span;
00107 if (insertDim != descDim)
00108 throw InvalidField("GestaltDataPointsFilter: Error, descriptor labels do not match descriptor data");
00109 }
00110
00111
00112 const int dimNormals(featDim-1);
00113 const int dimMeans(featDim-1);
00114 const int dimEigValues(featDim-1);
00115 const int dimEigVectors((featDim-1)*(featDim-1));
00116 const int dimCovariances((featDim-1)*(featDim-1));
00117 const int dimGestalt = 32;
00118
00119
00120 Labels cloudLabels, timeLabels;
00121
00122 if (keepNormals)
00123 cloudLabels.push_back(Label("normals", dimNormals));
00124 if (keepMeans)
00125 cloudLabels.push_back(Label("means", dimMeans));
00126 if (keepEigenValues)
00127 cloudLabels.push_back(Label("eigValues", dimEigValues));
00128 if (keepEigenVectors)
00129 cloudLabels.push_back(Label("eigVectors", dimEigVectors));
00130 if (keepCovariances)
00131 cloudLabels.push_back(Label("covariance", dimCovariances));
00132 if (keepGestaltFeatures)
00133 {
00134 cloudLabels.push_back(Label("gestaltMeans", dimGestalt));
00135 cloudLabels.push_back(Label("gestaltVariances", dimGestalt));
00136 cloudLabels.push_back(Label("warpedXYZ", 3));
00137 cloudLabels.push_back(Label("gestaltShapes", 2));
00138 }
00139 timeLabels.push_back(Label("time", 3));
00140
00141 cloud.allocateDescriptors(cloudLabels);
00142 cloud.allocateTimes(timeLabels);
00143
00144
00145 View cloudExistingDescriptors(cloud.descriptors.block(0,0,cloud.descriptors.rows(),cloud.descriptors.cols()));
00146 TimeView cloudExistingTimes(cloud.times.block(0,0,cloud.times.rows(),cloud.times.cols()));
00147 BuildData buildData(cloud.features, cloud.descriptors, cloud.times);
00148
00149
00150 if (keepNormals)
00151 buildData.normals = cloud.getDescriptorViewByName("normals");
00152 if(keepMeans)
00153 buildData.means = cloud.getDescriptorViewByName("means");
00154 if (keepEigenValues)
00155 buildData.eigenValues = cloud.getDescriptorViewByName("eigValues");
00156 if (keepEigenVectors)
00157 buildData.eigenVectors = cloud.getDescriptorViewByName("eigVectors");
00158 if (keepCovariances)
00159 buildData.covariance = cloud.getDescriptorViewByName("covariance");
00160 if (keepGestaltFeatures)
00161 {
00162 buildData.gestaltMeans = cloud.getDescriptorViewByName("gestaltMeans");
00163 buildData.gestaltVariances = cloud.getDescriptorViewByName("gestaltVariances");
00164 buildData.warpedXYZ = cloud.getDescriptorViewByName("warpedXYZ");
00165 buildData.gestaltShapes = cloud.getDescriptorViewByName("gestaltShapes");
00166 }
00167
00168 buildNew(
00169 buildData,
00170 0,
00171 pointsCount,
00172 cloud.features.rowwise().minCoeff(),
00173 cloud.features.rowwise().maxCoeff()
00174 );
00175
00176
00177 fuseRange(buildData, cloud, 0, pointsCount);
00178
00179
00180
00181 std::sort(buildData.indicesToKeep.begin(), buildData.indicesToKeep.end());
00182 const int ptsOut = buildData.indicesToKeep.size();
00183 for (int i = 0; i < ptsOut; ++i)
00184 {
00185 const int k = buildData.indicesToKeep[i];
00186 assert(i <= k);
00187 cloud.features.col(i) = cloud.features.col(k);
00188 cloud.times.col(i) = cloud.times.col(k);
00189 if (cloud.descriptors.rows() != 0)
00190 cloud.descriptors.col(i) = cloud.descriptors.col(k);
00191 if(keepNormals)
00192 buildData.normals->col(i) = buildData.normals->col(k);
00193 if(keepMeans)
00194 buildData.means->col(i) = buildData.means->col(k);
00195 if(keepEigenValues)
00196 buildData.eigenValues->col(i) = buildData.eigenValues->col(k);
00197 if(keepEigenVectors)
00198 buildData.eigenVectors->col(i) = buildData.eigenVectors->col(k);
00199 if(keepCovariances)
00200 buildData.covariance->col(i) = buildData.covariance->col(k);
00201 if(keepGestaltFeatures)
00202 {
00203 buildData.gestaltMeans->col(i) = buildData.gestaltMeans->col(k);
00204 buildData.gestaltVariances->col(i) = buildData.gestaltVariances->col(k);
00205 buildData.warpedXYZ->col(i) = buildData.warpedXYZ->col(k);
00206 buildData.gestaltShapes->col(i) = buildData.gestaltShapes->col(k);
00207 }
00208 }
00209 cloud.features.conservativeResize(Eigen::NoChange, ptsOut);
00210 cloud.descriptors.conservativeResize(Eigen::NoChange, ptsOut);
00211 cloud.times.conservativeResize(Eigen::NoChange, ptsOut);
00212
00213 if(buildData.unfitPointsCount != 0)
00214 LOG_INFO_STREAM(" GestaltDataPointsFilter - Could not compute normal for " << buildData.unfitPointsCount << " pts.");
00215 }
00216
00217 #include "VoxelGrid.h"
00218 template<typename T>
00219 void GestaltDataPointsFilter<T>::buildNew(
00220 BuildData& data, const int first, const int last,
00221 Vector&& minValues, Vector&& maxValues) const
00222 {
00223 using Voxel = typename VoxelGridDataPointsFilter<T>::Voxel;
00224
00225 const T minBoundX = minValues.x() / vSizeX;
00226 const T maxBoundX = maxValues.x() / vSizeX;
00227 const T minBoundY = minValues.y() / vSizeY;
00228 const T maxBoundY = maxValues.y() / vSizeY;
00229 const T minBoundZ = minValues.z() / vSizeZ;
00230 const T maxBoundZ = maxValues.z() / vSizeZ;
00231
00232
00233
00234 const unsigned int numDivX = 1 + maxBoundX - minBoundX;
00235 const unsigned int numDivY = 1 + maxBoundY - minBoundY;;
00236 const unsigned int numDivZ = 1 + maxBoundZ - minBoundZ;
00237 const unsigned int numVox = numDivX * numDivY * numDivZ;
00238
00239
00240
00241
00242
00243
00244 const int numPoints = last - first;
00245 std::vector<unsigned int> indices(numPoints);
00246
00247
00248
00249
00250 std::vector<Voxel> voxels;
00251
00252
00253 try
00254 {
00255 voxels = std::vector<Voxel>(numVox);
00256 }
00257 catch (std::bad_alloc&)
00258 {
00259 throw InvalidParameter((boost::format("GestaltDataPointsFilter: Memory allocation error with %1% voxels. Try increasing the voxel dimensions.") % numVox).str());
00260 }
00261
00262 const int featDim(data.features.rows());
00263
00264 for (int p = 0; p < numPoints; ++p)
00265 {
00266 const unsigned int i = floor(data.features(0,p)/vSizeX - minBoundX);
00267 const unsigned int j = floor(data.features(1,p)/vSizeY- minBoundY);
00268 unsigned int k = 0;
00269 unsigned int idx;
00270 if ( featDim == 4 )
00271 {
00272 k = floor(data.features(2,p)/vSizeZ - minBoundZ);
00273 idx = i + j * numDivX + k * numDivX * numDivY;
00274 }
00275 else
00276 {
00277 idx = i + j * numDivX;
00278 }
00279
00280 const unsigned int pointsInVox = voxels[idx].numPoints + 1;
00281
00282 if (pointsInVox == 1)
00283 {
00284 voxels[idx].firstPoint = p;
00285 }
00286
00287 voxels[idx].numPoints = pointsInVox;
00288
00289 indices[p] = idx;
00290
00291 }
00292
00293
00294 std::vector<unsigned int> pointsToKeep;
00295
00296
00297
00298 for (int p = 0; p < numPoints ; ++p)
00299 {
00300 const unsigned int idx = indices[p];
00301 const unsigned int firstPoint = voxels[idx].firstPoint;
00302
00303
00304 const int randomIndex = std::rand() % numPoints;
00305 for (int f = 0; f < (featDim - 1); ++f)
00306 {
00307 data.features(f,firstPoint) = data.features(f,randomIndex);
00308 }
00309 }
00310
00311 for (unsigned int idx = 0; idx < numVox; ++idx)
00312 {
00313 const unsigned int numPoints = voxels[idx].numPoints;
00314 const unsigned int firstPoint = voxels[idx].firstPoint;
00315
00316 if (numPoints > 0)
00317 {
00318
00319
00320
00321
00322 pointsToKeep.push_back(firstPoint);
00323 }
00324 }
00325
00326 const unsigned int nbPointsToKeep(pointsToKeep.size());
00327
00328
00329 for(unsigned int i=0; i<nbPointsToKeep; ++i)
00330 {
00331 const float r = (float)std::rand()/(float)RAND_MAX;
00332 if(r < ratio)
00333 {
00334
00335 const int k = pointsToKeep[i];
00336
00337 data.indicesToKeep.push_back(k);
00338 }
00339 }
00340 }
00341
00342 template<typename T>
00343 void GestaltDataPointsFilter<T>::fuseRange(
00344 BuildData& data, DataPoints& input, const int first, const int last) const
00345 {
00346 using namespace PointMatcherSupport;
00347
00348 typedef typename Eigen::Matrix<std::int64_t, Eigen::Dynamic, Eigen::Dynamic> Int64Matrix;
00349
00350 const unsigned int nbIdxToKeep(data.indicesToKeep.size());
00351 const int inputFeatDim(input.features.cols());
00352
00353 std::vector<int> indicesToKeepStrict;
00354 for (unsigned int i = 0; i < nbIdxToKeep ; ++i)
00355 {
00356 Eigen::Matrix<T,3,1> keyPoint;
00357 keyPoint = input.features.col(data.indicesToKeep[i]);
00358
00359
00360 const T minBoundX = keyPoint(0,0) - radius;
00361 const T maxBoundX = keyPoint(0,0) + radius;
00362 const T minBoundY = keyPoint(1,0) - radius;
00363 const T maxBoundY = keyPoint(1,0) + radius;
00364 const T minBoundZ = keyPoint(2,0) - radius;
00365 const T maxBoundZ = keyPoint(2,0) + radius;
00366
00367 Eigen::Matrix<T,3,1> feature;
00368 std::vector<int> goodIndices;
00369 for (int j = 0; j < inputFeatDim; ++j)
00370 {
00371 feature = input.features.col(j);
00372 if(feature(0,0) <= maxBoundX && feature(0,0) >= minBoundX &&
00373 feature(1,0) <= maxBoundY && feature(1,0) >= minBoundY &&
00374 feature(2,0) <= maxBoundZ && feature(2,0) >= minBoundZ &&
00375 keyPoint != feature)
00376 {
00377 goodIndices.push_back(j);
00378 }
00379 }
00380 const int colCount = goodIndices.size();
00381
00382 if (colCount == 0)
00383 {
00384 ++(data.unfitPointsCount);
00385 continue;
00386 }
00387
00388 const int featDim(data.features.rows());
00389
00390 Matrix d(featDim-1, colCount);
00391 Int64Matrix t(1, colCount);
00392
00393 for (int j = 0; j < colCount; ++j)
00394 {
00395 d.col(j) = data.features.block(0,data.indices[goodIndices[j]],featDim-1, 1);
00396 t.col(j) = data.times.col(data.indices[goodIndices[j]]);
00397 }
00398
00399 const Vector mean = d.rowwise().sum() / T(colCount);
00400 const Matrix NN = d.colwise() - mean;
00401 const std::int64_t minTime = t.minCoeff();
00402 const std::int64_t maxTime = t.maxCoeff();
00403 const std::int64_t meanTime = t.sum() / T(colCount);
00404
00405 const Matrix C(NN * NN.transpose());
00406 Vector eigenVa = Vector::Identity(featDim-1, 1);
00407 Matrix eigenVe = Matrix::Identity(featDim-1, featDim-1);
00408
00409 if(keepNormals || keepEigenValues || keepEigenVectors || keepCovariances || keepGestaltFeatures)
00410 {
00411 if(C.fullPivHouseholderQr().rank()+1 >= featDim-1)
00412 {
00413 const Eigen::EigenSolver<Matrix> solver(C);
00414 eigenVa = solver.eigenvalues().real();
00415 eigenVe = solver.eigenvectors().real();
00416 }
00417 else
00418 {
00419 data.unfitPointsCount += colCount;
00420 continue;
00421 }
00422 }
00423 Eigen::Matrix<T,3,1> normal, newX, newY;
00424 Eigen::Matrix<T,3,3> newBasis;
00425 double planarity = 0.;
00426 double cylindricality = 0.;
00427
00428 if(keepNormals || keepGestaltFeatures)
00429 {
00430
00431 normal = computeNormal<T>(eigenVa, eigenVe);
00432
00433 if(keepGestaltFeatures)
00434 {
00435 Vector eigenVaSort = sortEigenValues<T>(eigenVa);
00436 planarity = 2 * (eigenVaSort(1) - eigenVaSort(0))/eigenVaSort.sum();
00437 cylindricality = (eigenVaSort(2) - eigenVaSort(1))/eigenVaSort.sum();
00438
00439 Eigen::Matrix<T,3,1> up, base;
00440 up << 0,0,1;
00441 base << 1,0,0;
00442 newX << normal(0), normal(1), 0;
00443 newX.normalize();
00444 newY = up.cross(newX);
00445 newY = newY / newY.norm();
00446
00447 newBasis << newX(0), newY(0), up(0),
00448 newX(1), newY(1), up(1),
00449 newX(2), newY(2), up(2);
00450
00451
00452 if(planarity > 0.9)
00453 {
00454 data.unfitPointsCount += colCount;
00455 continue;
00456 }
00457
00458 if(acos(normal.dot(up)) < abs(10 * M_PI/180))
00459 {
00460 data.unfitPointsCount += colCount;
00461 continue;
00462 }
00463
00464
00465 for (int j = 0; j < colCount; ++j)
00466 {
00467 data.warpedXYZ->col(j) = ((data.features.block(0,j,3,1) - keyPoint).transpose() * newBasis).transpose();
00468 }
00469 }
00470 }
00471 Vector angles(colCount), radii(colCount), heights(colCount);
00472 Matrix gestaltMeans(4, 8), gestaltVariances(4, 8), numOfValues(4, 8);
00473 if(keepGestaltFeatures)
00474 {
00475
00476 angles = GestaltDataPointsFilter::calculateAngles(*data.warpedXYZ, keyPoint);
00477 radii = GestaltDataPointsFilter::calculateRadii(*data.warpedXYZ, keyPoint);
00478 heights = data.warpedXYZ->row(2);
00479
00480
00481 const T angularBinWidth = M_PI/4;
00482 const T radialBinWidth = radius/4;
00483 Matrix indices(2, colCount);
00484 gestaltMeans = Matrix::Zero(4, 8);
00485 gestaltVariances = Matrix::Zero(4, 8);
00486 numOfValues = Matrix::Zero(4, 8);
00487
00488 for (int it=0; it < colCount; ++it)
00489 {
00490 indices(0,it) = floor(radii(it)/radialBinWidth);
00491
00492 if(indices(0,it) > 3)
00493
00494 indices(0,it) = 3;
00495 indices(1,it) = floor(angles(it)/angularBinWidth);
00496 if(indices(1,it) > 7)
00497 indices(1,it) = 7;
00498 gestaltMeans(indices(0,it), indices(1,it)) += heights(it);
00499 ++(numOfValues(indices(0,it), indices(1,it)));
00500 }
00501
00502 for (int radial=0; radial < 4; ++radial)
00503 {
00504 for (int angular = 0; angular < 8; ++angular)
00505 {
00506 if (numOfValues(radial, angular) > 0)
00507 {
00508 gestaltMeans(radial, angular) = gestaltMeans(radial, angular)/numOfValues(radial, angular);
00509 }
00510 }
00511 }
00512 for (int it=0; it < colCount; ++it)
00513 {
00514 gestaltVariances(indices(0,it), indices(1,it)) += (heights(it)-gestaltMeans(indices(0,it), indices(1,it))) * (heights(it)-gestaltMeans(indices(0,it), indices(1,it)));
00515 }
00516 for (int radial=0; radial < 4; ++radial)
00517 {
00518 for (int angular = 0; angular < 8; ++angular)
00519 {
00520
00521 if (gestaltMeans(radial,angular) == 0 && radial > 0)
00522 {
00523 gestaltMeans(radial, angular) = gestaltMeans(radial-1, angular);
00524 gestaltVariances(radial, angular) = gestaltVariances(radial-1, angular);
00525 }
00526 else if (numOfValues(radial, angular) > 0)
00527 {
00528 gestaltVariances(radial, angular) = gestaltVariances(radial, angular)/numOfValues(radial, angular);
00529 }
00530 }
00531 }
00532 }
00533
00534 Vector serialEigVector;
00535 if(keepEigenVectors)
00536 serialEigVector = serializeEigVec<T>(eigenVe);
00537 Vector serialCovVector;
00538 if(keepCovariances)
00539 serialCovVector = serializeEigVec<T>(C);
00540 Vector serialGestaltMeans;
00541 Vector serialGestaltVariances;
00542 if(keepGestaltFeatures)
00543 {
00544 serialGestaltMeans = GestaltDataPointsFilter::serializeGestaltMatrix(gestaltMeans);
00545 serialGestaltVariances = GestaltDataPointsFilter::serializeGestaltMatrix(gestaltVariances);
00546 }
00547
00548 if(data.descriptors.rows() != 0)
00549 assert(data.descriptors.cols() != 0);
00550
00551
00552 data.times(0, data.indicesToKeep[i]) = minTime;
00553 data.times(1, data.indicesToKeep[i]) = maxTime;
00554 data.times(2, data.indicesToKeep[i]) = meanTime;
00555
00556
00557 if(keepNormals)
00558 data.normals->col(data.indicesToKeep[i]) = normal;
00559 if(keepMeans)
00560 data.means->col(data.indicesToKeep[i]) = mean;
00561 if(keepEigenValues)
00562 data.eigenValues->col(data.indicesToKeep[i]) = eigenVa;
00563 if(keepEigenVectors)
00564 data.eigenVectors->col(data.indicesToKeep[i]) = serialEigVector;
00565 if(keepCovariances)
00566 data.covariance->col(data.indicesToKeep[i]) = serialCovVector;
00567 if(keepGestaltFeatures)
00568 {
00569
00570 data.gestaltMeans->col(data.indicesToKeep[i]) = serialGestaltMeans;
00571 data.gestaltVariances->col(data.indicesToKeep[i]) = serialGestaltVariances;
00572 (*data.gestaltShapes)(0,data.indicesToKeep[i]) = planarity;
00573 (*data.gestaltShapes)(1,data.indicesToKeep[i]) = cylindricality;
00574 }
00575
00576 indicesToKeepStrict.push_back(data.indicesToKeep[i]);
00577 }
00578 data.indicesToKeep = indicesToKeepStrict;
00579 }
00580
00581 template<typename T>
00582 typename PointMatcher<T>::Vector
00583 GestaltDataPointsFilter<T>::serializeGestaltMatrix(const Matrix& gestaltFeatures) const
00584 {
00585
00586 const int dim = gestaltFeatures.rows() * gestaltFeatures.cols();
00587 Vector output(dim);
00588 for(int k=0; k < gestaltFeatures.rows(); ++k)
00589 {
00590 output.segment(k*gestaltFeatures.cols(), gestaltFeatures.cols()) =
00591 gestaltFeatures.row(k).transpose();
00592 }
00593 return output;
00594 }
00595
00596 template<typename T>
00597 typename PointMatcher<T>::Vector
00598 GestaltDataPointsFilter<T>::calculateAngles(
00599 const Matrix& points, const Eigen::Matrix<T,3,1>& keyPoint) const
00600 {
00601 const unsigned int dim(points.cols());
00602 Vector angles(dim);
00603
00604 for (unsigned int i = 0; i < dim; ++i)
00605 {
00606 angles(i) = atan2(points(0,i), points(1,i));
00607 if (angles(i) < 0)
00608 angles(i) += (2 * M_PI);
00609 }
00610
00611 return angles;
00612 }
00613
00614 template<typename T>
00615 typename PointMatcher<T>::Vector
00616 GestaltDataPointsFilter<T>::calculateRadii(
00617 const Matrix& points, const Eigen::Matrix<T,3,1>& keyPoint) const
00618 {
00619 const unsigned int dim(points.cols());
00620 Vector radii(dim);
00621
00622 for (unsigned int i = 0; i < dim; ++i)
00623 {
00624 radii(i) = sqrt((points(0,i)) * (points(0,i)) + (points(1,i)) * (points(1,i)));
00625 }
00626 return radii;
00627 }
00628
00629 template struct GestaltDataPointsFilter<float>;
00630 template struct GestaltDataPointsFilter<double>;