ICP.h
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:17k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. //############################################################
  2. // 
  3. // ICP.h
  4. //
  5. // Kari Pulli
  6. // Thu Jul  2 11:11:57 PDT 1998
  7. //
  8. // Iterative closest point method for registration
  9. //
  10. //############################################################
  11. #ifndef _ICP_H_
  12. #define _ICP_H_
  13. #ifdef WIN32
  14. # include "winGLdecs.h"
  15. #endif
  16. #include <GL/gl.h>
  17. #include <iostream.h>
  18. #include <algo.h>
  19. #include <vector.h>
  20. #include "Median.h"
  21. #include "absorient.h"
  22. #include "Xform.h"
  23. #include "Pnt3.h"
  24. #include "GlobalReg.h"
  25. #include "DrawObj.h"
  26. extern DrawObjects draw_other_things;
  27. // The template argument is supposed to be a pointer
  28. template <class T>
  29. class ICP : public DrawObj {
  30. public:
  31.   bool allow_bdry;
  32. private:
  33.   T            P, Q;
  34.   Xform<float> xfP, xfQ; // xfP is changed, xfQ stays constant
  35.   vector<Pnt3> ssP, ssQ, ssPn, ssQn; // subsampled, local coords
  36.   vector<Pnt3> pP, pQ, nP, nQ; // stored in world coords
  37.   int firstend;
  38.   Median<double> med;
  39.   // return culling threshold
  40.   float cull_pairs(int  p, // percentage to cull, [0,100]
  41.    bool cull = true)
  42.     {
  43.       if (p == 0) return 1.e33;
  44.       med.set_percentage((100-p)/100.0); med.clear();
  45.       int n = pP.size();
  46.       vector<double> d(n);
  47.       for (int i=0; i<n; i++) {
  48. med += d[i] = dist2(pP[i],pQ[i]);
  49.       }
  50.       double thr = med.find();
  51.       if (cull) {
  52. i=0;
  53. int k = 0;
  54. for (int j=0; j<pP.size(); j++) {
  55.   if (d[j] <= thr) {
  56.     // keep
  57.     pP[i] = pP[j]; pQ[i] = pQ[j];
  58.     nP[i] = nP[j]; nQ[i] = nQ[j];
  59.     i++;
  60.   } else {
  61.     if (j < firstend) k++;
  62.   }
  63. }
  64. pP.erase(&pP[i], pP.end());
  65. pQ.erase(&pQ[i], pQ.end());
  66. nP.erase(&nP[i], nP.end());
  67. nQ.erase(&nQ[i], nQ.end());
  68. firstend -= k;
  69.       }
  70.       return sqrtf(thr);
  71.     }
  72.   float Horn_align(void)
  73.     {
  74.       // calculate the best rigid motion for alignment 
  75.       double q[7];
  76.       horn_align(&pP[0], &pQ[0], pP.size(), q);
  77.       // apply the motion to the current transformation
  78.       xfP.addQuaternion(q, 7);
  79.       Xform<double> _xf;
  80.       _xf.addQuaternion(q, 7);
  81.       for_each(pP.begin(), pP.end(), _xf);
  82.       return RMS_point_to_point_error();
  83.     }
  84.   float CM_align(void)
  85.     {
  86.       // calculate the best rigid motion for alignment 
  87.       // we can do two loops of chen_medioni and the results
  88.       // in general improve
  89.       // third time doesn't improve much with the same pairs
  90.       double q[7];
  91.       for (int j=0; j<2; j++) {
  92. chen_medioni(&pP[0], &pQ[0], &nQ[0], pP.size(), q);
  93.   
  94. // apply the motion to the current transformation
  95. xfP.addQuaternion(q, 7);
  96. Xform<float> _xf;
  97. _xf.addQuaternion (q, 7);
  98. for_each(pP.begin(), pP.end(), _xf);
  99.       }
  100.       return RMS_point_to_point_error();
  101.     }
  102.   void find_pairs(float thr)
  103.     {
  104.       pP.clear(); pQ.clear(); nP.clear(); nQ.clear();
  105.       int n = ssP.size() + ssQ.size();
  106.       cout << "Finding ~" << n << " points there... " << flush;
  107.       pP.reserve(n); pQ.reserve(n); nP.reserve(n); nQ.reserve(n);
  108.       // for each selected point, find the closest point
  109.       Pnt3 wp,wn,lp,ln,cp,cn;
  110.       n = ssP.size();
  111.       // first, from P to Q
  112.       Xform<float> xfi = xfQ; xfi.fast_invert();
  113.       for (int i=0; i<n; i++) {
  114. // point to other mesh's coords
  115. xfP.apply(ssP[i], wp); xfP.apply_nrm(ssPn[i], wn); // world
  116. xfi.apply(wp, lp);     xfi.apply_nrm(wn, ln); // local in Q
  117. if (Q->closest_point(lp, ln, cp, cn, thr, allow_bdry)) {
  118.   // store world coords
  119.   // point
  120.   pP.push_back(wp);
  121.   xfQ(cp); pQ.push_back(cp);
  122.   // normal
  123.   nP.push_back(wn);
  124.   nQ.push_back(Pnt3()); xfQ.apply_nrm(cn, nQ.back());
  125. }
  126.       }
  127.       cout << "(" << pP.size() << "), and back... " << flush;
  128.       firstend = pP.size();
  129.       // then, from Q to P
  130.       xfi = xfP; xfi.fast_invert();
  131.       n = ssQ.size();
  132.       for (i=0; i<n; i++) {
  133. // point to other mesh's coords
  134. xfQ.apply(ssQ[i], wp); xfQ.apply_nrm(ssQn[i], wn); // world
  135. xfi.apply(wp, lp);     xfi.apply_nrm(wn, ln); // local in P
  136. if (P->closest_point(lp, ln, cp, cn, thr, allow_bdry)) {
  137.   // store world coords
  138.   // point
  139.   pQ.push_back(wp);
  140.   xfP(cp); pP.push_back(cp);
  141.   // normal
  142.   nQ.push_back(wn);
  143.   nP.push_back(Pnt3()); xfP.apply_nrm(cn, nP.back());
  144. }
  145.       }
  146.       cout << "(" << pP.size() - firstend << "): done." << endl;
  147.     }
  148. public:
  149.   ICP(void) : allow_bdry(0), firstend(0) 
  150.     { 
  151.       draw_other_things.add(this);
  152.     }
  153.   ~ICP(void)
  154.     { 
  155.       draw_other_things.remove(this);
  156.     }
  157.   void set(T p, T q) 
  158.     { 
  159.       P = p; Q = q; 
  160.       xfP = P->getXform();
  161.       xfQ = Q->getXform();
  162.     }
  163.   float RMS_point_to_point_error(void)
  164.     {
  165.       float len = 0;
  166.       int n = pP.size();
  167.       for (int i=0; i<n; i++) len += dist2(pP[i],pQ[i]);
  168.       return sqrtf(len/n);
  169.     }
  170.    
  171.   void sort_into_buckets(const vector<Pnt3> &n,
  172.  vector< vector<int> > &normbuckets)
  173.   {
  174.     const int Q = 4;
  175.     const float Qsqrt1_2 = 2.8284f;
  176.     normbuckets.resize(3*Q*Q);
  177.     for (int i=0; i < n.size(); i++) {
  178.       const float *N = &n[i][0];
  179.       float ax = fabs(N[0]), ay = fabs(N[1]), az = fabs(N[2]);
  180.       int A;
  181.       float u, v;
  182.       if (ax > ay) {
  183. if (ax > az) {
  184.   A = 0;  u = (N[0] > 0) ? N[1] : -N[1];  v = (N[0] > 0) ? N[2] : -N[2];
  185. } else {
  186.   A = 2;  u = (N[2] > 0) ? N[0] : -N[0];  v = (N[2] > 0) ? N[1] : -N[1];
  187. }
  188.       } else {
  189. if (ay > az) {
  190.   A = 1;  u = (N[1] > 0) ? N[2] : -N[2];  v = (N[1] > 0) ? N[0] : -N[0];
  191. } else {
  192.   A = 2;  u = (N[2] > 0) ? N[0] : -N[0];  v = (N[2] > 0) ? N[1] : -N[1];
  193. }
  194.       }
  195.       int U = int(u * Qsqrt1_2) + (Q/2);
  196.       int V = int(v * Qsqrt1_2) + (Q/2);
  197.       normbuckets[((A * Q) + U) * Q + V].push_back(i);
  198.     }
  199.     for (int bucket=0; bucket < normbuckets.size(); bucket++)
  200.       random_shuffle(normbuckets[bucket].begin(), normbuckets[bucket].end());
  201.   }
  202.   float align(float    sample_rate,
  203.       bool     normspace_sample,
  204.       int      n_iter,
  205.       int      culling_percentage,
  206.       int      method, // 0 plane (CM), 1 point (Horn)
  207.       int      thr_kind, // 0 relative, 1 absolute
  208.       float    thr_value)
  209.     {
  210.       float avgError;
  211.       // Subsample both meshes
  212.       if (normspace_sample) {
  213. // Sampling that tries to be somewhat uniform in the space of normals
  214. vector<Pnt3> tmpverts, tmpnorms;
  215. vector< vector<int> > normbuckets;
  216. tmpverts.reserve(max(P->num_vertices(), Q->num_vertices()));
  217. tmpnorms.reserve(max(P->num_vertices(), Q->num_vertices()));
  218. // Sample P
  219.         P->subsample_points(1.0, tmpverts, tmpnorms);
  220. sort_into_buckets(tmpnorms, normbuckets);
  221. int ndesired = int(ceil(sample_rate * tmpverts.size()));
  222. ssP.clear();  ssPn.clear();
  223. while (ssP.size() < ndesired) {
  224.   for (int i=0; i < normbuckets.size(); i++) {
  225.     if (!normbuckets[i].empty()) {
  226. int ind = normbuckets[i].back();
  227. ssP.push_back(tmpverts[ind]);
  228. ssPn.push_back(tmpnorms[ind]);
  229. normbuckets[i].pop_back();
  230.     }
  231.   }
  232. }
  233. tmpverts.clear(); tmpnorms.clear(); normbuckets.clear();
  234. // Sample Q
  235.         Q->subsample_points(1.0, tmpverts, tmpnorms);
  236. sort_into_buckets(tmpnorms, normbuckets);
  237. ndesired = int(ceil(sample_rate * tmpverts.size()));
  238. ssQ.clear();  ssQn.clear();
  239. while (ssQ.size() < ndesired) {
  240.   for (int i=0; i < normbuckets.size(); i++) {
  241.     if (!normbuckets[i].empty()) {
  242. int ind = normbuckets[i].back();
  243. ssQ.push_back(tmpverts[ind]);
  244. ssQn.push_back(tmpnorms[ind]);
  245. normbuckets[i].pop_back();
  246.     }
  247.   }
  248. }
  249.       } else {
  250. // Good ol' random sampling
  251.         P->subsample_points(sample_rate, ssP, ssPn);
  252.         Q->subsample_points(sample_rate, ssQ, ssQn);
  253.       }
  254.       if (thr_kind == 0) { 
  255. // relative threshold: change to absolute
  256. // find the smaller bounding box diagonal
  257. float diag = P->localBbox().diag();
  258. float tmp  = Q->localBbox().diag();
  259. if (tmp < diag) diag = tmp;
  260. thr_value *= diag;
  261.       }
  262.       if (n_iter == 0) {
  263. // just find pairs, mainly for debugging
  264. find_pairs(thr_value);
  265. cull_pairs(culling_percentage);
  266. return 0;
  267.       }
  268.       for (int i=0; i<n_iter; i++) {
  269. // find the point pairs going from set P to Q and
  270. // vice versa (all pts in world coords)
  271. find_pairs(thr_value);
  272. cull_pairs(culling_percentage);
  273. if (pP.size() == 0) {
  274.   cerr << "Couldn't find a single point pair!" << endl;
  275.   break;
  276. }
  277. if (method) avgError = Horn_align();
  278. else        avgError = CM_align();
  279. cout << avgError << endl;
  280.       }
  281.       P->setXform(xfP);
  282.       
  283.       return avgError;
  284.     }
  285.   bool too_few_pairs(void)
  286.     {
  287.       // if less than 10% of the points in the smaller
  288.       // mesh didn't find pair, don't even try
  289.       if (ssP.size() < ssQ.size()) {
  290. if (pP.size() <= .1 * ssP.size()) {
  291.   /*
  292.   cerr << "ICP::auto_align(): "
  293.        << "too few point pairs ("
  294.        << pP.size() << " / " << ssP.size() << ")"
  295.        << endl;
  296.   */
  297.   return true;
  298. }
  299.       } else {
  300. if (pQ.size() <= .1 * ssQ.size()) {
  301.   /*
  302.   cerr << "ICP::auto_align(): "
  303.        << "too few point pairs ("
  304.        << pQ.size() << " / " << ssQ.size() << ")"
  305.        << endl;
  306.   */
  307.   return true;
  308. }
  309.       }
  310.       // Absolute minimum: 50 matches in each
  311.       if ((pP.size() < 50) || (pQ.size() < 50)) {
  312. /*
  313. cerr << "ICP::auto_align(): "
  314.      << "too few point pairs"
  315.      << endl;
  316. */
  317. return true;
  318.       }
  319.       return false;
  320.     }
  321.   // The idea of the auto_align() is to find good point pairs
  322.   // for auto-calibration of the scanner or for global alignment
  323.   // within a set of scans.
  324.   // Return whether an alignment was really tried.
  325.   bool auto_align(vector<Pnt3> &_pP,
  326.   vector<Pnt3> &_nP,
  327.   vector<Pnt3> &_pQ,
  328.   vector<Pnt3> &_nQ,
  329.   Xform<float> &rel_xf,
  330.   float         final_abs_thresh = FLT_MAX, // mm
  331.   float         max_motion       = FLT_MAX,
  332.   bool normspace_sample = false)
  333.     {
  334.       // Sample rate is set to 10% or to get around 500 subsampled points,
  335.       // whichever is greater
  336.       // We'll use 100% for the last round
  337.       float Psample_rate = min(max(500.0f / P->num_vertices(), 0.1f), 1.0f);
  338.       float Qsample_rate = min(max(500.0f / Q->num_vertices(), 0.1f), 1.0f);
  339.       // first, test whether the bounding boxes overlap...
  340.       if (!P->worldBbox().intersect(Q->worldBbox())) {
  341. /*
  342. cerr << "ICP::auto_align(): "
  343.      << "world bounding boxes don't overlap" << endl;
  344. */
  345. return false;
  346.       }
  347.       allow_bdry = false;
  348.       // Assume the initial alignment is pretty good.
  349.       // To find out what kind of threshold we should
  350.       // use, find the *minimum* dimension of the two
  351.       // bounding boxes, take 20% of that as the initial
  352.       // relative threshold, find point pairs.
  353.       // Absolute threshold can override this.
  354.       float thr_value = P->localBbox().minDim();
  355.       float tmp       = Q->localBbox().minDim();
  356.       if (tmp < thr_value) thr_value = tmp;
  357.       thr_value *= .2;
  358.       if (normspace_sample) 
  359. cerr << endl << "Using normal-space sampling..." << endl;
  360.       // now register 4 rounds with diminishing 
  361.       // cull percentage and threshold
  362.       for (int i=0; i<4; i++) {
  363. if (normspace_sample) {
  364.   // Sampling that tries to be somewhat uniform in the space of normals
  365.   vector<Pnt3> tmpverts, tmpnorms;
  366.   vector< vector<int> > normbuckets;
  367.   tmpverts.reserve(max(P->num_vertices(), Q->num_vertices()));
  368.   tmpnorms.reserve(max(P->num_vertices(), Q->num_vertices()));
  369.   
  370.   // Sample P
  371.   P->subsample_points(1.0, tmpverts, tmpnorms);
  372.   sort_into_buckets(tmpnorms, normbuckets);
  373.   int ndesired = int(ceil(Psample_rate * tmpverts.size()));
  374.   ssP.clear();  ssPn.clear();
  375.   while (ssP.size() < ndesired) {
  376.     for (int i=0; i < normbuckets.size(); i++) {
  377.       if (!normbuckets[i].empty()) {
  378. int ind = normbuckets[i].back();
  379. ssP.push_back(tmpverts[ind]);
  380. ssPn.push_back(tmpnorms[ind]);
  381. normbuckets[i].pop_back();
  382.       }
  383.     }
  384.   }
  385.   tmpverts.clear(); tmpnorms.clear(); normbuckets.clear();
  386.   // Sample Q
  387.   Q->subsample_points(1.0, tmpverts, tmpnorms);
  388.   sort_into_buckets(tmpnorms, normbuckets);
  389.   ndesired = int(ceil(Qsample_rate * tmpverts.size()));
  390.   ssQ.clear();  ssQn.clear();
  391.   while (ssQ.size() < ndesired) {
  392.     for (int i=0; i < normbuckets.size(); i++) {
  393.       if (!normbuckets[i].empty()) {
  394. int ind = normbuckets[i].back();
  395. ssQ.push_back(tmpverts[ind]);
  396. ssQn.push_back(tmpnorms[ind]);
  397. normbuckets[i].pop_back();
  398.       }
  399.     }
  400.   }
  401.   
  402. } else {
  403.   // random sampling
  404.   P->subsample_points(Psample_rate, ssP, ssPn);
  405.   Q->subsample_points(Qsample_rate, ssQ, ssQn);
  406. }   
  407. // the interpolation doesn't intentionally go all the way
  408. // (which would require a 5th round)
  409. find_pairs(((4-i)*thr_value+i*final_abs_thresh)/5.0);
  410. if (too_few_pairs()) return false;
  411. cull_pairs(((4-i)*20+i*1)/5.0);
  412. CM_align();
  413.       }
  414.       
  415.       // Now do sets of three iterations as long as the
  416.       // results seem to get better.
  417.       float prev_error, curr_error = 1.e33;
  418.       Xform<float> xfPg;
  419.       do {
  420. // update the error
  421. prev_error = curr_error;
  422. // copy the last good point pairs ...
  423. //_pP = pP; _nP = nP; _pQ = pQ; _nQ = nQ;
  424. // ... and the last known good transformations
  425. xfPg = xfP;
  426. // subsample both meshes
  427. if (normspace_sample) {
  428.   // Sampling that tries to be somewhat uniform in the space of normals
  429.   vector<Pnt3> tmpverts, tmpnorms;
  430.   vector< vector<int> > normbuckets;
  431.   tmpverts.reserve(max(P->num_vertices(), Q->num_vertices()));
  432.   tmpnorms.reserve(max(P->num_vertices(), Q->num_vertices()));
  433.   
  434.   // Sample P
  435.   P->subsample_points(1.0, tmpverts, tmpnorms);
  436.   sort_into_buckets(tmpnorms, normbuckets);
  437.   int ndesired = int(ceil(Psample_rate * tmpverts.size()));
  438.   ssP.clear();  ssPn.clear();
  439.   while (ssP.size() < ndesired) {
  440.     for (int i=0; i < normbuckets.size(); i++) {
  441.       if (!normbuckets[i].empty()) {
  442. int ind = normbuckets[i].back();
  443. ssP.push_back(tmpverts[ind]);
  444. ssPn.push_back(tmpnorms[ind]);
  445. normbuckets[i].pop_back();
  446.       }
  447.     }
  448.   }
  449.   tmpverts.clear(); tmpnorms.clear(); normbuckets.clear();
  450.   // Sample Q
  451.   Q->subsample_points(1.0, tmpverts, tmpnorms);
  452.   sort_into_buckets(tmpnorms, normbuckets);
  453.   ndesired = int(ceil(Qsample_rate * tmpverts.size()));
  454.   ssQ.clear();  ssQn.clear();
  455.   while (ssQ.size() < ndesired) {
  456.     for (int i=0; i < normbuckets.size(); i++) {
  457.       if (!normbuckets[i].empty()) {
  458. int ind = normbuckets[i].back();
  459. ssQ.push_back(tmpverts[ind]);
  460. ssQn.push_back(tmpnorms[ind]);
  461. normbuckets[i].pop_back();
  462.       }
  463.     }
  464.   }
  465.  
  466. } else {
  467.   P->subsample_points(Psample_rate, ssP, ssPn);
  468.   Q->subsample_points(Qsample_rate, ssQ, ssQn);
  469. }
  470. // do two rounds, calculate errors
  471. curr_error = 0.0;
  472. for (int i=0; i<2; i++) {
  473.   find_pairs(final_abs_thresh);
  474.   cull_pairs(1);
  475.   if (too_few_pairs()) return false;
  476.   curr_error += CM_align();
  477. }
  478.       } while (curr_error < .9999 * prev_error);
  479.       if (curr_error > prev_error) {
  480. // restore the last known good points
  481. xfP = xfPg;
  482. //pP = _pP; nP = _nP; pQ = _pQ; nQ = _nQ;
  483.       }
  484.       // once more, with no subsampling
  485.       cout << "last round...";
  486.       P->subsample_points(1.0, ssP, ssPn);
  487.       Q->subsample_points(1.0, ssQ, ssQn);
  488.       find_pairs(final_abs_thresh);
  489.       cull_pairs(1);
  490.       CM_align();
  491.       cout << "done" << endl;
  492.       if (max_motion < FLT_MAX) {
  493. // see how much the subsampled points in P would move
  494. float d = 0.0;
  495. Xform<float> xf = P->getXform();
  496. xf = xf.fast_invert() * xfP;
  497. for (vector<Pnt3>::const_iterator it = ssP.begin();
  498.      it != ssP.end(); it++) {
  499.   Pnt3 p = *it; xf(p);
  500.   d += dist(*it, p);
  501. }
  502. d /= ssP.size();
  503. if (d > max_motion) return false;
  504.       }
  505.       get_pairs_for_global(_pP, _nP, _pQ, _nQ);
  506.       rel_xf = xfQ;
  507.       rel_xf.fast_invert();
  508.       rel_xf = rel_xf * xfP;
  509.       return true;
  510.     }
  511.   // get the same pairs as were used in the last iteration,
  512.   // but in local coordinates
  513.   void get_pairs_for_global(vector<Pnt3> &_pP,
  514.     vector<Pnt3> &_nP,
  515.     vector<Pnt3> &_pQ,
  516.     vector<Pnt3> &_nQ)
  517.     {
  518.       _pP = pP;
  519.       _nP = nP;
  520.       Xform<float> xfi = xfP;
  521.       xfi.fast_invert();
  522.       for_each(_pP.begin(), _pP.end(), xfi);
  523.       xfi.removeTranslation();
  524.       for_each(_nP.begin(), _nP.end(), xfi);
  525.       _pQ = pQ;
  526.       _nQ = nQ;
  527.       xfi = xfQ;
  528.       xfi.fast_invert();
  529.       for_each(_pQ.begin(), _pQ.end(), xfi);
  530.       xfi.removeTranslation();
  531.       for_each(_nQ.begin(), _nQ.end(), xfi);
  532.     }
  533.   void show_lines(void) { enable(); }
  534.   void hide_lines(void) { disable(); }
  535.   void drawthis(void)
  536.     {
  537.       glDisable(GL_LIGHTING);
  538.       
  539.       glBlendFunc(GL_ONE, GL_ONE);
  540.       glEnable(GL_BLEND);
  541.       glDepthFunc(GL_LEQUAL);
  542.       
  543.       glBegin(GL_LINES);
  544.       glColor3f(1,0,0);
  545.       for (int i=0; i<firstend; i++) {
  546. glVertex3fv(pP[i]); glVertex3fv(pQ[i]);
  547.       }
  548.       glColor3f(0,1,0);
  549.       for (; i<pQ.size(); i++) {
  550. glVertex3fv(pP[i]); glVertex3fv(pQ[i]);
  551.       }
  552.       glEnd();
  553.     }
  554. };
  555. template <class T>
  556. class AutoICP {
  557. private:
  558.   ICP<T>       icp;
  559.   GlobalReg*   regall;
  560.   bool         bRegallPrivate;
  561.   vector<Pnt3> pP, nP, pQ, nQ;
  562.   float        final_abs_threshold;
  563.   float        max_motion;
  564. public:
  565.   // both parameters in the same units as data, usually mm
  566.   AutoICP(GlobalReg* pGlobalReg = NULL,
  567.   float _abs_threshold = FLT_MAX, 
  568.   float _max_motion = 20.0)
  569.     : final_abs_threshold(_abs_threshold), max_motion (_max_motion)
  570.     {
  571.       if (!pGlobalReg) {
  572. regall = new GlobalReg;
  573. bRegallPrivate = true;
  574.       } else {
  575. regall = pGlobalReg;
  576. bRegallPrivate = false;
  577.       }
  578.     }
  579.   ~AutoICP()
  580.     {
  581.       if (bRegallPrivate)
  582. delete regall;
  583.     }
  584.   bool add_pair(T a, T b, int max_pairs = 0, bool nss = false)
  585.     {
  586.       icp.set (a, b);
  587.       Xform<float> rel_xf;
  588.       bool success = icp.auto_align(pP, nP, pQ, nQ, 
  589.     rel_xf,
  590.     final_abs_threshold,
  591.     FLT_MAX, nss);
  592.       
  593.       if (success) {
  594. regall->addPair (a, b, pP, nP, pQ, nQ, 
  595.  rel_xf, false, max_pairs);
  596.       }
  597.       return success;
  598.     }
  599.   void operator()(void)
  600.     {
  601.       regall->align (1.e-3);
  602.     }
  603. };
  604. #endif