00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifndef _AUTOALIGN_4PCS_H_
00024 #define _AUTOALIGN_4PCS_H_
00025
00034 #include <vcg/complex/complex.h>
00035 #include <vcg/space/point_matching.h>
00036 #include <vcg/complex/algorithms/closest.h>
00037 #include <vcg/complex/algorithms/point_sampling.h>
00038 #include <vcg/math/random_generator.h>
00039 #include <ctime>
00040 namespace vcg{
00041 namespace tri{
00042
00043 template <class MeshType>
00044 class FourPCS {
00045 public:
00046
00047 class PVertex;
00048 class PFace;
00049
00050 class PUsedTypes: public vcg::UsedTypes < vcg::Use<PVertex>::template AsVertexType,
00051 vcg::Use<PFace >::template AsFaceType >{};
00052
00053 class PVertex : public vcg::Vertex< PUsedTypes,vcg::vertex::BitFlags,vcg::vertex::Coord3f,vcg::vertex::Mark>{};
00054 class PFace : public vcg::Face< PUsedTypes> {};
00055 class PMesh : public vcg::tri::TriMesh< std::vector<PVertex>, std::vector<PFace> > {};
00056
00057 typedef typename MeshType::ScalarType ScalarType;
00058 typedef typename MeshType::CoordType CoordType;
00059 typedef typename vcg::Matrix44<ScalarType> Matrix44x;
00060 typedef typename vcg::Box3<ScalarType> Box3x;
00061
00062 typedef typename MeshType::VertexIterator VertexIterator;
00063 typedef typename MeshType::VertexPointer VertexPointer;
00064 typedef typename MeshType::VertexType VertexType;
00065 typedef vcg::Point4< vcg::Point3<ScalarType> > FourPoints;
00066 typedef vcg::GridStaticPtr<typename PMesh::VertexType, ScalarType > GridType;
00067
00068
00069 struct Param
00070 {
00071 ScalarType overlap;
00072
00073 int sampleNumP;
00074 float samplingRadius;
00075
00076 ScalarType deltaPerc;
00077 ScalarType deltaAbs;
00078 int feetSize;
00079 int scoreFeet;
00080 ScalarType cosAngle;
00081 int seed;
00082
00083 void Default(){
00084 overlap = 0.5;
00085 sampleNumP=500;
00086 samplingRadius=0;
00087
00088 deltaPerc = 0.5;
00089 deltaAbs = 0;
00090 feetSize = 25;
00091 scoreFeet = 50;
00092 seed =0;
00093 cosAngle = 0;
00094 }
00095 };
00096
00097 struct Stat
00098 {
00099 Stat() : initTime(0),selectCoplanarBaseTime(0),findCongruentTime(0),testAlignmentTime(0)
00100 {}
00101 clock_t initTime;
00102 clock_t selectCoplanarBaseTime;
00103 clock_t findCongruentTime;
00104 clock_t testAlignmentTime;
00105 float init() {return 1000.0f*float(initTime)/float(CLOCKS_PER_SEC);}
00106 float select() {return 1000.0f*float(selectCoplanarBaseTime)/float(CLOCKS_PER_SEC);}
00107 float findCongruent() {return 1000.0f*float(findCongruentTime)/float(CLOCKS_PER_SEC);}
00108 float testAlignment() {return 1000.0f*float(testAlignmentTime)/float(CLOCKS_PER_SEC);}
00109 };
00110
00111 class Couple
00112 {
00113 public:
00114 VertexPointer p0,p1;
00115 Couple(VertexPointer i, VertexPointer j, float d) : p0(i),p1(j),dist(d){}
00116 float dist;
00117 bool operator < (const Couple & o) const {return dist < o.dist;}
00118 VertexPointer operator[](const int &i) const {return (i==0)? this->p0 : this->p1;}
00119 };
00120
00121 struct Candidate
00122 {
00123 Candidate():score(0){}
00124 Candidate(FourPoints _p, vcg::Matrix44<ScalarType>_T):p(_p),T(_T){}
00125 FourPoints p;
00126 vcg::Matrix44<ScalarType> T;
00127 int score;
00128 inline bool operator <(const Candidate & o) const {return score > o.score;}
00129 };
00130
00131
00132
00133 struct EPoint{
00134 EPoint(vcg::Point3<ScalarType> _p, int _i):pos(_p),pi(_i){}
00135 vcg::Point3<ScalarType> pos;
00136 int pi;
00137 void GetBBox(vcg::Box3<ScalarType> & b){b.Add(pos);}
00138 };
00139
00140
00141 Param par;
00142 Stat stat;
00143
00144 MeshType *P;
00145 MeshType *Q;
00146
00147 math::MarsenneTwisterRNG rnd;
00148
00149 std::vector<VertexPointer> subsetQ;
00150 std::vector<VertexPointer> subsetP;
00151
00152 ScalarType side;
00153
00154 PMesh Invr;
00155 std::vector< Candidate > U;
00156 int iwinner;
00157 std::vector<FourPoints> bases;
00158 std::vector<VertexType*> ExtB[4];
00159
00160 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridQ;
00161 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridP;
00162
00163
00164 void IntersectionLineLine(const CoordType & x1,const CoordType & x2,const CoordType & x3,const CoordType & x4, CoordType&x)
00165 {
00166 CoordType a = x2-x1, b = x4-x3, c = x3-x1;
00167 x = x1 + a * ((c^b).dot(a^b)) / (a^b).SquaredNorm();
00168 }
00169
00170
00171 void Init(MeshType &_movP,MeshType &_fixQ)
00172 {
00173 clock_t t0= clock();
00174 P = &_movP;
00175 Q = &_fixQ;
00176 tri::UpdateBounding<MeshType>::Box(*P);
00177 if(par.seed==0) rnd.initialize(time(0));
00178 else rnd.initialize(par.seed);
00179
00180 ugridQ.Set(Q->vert.begin(),Q->vert.end());
00181 ugridP.Set(P->vert.begin(),P->vert.end());
00182
00183 if(par.samplingRadius==0)
00184 par.samplingRadius = tri::ComputePoissonDiskRadius(*P,par.sampleNumP);
00185 tri::PoissonPruning(*P, subsetP, par.samplingRadius, par.seed);
00186 tri::PoissonPruning(*Q, subsetQ, par.samplingRadius, par.seed);
00187 par.deltaAbs = par.samplingRadius * par.deltaPerc;
00188 side = P->bbox.Dim()[P->bbox.MaxDim()]*par.overlap;
00189 stat.initTime+=clock()-t0;
00190 }
00191
00192
00193
00194
00195 bool SelectCoplanarBase(FourPoints &B, ScalarType &r1, ScalarType &r2)
00196 {
00197 clock_t t0= clock();
00198
00199
00200 ScalarType dtol = side*0.1;
00201
00202
00203
00204 B[0] = P->vert[ rnd.generate(P->vert.size())].P();
00205
00206
00207 size_t i;
00208 for(i = 0; i < P->vert.size(); ++i){
00209 int id = rnd.generate(P->vert.size());
00210 ScalarType dd = (P->vert[id].P() - B[0]).Norm();
00211 if( ( dd < side + dtol) && (dd > side - dtol)){
00212 B[1] = P->vert[id].P();
00213 break;
00214 }
00215 }
00216 if(i == P->vert.size()) return false;
00217
00218
00219 const CoordType middle = (B[0]+B[1])/2.0;
00220 for(i = 0; i < P->vert.size(); ++i){
00221 int id = rnd.generate(P->vert.size());
00222 if( Distance(P->vert[id].P(),middle) < side*0.8 ){
00223 B[2] = P->vert[id].P();
00224 break;
00225 }
00226 }
00227 if(i == P->vert.size()) return false;
00228
00229
00230 ScalarType cpr = rnd.generate01();
00231 CoordType crossP = B[0] *(1-cpr)+B[1]*cpr;
00232 CoordType B4 = B[2]+(crossP-B[2]).Normalize()*side;
00233 CoordType n = ((B[0]-B[1]).normalized() ^ (B[2]-B[1]).normalized()).normalized();
00234 ScalarType radius = dtol;
00235
00236 std::vector<typename MeshType::VertexType*> closests;
00237 std::vector<ScalarType> distances;
00238 std::vector<CoordType> points;
00239
00240 vcg::tri::GetInSphereVertex<
00241 MeshType,
00242 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
00243 std::vector<typename MeshType::VertexType*>,
00244 std::vector<ScalarType>,
00245 std::vector<CoordType>
00246 >(*P,ugridP,B4,radius,closests,distances,points);
00247
00248 if(closests.empty())
00249 return false;
00250 int bestInd = -1; ScalarType bestv=std::numeric_limits<float>::max();
00251 for(i = 0; i <closests.size(); ++i){
00252 ScalarType dist_from_plane = fabs((closests[i]->P() - B[1]).normalized().dot(n));
00253 if( dist_from_plane < bestv){
00254 bestv = dist_from_plane;
00255 bestInd = i;
00256 }
00257 }
00258 if(bestv >dtol)
00259 return false;
00260 B[3] = closests[bestInd]->P();
00261
00262
00263
00264
00265 CoordType x;
00266
00267 IntersectionLineLine(B[0],B[1],B[2],B[3],x);
00268
00269 r1 = (x - B[0]).dot(B[1]-B[0]) / (B[1]-B[0]).SquaredNorm();
00270 r2 = (x - B[2]).dot(B[3]-B[2]) / (B[3]-B[2]).SquaredNorm();
00271
00272 if( ((B[0]+(B[1]-B[0])*r1)-(B[2]+(B[3]-B[2])*r2)).Norm() > par.deltaAbs )
00273 return false;
00274
00275 radius = side*0.5;
00276 std::vector< CoordType > samples;
00277 std::vector<ScalarType > dists;
00278
00279 for(int i = 0 ; i< 4; ++i){
00280 vcg::tri::GetKClosestVertex<
00281 MeshType,
00282 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
00283 std::vector<VertexType*>,
00284 std::vector<ScalarType>,
00285 std::vector< CoordType > >(*P,ugridP, par.feetSize ,B[i],radius, ExtB[i], dists, samples);
00286 }
00287
00288
00289 stat.selectCoplanarBaseTime+=clock()-t0;
00290 return true;
00291 }
00292
00293 bool IsTransfCongruent(const FourPoints &B, const FourPoints &fp, vcg::Matrix44<ScalarType> & mat)
00294 {
00295 std::vector<vcg::Point3<ScalarType> > fix(4);
00296 std::vector<vcg::Point3<ScalarType> > mov(4);
00297 for(int i = 0 ; i < 4; ++i) {
00298 mov[i]=B[i];
00299 fix[i]=fp[i];
00300 }
00301
00302 if(fabs( Distance(fix[0],fix[1]) - Distance(mov[0],mov[1]) ) > par.deltaAbs) return false;
00303 if(fabs( Distance(fix[0],fix[2]) - Distance(mov[0],mov[2]) ) > par.deltaAbs) return false;
00304 if(fabs( Distance(fix[0],fix[3]) - Distance(mov[0],mov[3]) ) > par.deltaAbs) return false;
00305 if(fabs( Distance(fix[1],fix[2]) - Distance(mov[1],mov[2]) ) > par.deltaAbs) return false;
00306 if(fabs( Distance(fix[1],fix[3]) - Distance(mov[1],mov[3]) ) > par.deltaAbs) return false;
00307 if(fabs( Distance(fix[2],fix[3]) - Distance(mov[2],mov[3]) ) > par.deltaAbs) return false;
00308
00309 vcg::ComputeRigidMatchMatrix(fix,mov,mat);
00310
00311 ScalarType maxSquaredDistance = 0.0;
00312 for(int i = 0; i < 4; ++i)
00313 maxSquaredDistance =std::max(maxSquaredDistance, SquaredDistance(mat * mov[i] ,fix[i]));
00314 return sqrt(maxSquaredDistance) < par.deltaAbs;
00315 }
00316
00319 void ComputeR1(std::vector<Couple > &R1)
00320 {
00321 R1.clear();
00322 for(size_t vi = 0; vi < subsetQ.size(); ++vi)
00323 for(size_t vj = vi; vj < subsetQ.size(); ++vj){
00324 ScalarType d = Distance(subsetQ[vi]->P(),subsetQ[vj]->P());
00325 if( (d < side+par.deltaAbs))
00326 {
00327 R1.push_back(Couple(subsetQ[vi],subsetQ[vj], d));
00328 R1.push_back(Couple(subsetQ[vj],subsetQ[vi], d));
00329 }
00330 }
00331
00332 std::sort(R1.begin(),R1.end());
00333 }
00334
00335
00336
00337 bool FindCongruent(const std::vector<Couple > &R1, const FourPoints &B, const ScalarType r1, const ScalarType r2)
00338 {
00339 clock_t t0=clock();
00340 int n_base=0;
00341 bool done = false;
00342 int n_closests = 0, n_congr = 0;
00343 int ac =0 ,acf = 0,tr = 0,trf =0;
00344 ScalarType d1,d2;
00345 d1 = (B[1]-B[0]).Norm();
00346 d2 = (B[3]-B[2]).Norm();
00347
00348 typename std::vector<Couple>::const_iterator bR1,eR1,bR2,eR2,ite;
00349 bR1 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d1-par.deltaAbs));
00350 eR1 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d1+par.deltaAbs));
00351 bR2 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d2-par.deltaAbs));
00352 eR2 = std::lower_bound(R1.begin(),R1.end(),Couple(0,0,d2+par.deltaAbs));
00353
00354
00355
00356
00357 if(bR1 == R1.end()) return false;
00358 if(bR2 == R1.end()) return false;
00359
00360
00361 Invr.Clear();
00362 typename PMesh::VertexIterator vii;
00363 int i = &(*bR1)-&(*R1.begin());
00364 for(ite = bR1; ite != eR1;++ite){
00365 vii = vcg::tri::Allocator<PMesh>::AddVertices(Invr,1);
00366
00367 (*vii).P() .Import( ite->p0->P() + ( ite->p1->P() - ite->p0->P()) * r1);
00368 ++i;
00369 }
00370 if(Invr.vert.empty() ) return false;
00371
00372
00373 typename PMesh::template PerVertexAttributeHandle<int> id = vcg::tri::Allocator<PMesh>::template AddPerVertexAttribute<int>(Invr,std::string("index"));
00374 i = &(*bR1)-&(*R1.begin());
00375 for(vii = Invr.vert.begin(); vii != Invr.vert.end();++vii,++i) id[vii] = i;
00376
00377 vcg::tri::UpdateBounding<PMesh>::Box(Invr);
00378
00379
00380 std::vector<EPoint> R2inv;
00381 i = &(*bR2)-&(*R1.begin());
00382
00383 for(ite = bR2; ite != eR2;++ite){
00384
00385 R2inv.push_back( EPoint( R1[i].p0->P() + (R1[i].p1->P() - R1[i].p0->P()) * r2,i));
00386 ++i;
00387 }
00388
00389 GridType ugrid;
00390 ugrid.Set(Invr.vert.begin(),Invr.vert.end());
00391 n_closests = 0; n_congr = 0; ac =0 ; acf = 0; tr = 0; trf = 0;
00392 printf("R2Inv.size = %d \n",R2inv.size());
00393 for(unsigned int i = 0 ; i < R2inv.size() ; ++i)
00394 {
00395 std::vector<typename PMesh::VertexType*> closests;
00396
00397
00398 vcg::Matrix44<ScalarType> mat;
00399 Box3x bb;
00400 bb.Add(R2inv[i].pos+CoordType(par.deltaAbs,par.deltaAbs, par.deltaAbs));
00401 bb.Add(R2inv[i].pos-CoordType(par.deltaAbs,par.deltaAbs, par.deltaAbs));
00402
00403 vcg::tri::GetInBoxVertex<PMesh,GridType,std::vector<typename PMesh::VertexType*> >
00404 (Invr,ugrid,bb,closests);
00405
00406 if(closests.size() > 5)
00407 closests.resize(5);
00408
00409 n_closests+=closests.size();
00410 for(unsigned int ip = 0; ip < closests.size(); ++ip)
00411 {
00412 FourPoints p;
00413 p[0] = R1[id[closests[ip]]][0]->cP();
00414 p[1] = R1[id[closests[ip]]][1]->cP();
00415 p[2] = R1[ R2inv[i].pi][0]->cP();
00416 p[3] = R1[ R2inv[i].pi][1]->cP();
00417
00418 n_base++;
00419 if(!IsTransfCongruent(B,p,mat)) {
00420 trf++;
00421 }
00422 else{
00423 tr++;
00424 n_congr++;
00425 Candidate c(p,mat);
00426 EvaluateAlignment(c);
00427
00428 if( c.score > par.scoreFeet)
00429 U.push_back(c);
00430 }
00431 }
00432 }
00433
00434 vcg::tri::Allocator<PMesh>::DeletePerVertexAttribute(Invr,id);
00435 printf("n_closests %5d = (An %5d ) + ( Tr %5d ) + (OK) %5d\n",n_closests,acf,trf,n_congr);
00436
00437 stat.findCongruentTime += clock()-t0;
00438 return done;
00439 }
00440
00441
00442 int EvaluateSample(Candidate & fp, const CoordType & tp, const CoordType & np)
00443 {
00444 CoordType ttp = fp.T * tp;
00445 vcg::Point4<ScalarType> np4 = fp.T * vcg::Point4<ScalarType>(np[0],np[1],np[2],0.0);
00446 CoordType tnp(np4[0],np4[1],np4[2]);
00447
00448 ScalarType dist ;
00449 VertexType* v = vcg::tri::GetClosestVertex(*Q, ugridQ, ttp, par.deltaAbs*2.0, dist );
00450
00451 if(v!=0)
00452 {
00453 if( v->N().dot(tnp) > par.cosAngle ) return 1;
00454 else return -1;
00455 }
00456 else return 0;
00457 }
00458
00459
00460 void EvaluateAlignment(Candidate & fp){
00461 int n_delta_close = 0;
00462 for(int i = 0 ; i< 4; ++i) {
00463 for(unsigned int j = 0; j < ExtB[i].size();++j){
00464 n_delta_close+=EvaluateSample(fp, ExtB[i][j]->P(), ExtB[i][j]->cN());
00465 }
00466 }
00467 fp.score = n_delta_close;
00468 }
00469
00470 void TestAlignment(Candidate & fp)
00471 {
00472 clock_t t0 = clock();
00473 int n_delta_close = 0;
00474 for(unsigned int j = 0; j < subsetP.size();++j){
00475 CoordType np = subsetP[j]->N();
00476 CoordType tp = subsetP[j]->P();
00477 n_delta_close+=EvaluateSample(fp,tp,np);
00478 }
00479 fp.score = n_delta_close;
00480 stat.testAlignmentTime += clock()-t0;
00481 }
00482
00483
00484 bool Align(Matrix44x & result, vcg::CallBackPos * cb )
00485 {
00486 int maxAttempt =100;
00487 int scoreThr = par.sampleNumP*0.8;
00488
00489 Candidate bestC;
00490
00491 std::vector<Couple > R1;
00492 ComputeR1(R1);
00493 for(int i = 0; i < maxAttempt && bestC.score<scoreThr ; ++i )
00494 {
00495 FourPoints B;
00496 ScalarType r1,r2;
00497 if(SelectCoplanarBase(B,r1,r2))
00498 {
00499 U.clear();
00500 FindCongruent(R1,B,r1,r2);
00501 qDebug("Attempt %i found %i candidate best score %i",i,U.size(),bestC.score);
00502 for(size_t i = 0 ; i < U.size() ;++i)
00503 {
00504 TestAlignment(U[i]);
00505 if(U[i].score > bestC.score)
00506 bestC = U[i];
00507 }
00508 }
00509 }
00510 result = bestC.T;
00511 return bestC.score >0;
00512 }
00513
00514 bool Align(int L, Matrix44x & result, vcg::CallBackPos * cb )
00515 {
00516 int bestv = 0;
00517 bool found;
00518 int n_tries = 0;
00519 U.clear();
00520
00521 if(L==0)
00522 {
00523
00524 L = (log(1.0-0.9) / log(1.0-pow((float)par.overlap,3.f)))+1;
00525 printf("using %d bases\n",L);
00526 }
00527 std::vector<Couple > R1;
00528 ComputeR1(R1);
00529
00530 for(int t = 0; t < L; ++t )
00531 {
00532 FourPoints B;
00533 ScalarType r1,r2;
00534 do
00535 {
00536 n_tries = 0;
00537 do
00538 {
00539 n_tries++;
00540 found = SelectCoplanarBase(B,r1,r2);
00541 }
00542 while(!found && (n_tries < 50));
00543 if(!found) {
00544 par.overlap*=0.9;
00545 side = P->bbox.Dim()[P->bbox.MaxDim()]*par.overlap;
00546 ComputeR1(R1);
00547 }
00548 } while (!found && (par.overlap >0.1));
00549
00550 if(par.overlap < 0.1) {
00551 printf("FAILED");
00552 return false;
00553 }
00554 bases.push_back(B);
00555 if(cb) cb(t*100/L,"Trying bases");
00556 if(FindCongruent(R1,B,r1,r2))
00557 break;
00558 }
00559
00560 if(U.empty()) return false;
00561
00562
00563 if(cb) cb(90,"TestAlignment");
00564 bestv = -std::numeric_limits<float>::max();
00565 iwinner = 0;
00566
00567 for(int i = 0 ; i < U.size() ;++i)
00568 {
00569 TestAlignment(U[i]);
00570 if(U[i].score > bestv){
00571 bestv = U[i].score;
00572 iwinner = i;
00573 }
00574 }
00575
00576 result = U[iwinner].T;
00577 Invr.Clear();
00578 return true;
00579 }
00580
00581 };
00582
00583 }
00584 }
00585 #endif