00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #ifndef RANSAC_MATCHING_H
00025 #define RANSAC_MATCHING_H
00026 #include<vcg/complex/algorithms/point_sampling.h>
00027 #include<vcg/complex/algorithms/update/color.h>
00028 #include<vcg/complex/algorithms/smooth.h>
00029 #include<vcg/space/index/kdtree/kdtree.h>
00030 #include<vcg/space/point_matching.h>
00031 namespace vcg
00032 {
00038 template <class MeshType>
00039 class BaseFeature
00040 {
00041 public:
00042 BaseFeature():_v(0) {}
00043 typename MeshType::VertexType *_v;
00044 typename MeshType::CoordType P() {return _v->cP();}
00045 };
00046
00047
00048 template <class MeshType>
00049 class BaseFeatureSet
00050 {
00051 public:
00052 typedef BaseFeature<MeshType> FeatureType;
00053 typedef typename MeshType::VertexType VertexType;
00054 typedef typename MeshType::ScalarType ScalarType;
00055
00056 class Param
00057 {
00058 public:
00059 Param()
00060 {
00061 featureSampleRatio = 0.5;
00062 }
00063
00064 ScalarType featureSampleRatio;
00065 };
00066
00067
00068 std::vector<FeatureType> fixFeatureVec;
00069 std::vector<FeatureType> movFeatureVec;
00070
00071 FeatureType &ff(int i) { return fixFeatureVec[i]; }
00072 FeatureType &mf(int i) { return movFeatureVec[i]; }
00073 int ffNum() const { return fixFeatureVec.size(); }
00074 int mfNum() const { return movFeatureVec.size(); }
00075
00076 void Init(MeshType &fix, MeshType &mov,
00077 std::vector<VertexType *> &fixSampleVec, std::vector<VertexType *> &movSampleVec,
00078 Param &fpp)
00079 {
00080 this->fixFeatureVec.resize(fixSampleVec.size()*fpp.featureSampleRatio);
00081 for(int i=0;i<fixFeatureVec.size();++i)
00082 this->fixFeatureVec[i]._v = fixSampleVec[i];
00083
00084 this->movFeatureVec.resize(movSampleVec.size()*fpp.featureSampleRatio);
00085 for(int i=0;i<movFeatureVec.size();++i)
00086 this->movFeatureVec[i]._v = movSampleVec[i];
00087
00088 printf("Generated %i Features on Fix\n",this->fixFeatureVec.size());
00089 printf("Generated %i Features on Mov\n",this->movFeatureVec.size());
00090 }
00091
00092
00093
00094
00095 void getMatchingFixFeatureVec(FeatureType &q, vector<int> &ffiVec, size_t maxMatchingFeature)
00096 {
00097 ffiVec.resize(std::min(fixFeatureVec.size(),maxMatchingFeature));
00098
00099 for(int i=0;i<ffiVec.size();++i)
00100 ffiVec[i]=i;
00101 }
00102 };
00103
00104
00105
00106 template <class MeshType>
00107 class NDFeature
00108 {
00109 public:
00110 NDFeature():_v(0) {}
00111 typename MeshType::VertexType *_v;
00112 typename MeshType::CoordType nd;
00113 typename MeshType::CoordType P() {return _v->cP();}
00114 };
00115
00116
00117 template <class MeshType>
00118 class NDFeatureSet
00119 {
00120 public:
00121 typedef NDFeature<MeshType> FeatureType;
00122 typedef typename MeshType::VertexType VertexType;
00123 typedef typename MeshType::CoordType CoordType;
00124 typedef typename MeshType::ScalarType ScalarType;
00125
00126 class Param
00127 {
00128 public:
00129 Param()
00130 {
00131 levAbs=CoordType(0,0,0);
00132 levPerc[0] = 0.01;
00133 levPerc[1] = levPerc[0]*2.0;
00134 levPerc[2] = levPerc[1]*2.0;
00135 }
00136
00137 CoordType levPerc;
00138 CoordType levAbs;
00139 };
00140
00141 std::vector<FeatureType> fixFeatureVec;
00142 std::vector<FeatureType> movFeatureVec;
00143 KdTree<ScalarType> *fixFeatureTree;
00144
00145 FeatureType &ff(int i) { return fixFeatureVec[i]; }
00146 FeatureType &mf(int i) { return movFeatureVec[i]; }
00147 int ffNum() const { return fixFeatureVec.size(); }
00148 int mfNum() const { return movFeatureVec.size(); }
00149
00150 void Init(MeshType &fix, MeshType &mov,
00151 std::vector<VertexType *> &fixSampleVec, std::vector<VertexType *> &movSampleVec, Param &pp)
00152 {
00153 ScalarType dd = std::max(fix.bbox.Diag(),mov.bbox.Diag());
00154 if(pp.levAbs == CoordType(0,0,0))
00155 pp.levAbs= pp.levPerc * dd;
00156
00157 BuildNDFeatureVector(fix,fixSampleVec,pp.levAbs,fixFeatureVec);
00158 BuildNDFeatureVector(mov,movSampleVec,pp.levAbs,movFeatureVec);
00159
00160 ConstDataWrapper<CoordType> cdw( &(fixFeatureVec[0].nd), fixFeatureVec.size(), sizeof(FeatureType));
00161 fixFeatureTree = new KdTree<ScalarType>(cdw);
00162
00163 printf("Generated %i ND Features on Fix\n",this->fixFeatureVec.size());
00164 printf("Generated %i ND Features on Mov\n",this->movFeatureVec.size());
00165 }
00166
00167
00168 static void BuildNDFeatureVector(MeshType &m, std::vector<VertexType *> &sampleVec, Point3f &distLev, std::vector<FeatureType> &featureVec )
00169 {
00170 tri::UpdateNormal<MeshType>::PerVertexNormalized(m);
00171 tri::Smooth<MeshType>::VertexNormalLaplacian(m,10);
00172
00173 VertexConstDataWrapper<MeshType > ww(m);
00174 KdTree<ScalarType> tree(ww);
00175 featureVec.resize(sampleVec.size());
00176 const Point3f sqDistLev(distLev[0]*distLev[0], distLev[1]*distLev[1], distLev[2]*distLev[2]);
00177 for(int i=0;i<sampleVec.size();++i)
00178 {
00179 featureVec[i]._v=sampleVec[i];
00180 std::vector<unsigned int> ptIndVec;
00181 std::vector<ScalarType> sqDistVec;
00182 tree.doQueryDist(sampleVec[i]->P(), distLev[2], ptIndVec, sqDistVec);
00183 Point3f varSum(0,0,0);
00184 Point3i varCnt(0,0,0);
00185
00186 for(int j=0;j<sqDistVec.size();++j)
00187 {
00188 ScalarType nDist = Distance(m.vert[i].N(),m.vert[ptIndVec[j]].N());
00189 if(sqDistVec[j]<sqDistLev[0]) {
00190 varSum[0] += nDist;
00191 ++varCnt[0];
00192 }
00193 if(sqDistVec[j]<sqDistLev[1]) {
00194 varSum[1] += nDist;
00195 ++varCnt[1];
00196 }
00197 {
00198 varSum[2] += nDist;
00199 ++varCnt[2];
00200 }
00201 }
00202 featureVec[i].nd[0] = varSum[0]/ScalarType(varCnt[0]);
00203 featureVec[i].nd[1] = varSum[1]/ScalarType(varCnt[1]);
00204 featureVec[i].nd[2] = varSum[2]/ScalarType(varCnt[2]);
00205 }
00206 }
00207
00208
00209
00210 void getMatchingFixFeatureVec(FeatureType &q, vector<int> &ffiVec, int maxNum)
00211 {
00212 ffiVec.clear();
00213 typename KdTree<ScalarType>::PriorityQueue pq;
00214 this->fixFeatureTree->doQueryK(q.nd,maxNum,pq);
00215 for(int i=0;i<pq.getNofElements();++i)
00216 {
00217 ffiVec.push_back(pq.getIndex(i));
00218 }
00219 }
00220 };
00221
00222
00239 template <class MeshType, class FeatureSetType>
00240 class RansacFramework
00241 {
00242 typedef typename FeatureSetType::FeatureType FeatureType;
00243 typedef typename FeatureSetType::Param FeatureParam;
00244
00245 typedef typename MeshType::CoordType CoordType;
00246 typedef typename MeshType::BoxType BoxType;
00247 typedef typename MeshType::ScalarType ScalarType;
00248 typedef typename MeshType::VertexType VertexType;
00249 typedef typename MeshType::VertexPointer VertexPointer;
00250 typedef typename MeshType::VertexIterator VertexIterator;
00251 typedef typename MeshType::EdgeType EdgeType;
00252 typedef typename MeshType::EdgeIterator EdgeIterator;
00253 typedef typename MeshType::FaceType FaceType;
00254 typedef typename MeshType::FacePointer FacePointer;
00255 typedef typename MeshType::FaceIterator FaceIterator;
00256 typedef typename MeshType::FaceContainer FaceContainer;
00257 typedef Matrix44<ScalarType> Matrix44Type;
00258
00259 public:
00260 class Param
00261 {
00262 public:
00263 Param()
00264 {
00265 iterMax=100;
00266 samplingRadiusPerc=0.005;
00267 samplingRadiusAbs=0;
00268 evalSize=1000;
00269 inlierRatioThr=0.3;
00270 inlierDistanceThrPerc = 1.5;
00271 congruenceThrPerc = 2.0;
00272 minFeatureDistancePerc = 4.0;
00273 maxMatchingFeatureNum = 100;
00274 areaThrPerc = 20.0;
00275
00276 }
00277
00278 ScalarType inlierRatioThr;
00279 ScalarType inlierDistanceThrPerc;
00280 ScalarType congruenceThrPerc;
00281 ScalarType minFeatureDistancePerc;
00282 ScalarType samplingRadiusPerc;
00283 ScalarType samplingRadiusAbs;
00284 ScalarType areaThrPerc;
00285 int iterMax;
00286 int evalSize;
00287 int maxMatchingFeatureNum;
00288
00289 ScalarType inlierSquareThr() const { return pow(samplingRadiusAbs* inlierDistanceThrPerc,2); }
00290 };
00291
00292 class Candidate
00293 {
00294 public:
00295 int fixInd[3];
00296 int movInd[3];
00297 int inlierNum;
00298 int evalSize;
00299 Matrix44Type Tr;
00300 ScalarType err() const {return float(inlierNum)/float(evalSize);}
00301 bool operator <(const Candidate &cc) const
00302 {
00303 return this->err() > cc.err();
00304 }
00305
00306 };
00307
00308 FeatureSetType FS;
00309 std::vector<Point3f> fixConsensusVec, movConsensusVec;
00310 KdTree<ScalarType> *consensusTree;
00311
00312
00313
00314
00315
00316
00317 bool FindPermutation(int d01, int d02, int d12, int m01, int m02, int m12, int nm[], Param &pp)
00318 {
00319 ScalarType eps = pp.samplingRadiusAbs*2.0;
00320
00321 if(fabs(d01-m01)<eps) {
00322 if(fabs(d02-m02)<eps) {
00323 if(fabs(d12-m12)<eps){ nm[0]=0;nm[1]=1;nm[2]=2; return true; }
00324 else return false;
00325 }
00326 if(fabs(d02-m12)<eps) {
00327 if(fabs(d12-m02)<eps){ nm[0]=1;nm[1]=0;nm[2]=2; return true; }
00328 else return false;
00329 }
00330 }
00331
00332 if(fabs(d01-m02)<eps) {
00333 if(fabs(d02-m01)<eps) {
00334 if(fabs(d12-m12)<eps){ nm[0]=0;nm[1]=2;nm[2]=1; return true; }
00335 else return false;
00336 }
00337 if(fabs(d02-m12)<eps) {
00338 if(fabs(d12-m01)<eps){ nm[0]=2;nm[1]=0;nm[2]=1; return true; }
00339 else return false;
00340 }
00341 }
00342
00343 if(fabs(d01-m12)<eps) {
00344 if(fabs(d02-m01)<eps) {
00345 if(fabs(d12-m02)<eps){ nm[0]=1;nm[1]=2;nm[2]=0; return true; }
00346 else return false;
00347 }
00348 if(fabs(d02-m02)<eps) {
00349 if(fabs(d12-m01)<eps){ nm[0]=2;nm[1]=1;nm[2]=0; return true; }
00350 else return false;
00351 }
00352 }
00353 return false;
00354 }
00355
00356
00357
00358
00359 void EvaluateFeature(int testSize, const char *filename, Param &pp)
00360 {
00361
00362
00363 MeshType tmpM;
00364 int neededSizeSum=0;
00365 int foundCnt=0;
00366 printf("Testing Feature size %i\n",testSize);
00367 for(int i=0;i<FS.mfNum();++i)
00368 {
00369 int neededSize = testSize;
00370 for(int j=1;j<neededSize;++j)
00371 {
00372 std::vector<int> closeFeatureVec;
00373 FS.getMatchingFixFeatureVec(FS.mf(i), closeFeatureVec, j);
00374 for(int k=0; k<closeFeatureVec.size();++k)
00375 {
00376 if(Distance(FS.mf(i).P(),FS.ff(closeFeatureVec[k]).P() )<pp.samplingRadiusAbs *3.0 )
00377 neededSize = j;
00378 }
00379 }
00380 tri::Allocator<MeshType>::AddVertex(tmpM, FS.mf(i).P());
00381 tmpM.vert.back().Q() = neededSize;
00382 neededSizeSum+=neededSize;
00383 if(neededSize<testSize) foundCnt++;
00384 }
00385
00386 tri::UpdateColor<MeshType>::PerVertexQualityRamp(tmpM);
00387 tri::io::ExporterPLY<MeshType>::Save(tmpM,filename, tri::io::Mask::IOM_VERTCOLOR + tri::io::Mask::IOM_VERTQUALITY);
00388 printf("Found %i / %i Average Needed Size %5.2f on %i\n",foundCnt,FS.mfNum(), float(neededSizeSum)/FS.mfNum(),testSize);
00389
00390 }
00391
00392
00393
00394
00395
00396 void Process_SearchEvaluateTriple (vector<Candidate> &cVec, Param &pp)
00397 {
00398 math::MarsenneTwisterRNG rnd;
00399
00400 ScalarType congruenceEps = pp.samplingRadiusAbs * pp.congruenceThrPerc;
00401 ScalarType minFeatureDistEps = pp.samplingRadiusAbs * pp.minFeatureDistancePerc;
00402 ScalarType minAreaThr = pp.samplingRadiusAbs * pp.samplingRadiusAbs *pp.areaThrPerc;
00403 printf("Starting search congruenceEps = samplingRadiusAbs * 3.0 = %6.2f \n",congruenceEps);
00404 int iterCnt=0;
00405
00406 while ( (iterCnt < pp.iterMax) && (cVec.size()<100) )
00407 {
00408 Candidate c;
00409
00410 c.movInd[0] = rnd.generate(FS.mfNum());
00411 c.movInd[1] = rnd.generate(FS.mfNum());
00412 ScalarType d01 = Distance(FS.mf(c.movInd[0]).P(),FS.mf(c.movInd[1]).P());
00413 if( d01 > minFeatureDistEps )
00414 {
00415 c.movInd[2] = rnd.generate(FS.mfNum());
00416 ScalarType d02=Distance(FS.mf(c.movInd[0]).P(),FS.mf(c.movInd[2]).P());
00417 ScalarType d12=Distance(FS.mf(c.movInd[1]).P(),FS.mf(c.movInd[2]).P());
00418 ScalarType areaTri = DoubleArea(Triangle3<ScalarType>(FS.mf(c.movInd[0]).P(), FS.mf(c.movInd[1]).P(), FS.mf(c.movInd[2]).P() ));
00419 if( ( d02 > minFeatureDistEps ) &&
00420 ( d12 > minFeatureDistEps ) &&
00421 ( areaTri > minAreaThr) &&
00422 ( fabs(d01-d02) > congruenceEps ) &&
00423 ( fabs(d01-d12) > congruenceEps ) &&
00424 ( fabs(d12-d02) > congruenceEps ) )
00425 {
00426
00427 printf("Starting search of a [%i] congruent triple for %4i %4i %4i - %6.2f %6.2f %6.2f\n",
00428 iterCnt,c.movInd[0],c.movInd[1],c.movInd[2],d01,d02,d12);
00429
00430
00431 std::vector<int> fixFeatureVec0;
00432 FS.getMatchingFixFeatureVec(FS.mf(c.movInd[0]), fixFeatureVec0,pp.maxMatchingFeatureNum);
00433 std::vector<int> fixFeatureVec1;
00434 FS.getMatchingFixFeatureVec(FS.mf(c.movInd[1]), fixFeatureVec1,pp.maxMatchingFeatureNum);
00435 std::vector<int> fixFeatureVec2;
00436 FS.getMatchingFixFeatureVec(FS.mf(c.movInd[2]), fixFeatureVec2,pp.maxMatchingFeatureNum);
00437
00438 int congrNum=0;
00439 int congrGoodNum=0;
00440 for(int i=0;i<fixFeatureVec0.size();++i)
00441 {
00442 if(cVec.size()>100) break;
00443 c.fixInd[0]=fixFeatureVec0[i];
00444 for(int j=0;j<fixFeatureVec1.size();++j)
00445 {
00446 if(cVec.size()>100) break;
00447 c.fixInd[1]=fixFeatureVec1[j];
00448 ScalarType m01 = Distance(FS.ff(c.fixInd[0]).P(),FS.ff(c.fixInd[1]).P());
00449 if( (fabs(m01-d01)<congruenceEps) )
00450 {
00451
00452 ++congrNum;
00453 for(int k=0;k<fixFeatureVec2.size();++k)
00454 {
00455 if(cVec.size()>100) break;
00456 c.fixInd[2]=fixFeatureVec2[k];
00457 ScalarType m02=Distance(FS.ff(c.fixInd[0]).P(),FS.ff(c.fixInd[2]).P());
00458 ScalarType m12=Distance(FS.ff(c.fixInd[1]).P(),FS.ff(c.fixInd[2]).P());
00459 if( (fabs(m02-d02)<congruenceEps) && (fabs(m12-d12)<congruenceEps ) )
00460 {
00461 c.Tr = GenerateMatchingMatrix(c,pp);
00462
00463 EvaluateMatrix(c,pp);
00464 if(c.err() > pp.inlierRatioThr ){
00465 printf("- - Found %lu th good congruent triple %i %i %i -- %f / %i \n", cVec.size(), c.movInd[0],c.movInd[1],c.movInd[2],c.err(),pp.evalSize);
00466
00467
00468
00469
00470 ++congrGoodNum;
00471 cVec.push_back(c);
00472 }
00473 }
00474 }
00475 }
00476 }
00477 }
00478 printf("Completed Search of congruent triple (found %i / %i good/congruent)\n",congrGoodNum,congrNum);
00479 }
00480 }
00481 ++iterCnt;
00482 }
00483
00484 printf("Found %lu candidates \n",cVec.size());
00485 sort(cVec.begin(),cVec.end());
00486 printf("best candidate %f \n",cVec[0].err());
00487
00488 pp.evalSize = FS.mfNum();
00489
00490 for(int i=0;i<cVec.size();++i)
00491 EvaluateMatrix(cVec[i],pp);
00492
00493 sort(cVec.begin(),cVec.end());
00494
00495 printf("After re-evaluation best is %f",cVec[0].err());
00496
00497
00498
00499 }
00500
00501
00512 void EvaluateMatrix(Candidate &c, Param &pp)
00513 {
00514 c.inlierNum=0;
00515 c.evalSize=pp.evalSize;
00516
00517 ScalarType sqThr = pp.inlierSquareThr();
00518 int mid = pp.evalSize/2;
00519 uint ind;
00520 ScalarType squareDist;
00521 std::vector<Point3f>::iterator pi=movConsensusVec.begin();
00522
00523 for(int j=0;j<2;++j)
00524 {
00525 for(int i=0;i<mid;++i)
00526 {
00527 Point3f qp = c.Tr*(*pi);
00528 consensusTree->doQueryClosest(qp,ind,squareDist);
00529 if(squareDist < sqThr)
00530 ++c.inlierNum;
00531 ++pi;
00532 }
00533
00534 if((j==0) && (c.inlierNum < mid/10))
00535 {
00536 c.inlierNum *=2;
00537 return;
00538 }
00539 }
00540 }
00541
00542 void DumpInlier(MeshType &m, Candidate &c, Param &pp)
00543 {
00544 ScalarType sqThr = pp.inlierSquareThr();
00545 for(int i=0;i<pp.evalSize;++i)
00546 {
00547 Point3f qp = c.Tr*movConsensusVec[i];
00548 uint ind;
00549 ScalarType squareDist;
00550 consensusTree->doQueryClosest(qp,ind,squareDist);
00551 if(squareDist < sqThr)
00552 tri::Allocator<MeshType>::AddVertex(m,qp);
00553 }
00554 }
00555
00556
00557
00558
00559
00560 Matrix44f GenerateMatchingMatrix(Candidate &c, Param pp)
00561 {
00562 std::vector<Point3f> pFix(3);
00563 pFix[0]= FS.ff(c.fixInd[0]).P();
00564 pFix[1]= FS.ff(c.fixInd[1]).P();
00565 pFix[2]= FS.ff(c.fixInd[2]).P();
00566
00567 std::vector<Point3f> pMov(3);
00568 pMov[0]= FS.mf(c.movInd[0]).P();
00569 pMov[1]= FS.mf(c.movInd[1]).P();
00570 pMov[2]= FS.mf(c.movInd[2]).P();
00571
00572 Point3f upFix = vcg::Normal(pFix[0],pFix[1],pFix[2]);
00573 Point3f upMov = vcg::Normal(pMov[0],pMov[1],pMov[2]);
00574
00575 upFix.Normalize();
00576 upMov.Normalize();
00577
00578 upFix *= Distance(pFix[0],pFix[1]);
00579 upMov *= Distance(pMov[0],pMov[1]);
00580
00581 for(int i=0;i<3;++i) pFix.push_back(pFix[i]+upFix);
00582 for(int i=0;i<3;++i) pMov.push_back(pMov[i]+upMov);
00583
00584 Matrix44f res;
00585 ComputeRigidMatchMatrix(pFix,pMov,res);
00586 return res;
00587 }
00588
00589
00590 void Init(MeshType &fixM, MeshType &movM, Param &pp, FeatureParam &fpp)
00591 {
00592 tri::UpdateNormal<MeshType>::PerVertexNormalizedPerFaceNormalized(fixM);
00593 tri::UpdateNormal<MeshType>::PerVertexNormalizedPerFaceNormalized(movM);
00594
00595
00596 typedef tri::TrivialPointerSampler<MeshType> BaseSampler;
00597 typename tri::SurfaceSampling<MeshType, BaseSampler>::PoissonDiskParam pdp;
00598 pdp.randomSeed = 0;
00599 pdp.bestSampleChoiceFlag = true;
00600 pdp.bestSamplePoolSize = 20;
00601 int t0=clock();
00602 pp.samplingRadiusAbs = pp.samplingRadiusPerc *fixM.bbox.Diag();
00603 BaseSampler pdSampler;
00604 std::vector<VertexType *> fixSampleVec;
00605 tri::SurfaceSampling<MeshType,BaseSampler>::PoissonDiskPruning(pdSampler, fixM, pp.samplingRadiusAbs,pdp);
00606 std::swap(pdSampler.sampleVec,fixSampleVec);
00607 std::vector<VertexType *> movSampleVec;
00608 tri::SurfaceSampling<MeshType,BaseSampler>::PoissonDiskPruning(pdSampler, movM, pp.samplingRadiusAbs,pdp);
00609 std::swap(pdSampler.sampleVec,movSampleVec);
00610 int t1=clock();
00611 printf("Poisson Sampling of surfaces %5.2f ( %iv and %iv) \n",float(t1-t0)/CLOCKS_PER_SEC,fixSampleVec.size(),movSampleVec.size());
00612 printf("Sampling Radius %f \n",pp.samplingRadiusAbs);
00613
00614 for(int i=0;i<fixSampleVec.size();++i)
00615 this->fixConsensusVec.push_back(fixSampleVec[i]->P());
00616
00617 for(int i=0;i<movSampleVec.size();++i)
00618 this->movConsensusVec.push_back(movSampleVec[i]->P());
00619
00620 FS.Init(fixM, movM, fixSampleVec, movSampleVec, fpp);
00621
00622 std::random_shuffle(movConsensusVec.begin(),movConsensusVec.end());
00623
00624 VectorConstDataWrapper<std::vector<CoordType> > ww(fixConsensusVec);
00625 consensusTree = new KdTree<ScalarType>(ww);
00626 }
00627
00628
00629 };
00630
00631 }
00632
00633
00634 #endif // RANSAC_MATCHING_H