00001 #ifndef _4PCS_
00002 #define _4PCS_
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00032 #include <vcg/space/point3.h>
00033 #include <vcg/space/point4.h>
00034 #include <vcg/space/line3.h>
00035 #include <vcg/space/plane3.h>
00036 #include <vcg/math/base.h>
00037 #include <vcg/math/point_matching.h>
00038 #include <vcg/math/matrix44.h>
00039
00040 #include <vcg/space/index/grid_static_ptr.h>
00041 #include <vcg/complex/trimesh/closest.h>
00042 #include <vcg/complex/trimesh/update/bounding.h>
00043
00044 #include <vcg/simplex/vertex/base.h>
00045 #include <vcg/simplex/face/base.h>
00046 #include <vcg/complex/trimesh/base.h>
00047 #include <vcg/complex/trimesh/stat.h>
00048 #include <wrap/io_trimesh/export_ply.h>
00049
00050
00051
00052
00053 typedef bool AACb( const int pos,const char * str );
00054
00055 namespace vcg{
00056 namespace tri{
00057 template <class MeshType>
00058 class FourPCS {
00059 public:
00060
00061 class PVertex;
00062 class PFace;
00063
00064 class PUsedTypes: public vcg::UsedTypes < vcg::Use<PVertex>::template AsVertexType,
00065 vcg::Use<PFace >::template AsFaceType >{};
00066
00067
00068 class PVertex : public vcg::Vertex< PUsedTypes,vcg::vertex::BitFlags,vcg::vertex::Coord3f ,vcg::vertex::Mark>{};
00069
00070 class PFace : public vcg::Face< PUsedTypes> {};
00071
00072 class PMesh : public vcg::tri::TriMesh< std::vector<PVertex>, std::vector<PFace> > {};
00073
00074 typedef typename MeshType::ScalarType ScalarType;
00075 typedef typename MeshType::CoordType CoordType;
00076 typedef typename MeshType::VertexIterator VertexIterator;
00077 typedef typename MeshType::VertexType VertexType;
00078 typedef vcg::Point4< vcg::Point3<ScalarType> > FourPoints;
00079 typedef vcg::GridStaticPtr<typename PMesh::VertexType, ScalarType > GridType;
00080
00081
00082 struct Parameters{
00083 ScalarType delta;
00084 int feetsize;
00085 ScalarType f;
00086 int scoreFeet,
00087 scoreAln;
00088
00089 void Default(){
00090 delta = 0.5;
00091 feetsize = 25;
00092 f = 0.5;
00093 scoreFeet = 50;
00094 scoreAln = 200;
00095 }
00096 };
00097
00098 Parameters prs;
00099
00100 public:
00101 void Init(MeshType &_P,MeshType &_Q);
00102 bool Align( int L, vcg::Matrix44f & result, AACb * cb = NULL );
00103
00104
00105 private:
00106 struct Couple: public std::pair<int,int>{
00107 Couple(const int & i, const int & j, float d):std::pair<int,int>(i,j),dist(d){}
00108 Couple(float d):std::pair<int,int>(0,0),dist(d){}
00109 float dist;
00110 const bool operator < (const Couple & o) const {return dist < o.dist;}
00111 int & operator[](const int &i){return (i==0)? first : second;}
00112 };
00113
00114
00115
00116
00117
00118 void IntersectionLineLine(const CoordType & x1,const CoordType & x2,const CoordType & x3,const CoordType & x4, CoordType&x)
00119 {
00120 CoordType a = x2-x1, b = x4-x3, c = x3-x1;
00121 x = x1 + a * ((c^b).dot(a^b)) / (a^b).SquaredNorm();
00122 }
00123
00124
00125
00126
00127 struct CandiType{
00128 CandiType(){};
00129 CandiType(FourPoints _p,vcg::Matrix44<ScalarType>_T):p(_p),T(_T){}
00130 FourPoints p;
00131 vcg::Matrix44<ScalarType> T;
00132 ScalarType err;
00133 int score;
00134 int base;
00135 inline bool operator <(const CandiType & o) const {return score > o.score;}
00136 };
00137
00138
00139 MeshType *P,
00140 *Q;
00141 std::vector<int> mapsub;
00142
00143
00144 PMesh Invr;
00145
00146 std::vector< CandiType > U;
00147 CandiType winner;
00148 int iwinner;
00149
00150 FourPoints B;
00151 std::vector<FourPoints> bases;
00152 ScalarType side;
00153 std::vector<VertexType*> ExtB[4];
00154 std::vector<VertexType*> subsetP;
00155 ScalarType radius;
00156
00157 ScalarType Bangle;
00158 std::vector<Couple > R1;
00159 ScalarType r1,r2;
00160
00161
00162 struct EPoint{
00163 EPoint(vcg::Point3<ScalarType> _p, int _i):pos(_p),pi(_i){}
00164 vcg::Point3<ScalarType> pos;
00165 int pi;
00166 void GetBBox(vcg::Box3<ScalarType> & b){b.Add(pos);}
00167 };
00168
00169 GridType *ugrid;
00170 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridQ;
00171 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType > ugridP;
00172
00173
00174
00175
00176 bool SelectCoplanarBase();
00177 bool FindCongruent() ;
00178
00179
00180 void ComputeR1R2(ScalarType d1,ScalarType d2);
00181
00182 bool IsTransfCongruent(FourPoints fp,vcg::Matrix44<ScalarType> & mat, float & trerr);
00183 int EvaluateSample(CandiType & fp, CoordType & tp, CoordType & np, const float & angle);
00184 void EvaluateAlignment(CandiType & fp);
00185 void TestAlignment(CandiType & fp);
00186
00187
00188 public:
00189 std::vector<vcg::Matrix44f> allTr;
00190 FILE * db;
00191 char namemesh1[255],namemesh2[255];
00192 int n_base;
00193 void InitDebug(const char * name1, const char * name2){
00194 db = fopen("debugPCS.txt","w");
00195 sprintf(&namemesh1[0],"%s",name1);
00196 sprintf(&namemesh2[0],"%s",name2);
00197 n_base = 0;
00198 }
00199
00200 void FinishDebug(){
00201 fclose(db);
00202 }
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217 };
00218
00219 template <class MeshType>
00220 void
00221 FourPCS<MeshType>:: Init(MeshType &_P,MeshType &_Q){
00222
00223 P = &_P;Q=&_Q;
00224 ugridQ.Set(Q->vert.begin(),Q->vert.end());
00225 ugridP.Set(P->vert.begin(),P->vert.end());
00226 int vi;
00227
00228
00229
00230 float ratio = 800 / (float) Q->vert.size();
00231 for(vi = 0; vi < Q->vert.size(); ++vi)
00232 if(rand()/(float) RAND_MAX < ratio)
00233 mapsub.push_back(vi);
00234
00235 for(vi = 0; vi < P->vert.size(); ++vi)
00236 if(rand()/(float) RAND_MAX < ratio)
00237 subsetP.push_back(&P->vert[vi]);
00238
00239
00240 float avD = 0.0,dist;
00241 for(int i = 0 ; i < 100; ++i){
00242 int ri = rand()/(float) RAND_MAX * Q->vert.size() -1;
00243 std::vector< CoordType > samples,d_samples;
00244 std::vector<ScalarType > dists;
00245 std::vector<VertexType* > ress;
00246 vcg::tri::GetKClosestVertex<
00247 MeshType,
00248 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType>,
00249 std::vector<VertexType*>,
00250 std::vector<ScalarType>,
00251 std::vector< CoordType > >(*Q,ugridQ,2,Q->vert[ri].cP(),Q->bbox.Diag(), ress,dists, samples);
00252 assert(ress.size() == 2);
00253 avD+=dists[1];
00254 }
00255 avD /=100;
00256 avD /= sqrt(ratio);
00257
00258 prs.delta = avD * prs.delta;
00259 side = P->bbox.Dim()[P->bbox.MaxDim()]*prs.f;
00260
00261 }
00262
00263 template <class MeshType>
00264 bool
00265 FourPCS<MeshType>::SelectCoplanarBase(){
00266
00267 vcg::tri::UpdateBounding<MeshType>::Box(*P);
00268
00269
00270 ScalarType dtol = side*0.1;
00271
00272
00273 int i = 0,ch;
00274
00275
00276 ch = (rand()/(float)RAND_MAX)*(P->vert.size()-2);
00277 B[0] = P->vert[ch].P();
00278
00279
00280 for(i = 0; i < P->vert.size(); ++i){
00281 ScalarType dd = (P->vert[i].P() - B[0]).Norm();
00282 if( ( dd < side + dtol) && (dd > side - dtol)){
00283 B[1] = P->vert[i].P();
00284
00285 break;
00286 }
00287 }
00288 if(i == P->vert.size())
00289 return false;
00290
00291
00292 int best = -1; ScalarType bestv=std::numeric_limits<float>::max();
00293 for(i = 0; i < P->vert.size(); ++i){
00294 int id = rand()/(float)RAND_MAX * (P->vert.size()-1);
00295 ScalarType dd = (P->vert[id].P() - B[1]).Norm();
00296 if( ( dd < side + dtol) && (dd > side - dtol)){
00297 ScalarType angle = fabs( ( P->vert[id].P()-B[1]).normalized().dot((B[1]-B[0]).normalized()));
00298 if( angle < bestv){
00299 bestv = angle;
00300 best = id;
00301 }
00302 }
00303 }
00304 if(best == -1)
00305 return false;
00306 B[2] = P->vert[best].P();
00307
00308
00309 CoordType n = ((B[0]-B[1]).normalized() ^ (B[2]-B[1]).normalized()).normalized();
00310 CoordType B4 = B[1] + (B[0]-B[1]) + (B[2]-B[1]);
00311 VertexType * v =0;
00312 ScalarType radius = dtol*4.0;
00313
00314 std::vector<typename MeshType::VertexType*> closests;
00315 std::vector<ScalarType> distances;
00316 std::vector<CoordType> points;
00317
00318 vcg::tri::GetInSphereVertex<
00319 MeshType,
00320 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
00321 std::vector<typename MeshType::VertexType*>,
00322 std::vector<ScalarType>,
00323 std::vector<CoordType>
00324 >(*P,ugridP,B4,radius,closests,distances,points);
00325
00326 if(closests.empty())
00327 return false;
00328 best = -1; bestv=std::numeric_limits<float>::max();
00329 for(i = 0; i <closests.size(); ++i){
00330 ScalarType angle = fabs((closests[i]->P() - B[1]).normalized().dot(n));
00331 if( angle < bestv){
00332 bestv = angle;
00333 best = i;
00334 }
00335 }
00336 B[3] = closests[best]->P();
00337
00338
00339
00340
00341 CoordType x;
00342 std::swap(B[1],B[2]);
00343 IntersectionLineLine(B[0],B[1],B[2],B[3],x);
00344
00345 r1 = (x - B[0]).dot(B[1]-B[0]) / (B[1]-B[0]).SquaredNorm();
00346 r2 = (x - B[2]).dot(B[3]-B[2]) / (B[3]-B[2]).SquaredNorm();
00347
00348 if( ((B[0]+(B[1]-B[0])*r1)-(B[2]+(B[3]-B[2])*r2)).Norm() > prs.delta )
00349 return false;
00350
00351 radius =side*0.5;
00352 std::vector< CoordType > samples,d_samples;
00353 std::vector<ScalarType > dists;
00354
00355 for(int i = 0 ; i< 4; ++i){
00356 vcg::tri::GetKClosestVertex<
00357 MeshType,
00358 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >,
00359 std::vector<VertexType*>,
00360 std::vector<ScalarType>,
00361 std::vector< CoordType > >(*P,ugridP, prs.feetsize ,B[i],radius, ExtB[i],dists, samples);
00362 }
00363
00364
00365
00366
00367 return true;
00368
00369 }
00370
00371
00372 template <class MeshType>
00373 bool
00374 FourPCS<MeshType>::IsTransfCongruent(FourPoints fp,vcg::Matrix44<ScalarType> & mat, float & trerr){
00375
00376 std::vector<vcg::Point3<ScalarType> > fix;
00377 std::vector<vcg::Point3<ScalarType> > mov;
00378 for(int i = 0 ; i < 4; ++i) mov.push_back(B[i]);
00379 for(int i = 0 ; i < 4; ++i) fix.push_back(fp[i]);
00380
00381 vcg::Point3<ScalarType> n,p;
00382 n = (( B[1]-B[0]).normalized() ^ ( B[2]- B[0]).normalized())*( B[1]- B[0]).Norm();
00383 p = B[0] + n;
00384 mov.push_back(p);
00385 n = (( fp[1]-fp[0]).normalized() ^ (fp[2]- fp[0]).normalized())*( fp[1]- fp[0]).Norm();
00386 p = fp[0] + n;
00387 fix.push_back(p);
00388
00389 vcg::PointMatching<ScalarType>::ComputeRigidMatchMatrix(mat,fix,mov);
00390
00391 ScalarType err = 0.0;
00392 for(int i = 0; i < 4; ++i) err+= (mat * mov[i] - fix[i]).SquaredNorm();
00393
00394 trerr = vcg::math::Sqrt(err);
00395 return err < prs.delta* prs.delta*4.0;
00396 }
00397
00398 template <class MeshType>
00399 void
00400 FourPCS<MeshType>::ComputeR1R2(ScalarType d1,ScalarType d2){
00401 int vi,vj;
00402 R1.clear();
00403
00404 int start = clock();
00405 for(vi = 0; vi < mapsub.size(); ++vi) for(vj = vi; vj < mapsub.size(); ++vj){
00406 ScalarType d = ((Q->vert[mapsub[vi]]).P()-(Q->vert[mapsub[vj]]).P()).Norm();
00407 if( (d < d1+ side*0.5) && (d > d1-side*0.5))
00408 {
00409 R1.push_back(Couple(mapsub[vi],mapsub[vj],d ));
00410 R1.push_back(Couple(mapsub[vj],mapsub[vi],d));
00411 }
00412 }
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 std::sort(R1.begin(),R1.end());
00423
00424 }
00425
00426 template <class MeshType>
00427 bool
00428 FourPCS<MeshType>::FindCongruent() {
00429 bool done = false;
00430 std::vector<EPoint> R2inv;
00431 int n_closests = 0, n_congr = 0;
00432 int ac =0 ,acf = 0,tr = 0,trf =0;
00433 ScalarType d1,d2;
00434 d1 = (B[1]-B[0]).Norm();
00435 d2 = (B[3]-B[2]).Norm();
00436
00437 int start = clock();
00438
00439
00440 typename PMesh::VertexIterator vii;
00441 typename std::vector<Couple>::iterator bR1,eR1,bR2,eR2,ite,cite;
00442 bR1 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d1-prs.delta*2.0));
00443 eR1 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d1+prs.delta*2.0));
00444 bR2 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d2-prs.delta*2.0));
00445 eR2 = std::lower_bound<typename std::vector<Couple>::iterator,Couple>(R1.begin(),R1.end(),Couple(d2+prs.delta*2.0));
00446
00447
00448
00449
00450 if(bR1 == R1.end()) return false;
00451 if(bR2 == R1.end()) return false;
00452
00453
00454 Invr.Clear();
00455 int i = &(*bR1)-&(*R1.begin());
00456 for(ite = bR1; ite != eR1;++ite){
00457 vii = vcg::tri::Allocator<PMesh>::AddVertices(Invr,1);
00458 (*vii).P() = Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r1;
00459 ++i;
00460 }
00461 if(Invr.vert.empty() ) return false;
00462
00463
00464 typename PMesh::template PerVertexAttributeHandle<int> id = vcg::tri::Allocator<PMesh>::template AddPerVertexAttribute<int>(Invr,std::string("index"));
00465 i = &(*bR1)-&(*R1.begin());
00466 for(vii = Invr.vert.begin(); vii != Invr.vert.end();++vii,++i) id[vii] = i;
00467
00468 vcg::tri::UpdateBounding<PMesh>::Box(Invr);
00469
00470
00471 ugrid = new GridType();
00472 ugrid->Set(Invr.vert.begin(),Invr.vert.end());
00473
00474 i = &(*bR2)-&(*R1.begin());
00475
00476 for(ite = bR2; ite != eR2;++ite){
00477 R2inv.push_back( EPoint( Q->vert[R1[i][0]].P() + (Q->vert[R1[i][1]].P()-Q->vert[R1[i][0]].P()) * r2,i));
00478 ++i;
00479 }
00480
00481 n_closests = 0; n_congr = 0; ac =0 ; acf = 0; tr = 0; trf = 0;
00482
00483 for(uint i = 0 ; i < R2inv.size() ; ++i){
00484
00485 std::vector<typename PMesh::VertexType*> closests;
00486 std::vector<ScalarType> distances;
00487 std::vector<CoordType> points;
00488
00489
00490 vcg::Matrix44<ScalarType> mat;
00491 vcg::Box3f bb;
00492 bb.Add(R2inv[i].pos+vcg::Point3f(prs.delta * 0.1,prs.delta * 0.1 , prs.delta * 0.1 ));
00493 bb.Add(R2inv[i].pos-vcg::Point3f(prs.delta * 0.1,prs.delta* 0.1 , prs.delta* 0.1));
00494
00495 vcg::tri::GetInBoxVertex<PMesh,GridType,std::vector<typename PMesh::VertexType*> >
00496 (Invr,*ugrid,bb,closests);
00497
00498 n_closests+=closests.size();
00499 for(uint ip = 0; ip < closests.size(); ++ip){
00500 FourPoints p;
00501 p[0] = Q->vert[R1[id[closests[ip]]][0]].P();
00502 p[1] = Q->vert[R1[id[closests[ip]]][1]].P();
00503 p[2] = Q->vert[R1[ R2inv[i].pi][0]].P();
00504 p[3] = Q->vert[R1[ R2inv[i].pi][1]].P();
00505
00506 float trerr;
00507 n_base++;
00508 if(!IsTransfCongruent(p,mat,trerr)) {
00509 trf++;
00510
00511
00512
00513
00514 }
00515 else{
00516 tr++;
00517 n_congr++;
00518 U.push_back(CandiType(p,mat));
00519 EvaluateAlignment(U.back());
00520 U.back().base = bases.size()-1;
00521
00522 if( U.back().score > prs.scoreFeet){
00523 TestAlignment(U.back());
00524 if(U.back().score > prs.scoreAln)
00525 {
00526 done = true; break;
00527 }
00528 }
00529
00530
00531
00532
00533 }
00534 }
00535 }
00536
00537 delete ugrid;
00538 vcg::tri::Allocator<PMesh>::DeletePerVertexAttribute(Invr,id);
00539 printf("n_closests %5d = (An %5d ) + ( Tr %5d ) + (OK) %5d\n",n_closests,acf,trf,n_congr);
00540
00541 return done;
00542
00543
00544 }
00545
00546
00547
00548 template <class MeshType>
00549 int FourPCS<MeshType>::EvaluateSample(CandiType & fp, CoordType & tp, CoordType & np, const float & angle){
00550 VertexType* v;
00551 ScalarType dist ;
00552 radius = prs.delta;
00553 tp = fp.T * tp;
00554
00555 vcg::Point4<ScalarType> np4;
00556 np4 = fp.T * vcg::Point4<ScalarType>(np[0],np[1],np[2],0.0);
00557 np[0] = np4[0]; np[1] = np4[1]; np[2] = np4[2];
00558
00559 v = 0;
00560
00561
00562
00563
00564 typename MeshType::VertexType vq;
00565 vq.P() = tp;
00566 vq.N() = np;
00567 v = vcg::tri::GetClosestVertexNormal<
00568 MeshType,
00569 vcg::GridStaticPtr<typename MeshType::VertexType, ScalarType >
00570 >(*Q,ugridQ,vq,radius, dist );
00571
00572 if(v!=0)
00573 if( v->N().dot(np) -angle >0) return 1; else return -1;
00574
00575 }
00576
00577
00578 template <class MeshType>
00579 void
00580 FourPCS<MeshType>::EvaluateAlignment(CandiType & fp){
00581 int n_delta_close = 0;
00582 for(int i = 0 ; i< 4; ++i) {
00583 for(uint j = 0; j < ExtB[i].size();++j){
00584 CoordType np = ExtB[i][j]->cN();;
00585 CoordType tp = ExtB[i][j]->P();
00586 n_delta_close+=EvaluateSample(fp,tp,np,0.9);
00587 }
00588 }
00589 fp.score = n_delta_close;
00590 }
00591
00592 template <class MeshType>
00593 void
00594 FourPCS<MeshType>::TestAlignment(CandiType & fp){
00595 radius = prs.delta;
00596 int n_delta_close = 0;
00597 for(uint j = 0; j < subsetP.size();++j){
00598 CoordType np = subsetP[j]->N();
00599 CoordType tp = subsetP[j]->P();
00600 n_delta_close+=EvaluateSample(fp,tp,np,0.6);
00601 }
00602 fp.score = n_delta_close;
00603 }
00604
00605
00606 template <class MeshType>
00607 bool
00608 FourPCS<MeshType>:: Align( int L, vcg::Matrix44f & result, AACb * cb ){
00609
00610 int bestv = 0;
00611 bool found;
00612 int n_tries = 0;
00613 U.clear();
00614
00615 if(L==0)
00616 {
00617 L = (log(1.0-0.9999) / log(1.0-pow((float)prs.f,3.f)))+1;
00618 printf("using %d bases\n",L);
00619 }
00620
00621 ComputeR1R2(side*1.4,side*1.4);
00622
00623 for(int t = 0; t < L; ++t ){
00624 do{
00625 n_tries = 0;
00626 do{
00627 n_tries++;
00628 found = SelectCoplanarBase();
00629 }
00630 while(!found && (n_tries <50));
00631 if(!found) {
00632 prs.f*=0.98;
00633 side = P->bbox.Dim()[P->bbox.MaxDim()]*prs.f;
00634 ComputeR1R2(side*1.4,side*1.4);
00635 }
00636 } while (!found && (prs.f >0.1));
00637
00638 if(prs.f <0.1) {
00639 printf("FAILED");
00640 return false;
00641 }
00642 bases.push_back(B);
00643 if(cb) cb(t*100/L,"trying bases");
00644 if(FindCongruent())
00645 break;
00646 }
00647
00648 if(U.empty()) return false;
00649
00650 std::sort(U.begin(),U.end());
00651
00652 bestv = -std::numeric_limits<float>::max();
00653 iwinner = 0;
00654
00655 for(int i = 0 ; i < U.size() ;++i)
00656 {
00657 TestAlignment(U[i]);
00658 if(U[i].score > bestv){
00659 bestv = U[i].score;
00660 iwinner = i;
00661 }
00662 }
00663
00664 printf("Best score: %d \n", bestv);
00665
00666 winner = U[iwinner];
00667 result = winner.T;
00668
00669
00670 Invr.Clear();
00671
00672 return true;
00673 }
00674
00675 }
00676 }
00677 #endif