/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2012 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef RANSAC_MATCHING_H #define RANSAC_MATCHING_H #include #include #include #include namespace vcg { template class BaseFeature { public: BaseFeature():_v(0) {} typename MeshType::VertexType *_v; typename MeshType::CoordType P() {return _v->cP();} }; template class BaseFeatureSet { public: typedef BaseFeature FeatureType; typedef typename MeshType::VertexType VertexType; std::vector fixFeatureVec; std::vector movFeatureVec; FeatureType &ff(int i) { return fixFeatureVec[i]; } FeatureType &mf(int i) { return movFeatureVec[i]; } int ffNum() const { return fixFeatureVec.size(); } void Init(MeshType &fix, MeshType &mov, std::vector &fixSampleVec, std::vector &movSampleVec) { this->fixFeatureVec.resize(fixSampleVec.size()/20); for(int i=0;ifixFeatureVec[i]._v = fixSampleVec[i]; this->movFeatureVec.resize(movSampleVec.size()/20); for(int i=0;imovFeatureVec[i]._v = movSampleVec[i]; printf("Generated %i Features on Fix\n",this->fixFeatureVec.size()); printf("Generated %i Features on Mov\n",this->movFeatureVec.size()); } // Returns the indexes of all the fix features matching a given one (from mov usually) void getMatchingFeatureVec(FeatureType &q, vector &mfiVec) { mfiVec.resize(movFeatureVec.size()); for(int i=0;i class NDFeature { public: typedef typename MeshType::ScalarType ScalarType; typename MeshType::VertexType *_v; typename MeshType::CoordType nd; // typename MeshType::CoordType P() {return _v->cP();} static void EvalNormalVariation(MeshType &m, ScalarType dist) { tri::UpdateNormal::PerVertexNormalized(m); VertexConstDataWrapper ww(m); KdTree tree(ww); for(int i=0;i ptIndVec; std::vector sqDistVec; tree.doQueryDist(m.vert[i].P(),dist, ptIndVec, sqDistVec); ScalarType varSum=0; for(int j=0;j::PerVertexQualityGray(m,0,0); } }; template class RansacFramework { typedef typename FeatureSetType::FeatureType FeatureType; typedef typename MeshType::CoordType CoordType; typedef typename MeshType::BoxType BoxType; typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::EdgeType EdgeType; typedef typename MeshType::EdgeIterator EdgeIterator; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::FacePointer FacePointer; typedef typename MeshType::FaceIterator FaceIterator; typedef typename MeshType::FaceContainer FaceContainer; typedef Matrix44 Matrix44Type; public: class Param { public: Param() { iterMax=100; samplingRadiusPerc=0.005; samplingRadiusAbs=0; evalSize=1000; inlierRatioThr=0.3; inlierDistanceThrPerc = 1.5; // the distance between a transformed mov sample and the corresponding on fix should be 1.5 * sampling dist. congruenceThrPerc = 2.0; // the distance between two matching features must be within 2.0 * sampling distance minFeatureDistancePerc = 4.0; // the distance between two chosen features must be at least 4.0 * sampling distance } ScalarType inlierRatioThr; ScalarType inlierDistanceThrPerc; ScalarType congruenceThrPerc; ScalarType minFeatureDistancePerc; ScalarType samplingRadiusPerc; ScalarType samplingRadiusAbs; int iterMax; int evalSize; ScalarType inlierSquareThr() const { return pow(samplingRadiusAbs* inlierDistanceThrPerc,2); } }; class Candidate { public: int fixInd[3]; int movInd[3]; int inlierNum; int evalSize; Matrix44Type Tr; ScalarType err() const {return float(inlierNum)/float(evalSize);} bool operator <(const Candidate &cc) const { return this->err() > cc.err(); } }; FeatureSetType FS; std::vector fixConsensusVec, movConsensusVec; KdTree *consensusTree; // Given three pairs of sufficiently different distances (e.g. the edges of a scalene triangle) // it finds the permutation that brings the vertexes so that the distances match. // The meaning of the permutation vector nm0,nm1,nm2 is that the (N)ew index of (M)ov vertx i is the value of nmi bool FindPermutation(int d01, int d02, int d12, int m01, int m02, int m12, int nm[], Param &pp) { ScalarType eps = pp.samplingRadiusAbs*2.0; if(fabs(d01-m01) &cVec, Param &pp) { math::MarsenneTwisterRNG rnd; // ScalarType congruenceEps = pow(pp.samplingRadiusAbs * pp.congruenceThrPerc,2.0f); ScalarType congruenceEps = pp.samplingRadiusAbs * pp.congruenceThrPerc; ScalarType minFeatureDistEps = pp.samplingRadiusAbs * pp.minFeatureDistancePerc; printf("Starting search congruenceEps = samplingRadiusAbs * 3.0 = %6.2f \n",congruenceEps); int iterCnt=0; while ( (iterCnt < pp.iterMax) && (cVec.size()<100) ) { Candidate c; // Choose a random pair of features from fix c.fixInd[0] = rnd.generate(FS.ffNum()); c.fixInd[1] = rnd.generate(FS.ffNum()); ScalarType d01 = Distance(FS.ff(c.fixInd[0]).P(),FS.ff(c.fixInd[1]).P()); if( d01 > minFeatureDistEps ) { c.fixInd[2] = rnd.generate(FS.ffNum()); ScalarType d02=Distance(FS.ff(c.fixInd[0]).P(),FS.ff(c.fixInd[2]).P()); ScalarType d12=Distance(FS.ff(c.fixInd[1]).P(),FS.ff(c.fixInd[2]).P()); if( ( d02 > minFeatureDistEps ) && // Sample are sufficiently distant ( d12 > minFeatureDistEps ) && ( fabs(d01-d02) > congruenceEps ) && // and they make a scalene triangle ( fabs(d01-d12) > congruenceEps ) && ( fabs(d12-d02) > congruenceEps ) ) { // Find a congruent triple on mov printf("Starting search of a [%i] congruent triple for %4i %4i %4i - %6.2f %6.2f %6.2f\n",iterCnt,c.fixInd[0],c.fixInd[1],c.fixInd[2],d01,d02,d12); // As a first Step we ask for three vectors of matching features; std::vector movFeatureVec0; FS.getMatchingFeatureVec(FS.ff(c.fixInd[0]), movFeatureVec0); std::vector movFeatureVec1; FS.getMatchingFeatureVec(FS.ff(c.fixInd[1]), movFeatureVec1); std::vector movFeatureVec2; FS.getMatchingFeatureVec(FS.ff(c.fixInd[2]), movFeatureVec2); int congrNum=0; int congrGoodNum=0; for(int i=0;i100) break; c.movInd[0]=movFeatureVec0[i]; for(int j=0;j100) break; c.movInd[1]=movFeatureVec1[j]; ScalarType m01 = Distance(FS.mf(c.movInd[0]).P(),FS.mf(c.movInd[1]).P()); if( (fabs(m01-d01)100) break; c.movInd[2]=movFeatureVec2[k]; ScalarType m02=Distance(FS.mf(c.movInd[0]).P(),FS.mf(c.movInd[2]).P()); ScalarType m12=Distance(FS.mf(c.movInd[1]).P(),FS.mf(c.movInd[2]).P()); if( (fabs(m02-d02) pp.inlierRatioThr ){ printf("- - Found %i 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); ++congrGoodNum; cVec.push_back(c); } } } } } } printf("Completed Search of congruent triple (found %i / %i good/congruent)\n",congrGoodNum,congrNum); } } ++iterCnt; } // end While printf("Found %i candidates \n",cVec.size()); sort(cVec.begin(),cVec.end()); printf("best candidate %f \n",cVec[0].err()); pp.evalSize = pp.evalSize*10; for(int i=0;i H; for(int i=0;idoQueryClosest(qp,ind,squareDist); if(squareDist < sqThr) ++c.inlierNum; } } int DumpInlier(MeshType &m, Candidate &c, Param &pp) { ScalarType sqThr = pp.inlierSquareThr(); for(int i=0;idoQueryClosest(qp,ind,squareDist); if(squareDist < sqThr) tri::Allocator::AddVertex(m,qp); } } // Find the transformation that matches the mov onto the fix // eg M * piMov = piFix Matrix44f GenerateMatchingMatrix(Candidate &c, Param pp) { std::vector pFix(3); pFix[0]= FS.ff(c.fixInd[0]).P(); pFix[1]= FS.ff(c.fixInd[1]).P(); pFix[2]= FS.ff(c.fixInd[2]).P(); std::vector pMov(3); pMov[0]= FS.mf(c.movInd[0]).P(); pMov[1]= FS.mf(c.movInd[1]).P(); pMov[2]= FS.mf(c.movInd[2]).P(); Point3f upFix = vcg::Normal(pFix[0],pFix[1],pFix[2]); Point3f upMov = vcg::Normal(pMov[0],pMov[1],pMov[2]); upFix.Normalize(); upMov.Normalize(); upFix *= Distance(pFix[0],pFix[1]); upMov *= Distance(pMov[0],pMov[1]); for(int i=0;i<3;++i) pFix.push_back(pFix[i]+upFix); for(int i=0;i<3;++i) pMov.push_back(pMov[i]+upMov); Matrix44f res; ComputeRigidMatchMatrix(pFix,pMov,res); return res; } void Init(MeshType &fixM, MeshType &movM, Param &pp) { // First a bit of Sampling typedef tri::TrivialPointerSampler BaseSampler; typename tri::SurfaceSampling::PoissonDiskParam pdp; pdp.randomSeed = 0; pdp.bestSampleChoiceFlag = true; pdp.bestSamplePoolSize = 20; int t0=clock(); pp.samplingRadiusAbs = pp.samplingRadiusPerc *fixM.bbox.Diag(); BaseSampler pdSampler; std::vector fixSampleVec; tri::SurfaceSampling::PoissonDiskPruning(pdSampler, fixM, pp.samplingRadiusAbs,pdp); std::swap(pdSampler.sampleVec,fixSampleVec); std::vector movSampleVec; tri::SurfaceSampling::PoissonDiskPruning(pdSampler, movM, pp.samplingRadiusAbs,pdp); std::swap(pdSampler.sampleVec,movSampleVec); int t1=clock(); printf("Poisson Sampling of surfaces %5.2f ( %iv and %iv) \n",float(t1-t0)/CLOCKS_PER_SEC,fixSampleVec.size(),movSampleVec.size()); printf("Sampling Radius %f \n",pp.samplingRadiusAbs); for(int i=0;ifixConsensusVec.push_back(fixSampleVec[i]->P()); for(int i=0;imovConsensusVec.push_back(movSampleVec[i]->P()); FS.Init(fixM, movM, fixSampleVec, movSampleVec); std::random_shuffle(movConsensusVec.begin(),movConsensusVec.end()); VectorConstDataWrapper > ww(fixConsensusVec); consensusTree = new KdTree(ww); } }; } //end namespace vcg #endif // RANSAC_MATCHING_H