Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ImageAlignment.cpp
1 /*
2 This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4 Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10 BIAS is free software; you can redistribute it and/or modify
11 it under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
14 
15 BIAS is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Lesser General Public License for more details.
19 
20 You should have received a copy of the GNU Lesser General Public License
21 along with BIAS; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 
25 
26 #include <Matcher2D/ImageAlignment.hh>
27 #include <MathAlgo/SVD.hh>
28 #include <Base/Image/ImageIO.hh> // only for debug image output
29 #include <Base/Common/FileHandling.hh>
30 #include <Base/Common/BIASpragma.hh>
31 
32 // use isotropic or anisotropic parameter uncertainty
33 //#define ANISOTROPIC
34 
35 
36 // if grey value cannot be obtained, which value is set
37 #define UNDEF_VALUE 0.0
38 
39 using namespace BIAS;
40 using namespace std;
41 
43  MaxIterations_(20), MaxUpdate_(0.01), Dampening_(0.0),
44  AffineBrightnessInvariance_(false),
45  LineSearch_(0.3), intensitySigma_(1.0),
46  T_(NULL), mean1_(0.0),
47  mean2_(0.0), dev1_(1.0), dev2_(1.0),iterationDebug_(0) {
48 
49 }
50 
51 
54  Vector<double>& params, Matrix<double>& Cov, int PyramidLevel)
55 {
56  // make sure the user has set a warp model
57  BIASASSERT(T_!=NULL);
58 
59  int status = 0;
60  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
61  "compute template and gradients from warped I1 with prediction "
62  <<params);
63  T_->SetParameters(params);
64 
65  Matrix<double> PIJ;
66  if (T_->ParameterInversionJacobian(PIJ)!=0) return -3;
67  const Matrix<double> CovInv(PIJ * Cov * PIJ.Transpose());
68 
69  // check whether we have to re-evaluate for each pixel:
70  const bool TextureJacobianIsConstant = T_->TextureJacobianIsConstant();
71  const bool ParameterJacobianIsConstant = T_->ParameterJacobianIsConstant();
72 
73  // reset active flag
74  for (unsigned int i=0; i<active_.size(); i++) active_[i] = true;
75 
76 
77  if (PyramidLevel<0) {
78  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
79  "auto pyramid, determine required scale for each pixel");
80  Matrix<double> JacParams;
81  Matrix2x2<double> JacTex;
82 #ifdef BIAS_DEBUG
83  Image<float> *ptheweights = NULL;
84  float *pwdata = NULL;
85  // save debug image with weights
86  if ((GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES)) {
87  int numberOfPixelsPerRow =
88  int(ceil(sqrt(double(TemplatePositions_.size()))));
89 #ifdef ANISOTROPIC
90  ptheweights = new Image<float>(numberOfPixelsPerRow,
91  numberOfPixelsPerRow,3);
92 #else
93  ptheweights = new Image<float>(numberOfPixelsPerRow,
94  numberOfPixelsPerRow,1);
95 #endif
96  ptheweights->SetZero();
97  pwdata = ptheweights->GetImageData();
98  }
99 #endif
100  for (unsigned int p=0; p<TemplatePositions_.size(); p++) {
101  // which pixel in the source image ?
102  const HomgPoint2D& x(TemplatePositions_[p]);
103  // where does the prediction map it ?
104  T_->MapForward(x, ImagePositions_[p]);
105  // need new jacobian ?
106  if (p==0 || !ParameterJacobianIsConstant)
107 
108  // if (T_->ParameterJacobianForward(JacParams, x)!=0) return -4;
109  if (T_->ParameterJacobianBackward(JacParams, ImagePositions_[p])!=0)
110  return -4;
111 
112 
113  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
114  "propagate parameter uncertainty "<<Cov);
115  // approximate isotropic 2d uncertainty of this pixel's position
116  BIAS::Matrix2x2<double> PixelCov(JacParams*Cov*JacParams.Transpose());
117  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, " to pixel cov "<<PixelCov);
118  double scale =
119  sqrt(PixelCov.Trace());
120  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, " to position uncertainty "<<scale);
121  // compute best pyramid level to access and save grey value
122  double pyrLevel = 0.0;
123  if (scale<=0.5) scale = 0.5;
124  else if (scale>I1.size()-1.0) {
125  scale = I1.size()-1.0;
126  pyrLevel = log2(scale) + 1.0;
127  } else {
128  pyrLevel = log2(scale) + 1.0;
129  }
130  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, "Using pyramid level "<<pyrLevel);
131  TemplateScales_[p] = scale;
132  // check whether we can obtain all grey values:
133  int checker = 0;
134 #ifdef ANISOTROPIC
135  checker =
136  I1.GetAnisotropicImageValue(x[0], x[1], PixelCov,
137  TemplateGreyValues_[p]);
138 
139  if (checker!=0) {
140  // try to obtain reasonable grey value anyway
141  if (I1[0]->IsInROI(x[0], x[1]))
142  TemplateGreyValues_[p] = I1.GetImageValue(x[0], x[1], 0);
143  else {
144  BIASERR(x<<" is not in ROI of source!!!");
145  TemplateGreyValues_[p] = UNDEF_VALUE;
146  }
147  }
148 
149  // approximate gradient at that level
150  // could be improved by simply convolving with gaussian derivative
151  double gv1,gv2, gv3,gv4;
152  checker+=
153  I1.GetAnisotropicImageValue(x[0]+0.5*scale, x[1], PixelCov, gv1);
154  checker+=
155  I1.GetAnisotropicImageValue(x[0]-0.5*scale, x[1], PixelCov, gv2);
156  checker+=
157  I1.GetAnisotropicImageValue(x[0], x[1]+0.5*scale, PixelCov, gv3);
158  checker+=
159  I1.GetAnisotropicImageValue(x[0], x[1]-0.5*scale, PixelCov, gv4);
160  Vector2<double> grad(gv1-gv2, gv3-gv4);
161 #else
162  checker =
163  I1.GetTrilinearImageValue(x[0], x[1], pyrLevel, TemplateGreyValues_[p]);
164 
165  if (checker!=0) {
166  // try to obtain reasonable grey value anyway
167  if (I1[0]->IsInROI(x[0], x[1]))
168  TemplateGreyValues_[p] = I1.GetImageValue(x[0], x[1], 0);
169  else {
170  BIASERR(x<<" is not in ROI of source!!!");
171  TemplateGreyValues_[p] = UNDEF_VALUE;
172  }
173  }
174  double gv1,gv2, gv3,gv4;
175  checker+= I1.GetTrilinearImageValue(x[0]+0.5*scale, x[1], pyrLevel, gv1);
176  checker+= I1.GetTrilinearImageValue(x[0]-0.5*scale, x[1], pyrLevel, gv2);
177  checker+= I1.GetTrilinearImageValue(x[0], x[1]+0.5*scale, pyrLevel, gv3);
178  checker+= I1.GetTrilinearImageValue(x[0], x[1]-0.5*scale, pyrLevel, gv4);
179  Vector2<double> grad(gv1 - gv2, gv3-gv4);
180 #endif
181  if (p==0 || !TextureJacobianIsConstant)
182  if (T_->TextureJacobianForward(x, JacTex)!=0) return -5;
183 
184  if (checker!=0) {
185  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Could not obtain grey value for "
186  <<x<<" at pyramid level "
187  <<pyrLevel);
188  active_[p] = false;
189  continue;
190  } else {
191  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Using "<<x<<" at pyramid level "
192  <<pyrLevel<<" with grey value "<<TemplateGreyValues_[p]);
193  }
194  // approximate gradient at that level
195  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Rotate and scale gradient "
196  <<grad[0]<<";"<<grad[1]);
197 
198  // rotate gradient with texture transformation and divide secant length
199  TemplateGradients_[p] = JacTex * (grad * 1.0 / scale);
200  active_[p] = TemplateGradients_[p].NormL2()*scale>intensitySigma_;
201  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"GRAD="<<TemplateGradients_[p]
202  <<(active_[p]?" active":" skipped"));
203 
204 
205 #ifdef BIAS_DEBUG
206  if ((GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES)) {
207 #ifdef ANISOTROPIC
208  *pwdata ++ = PixelCov[0][0];
209  *pwdata ++ = PixelCov[0][1];
210  *pwdata ++ = PixelCov[1][1];
211 #else
212  *pwdata ++ = pyrLevel;
213 #endif
214  }
215 #endif
216  INFNANCHECK(TemplateGreyValues_[p]);
217  }
218 #ifdef BIAS_DEBUG
219  // save debug image with weights
220  if ((GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES)) {
221  //ImageIO::Save(string("tlevels-")+FileHandling::toString(iterationDebug_)
222  // +string(".mip"), *ptheweights);
223  ImageIO::Save(string("tlevels-")
224  +FileHandling::toString(iterationDebug_)
225  +string(".mip"),
226  *ptheweights);
227  delete ptheweights;
228  }
229 #endif
230  } else {
231  // use user specified pyramid level
232  const double scale = pow(2.0,PyramidLevel);
233  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Using pyramid level "
234  <<PyramidLevel<<" for grey values and gradients (scale="<<scale
235  <<")");
236  Matrix2x2<double> JacTex;
237  for (unsigned int p=0; p<TemplatePositions_.size(); p++) {
238  const HomgPoint2D& x(TemplatePositions_[p]);
239  T_->MapForward(x, ImagePositions_[p]);
240  int checker = 0;
241  checker += I1.GetTrilinearImageValue(x[0], x[1], PyramidLevel,
242  TemplateGreyValues_[p]);
243 
244  if (checker!=0) {
245  // try to obtain reasonable grey value anyway
246  if (I1[0]->IsInROI(x[0], x[1]))
247  TemplateGreyValues_[p] = I1.GetImageValue(x[0], x[1], 0);
248  else {
249  BIASERR(x<<" is not in ROI of source!!!");
250  TemplateGreyValues_[p] = UNDEF_VALUE;
251  }
252  }
253 
254  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, "Pixel grey value at "<<x<<" is "
255  <<TemplateGreyValues_[p]);
256  INFNANCHECK(TemplateGreyValues_[p]);
257 
258  // hier kann noch kraeftig optimiert werden, selbe gewichte fuer alle !
259  // trilinear ist unnoetig, benutze naechste ganze pyramidenstufe
260  // approximate gradient at that level
261  double gv1,gv2, gv3,gv4;
262  checker += I1.GetTrilinearImageValue(x[0]+0.5*scale, x[1],
263  double(PyramidLevel), gv1);
264  checker += I1.GetTrilinearImageValue(x[0]-0.5*scale, x[1],
265  double(PyramidLevel), gv2);
266  checker += I1.GetTrilinearImageValue(x[0], x[1]+0.5*scale,
267  double(PyramidLevel), gv3);
268  checker += I1.GetTrilinearImageValue(x[0], x[1]-0.5*scale,
269  double(PyramidLevel), gv4);
270 
271  Vector2<double> grad(gv1 - gv2, gv3-gv4);
272 
273  // compute jacobian only at first time or when not constant
274  if (p==0 || !TextureJacobianIsConstant)
275  if (T_->TextureJacobianForward(x, JacTex)!=0) return -6;
276 
277  if (checker != 0) {
278  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Could not obtain grey value for "
279  <<x<<" at pyramid level "<<PyramidLevel);
280  active_[p] = false;
281  continue;
282  }
283 
284 
285 
286  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
287  "rotate gradient "<<grad[0]<<";"<<grad[1]
288  <<" with local texture warp ["<<JacTex[0][0]<<" "<<JacTex[0][1]
289  <<"; "<<JacTex[1][0]<<" "<<JacTex[1][1]<<"]");
290  TemplateGradients_[p] = JacTex * (grad * 1.0 / scale);
291  double gradnorm = TemplateGradients_[p].NormL2();
292  active_[p] = (gradnorm*scale>intensitySigma_);
293  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"GRAD="<<TemplateGradients_[p]
294  <<(active_[p]?" active":" skipped"));
295 
296  if ((BIAS_ISINF(TemplateGreyValues_[p])||BIAS_ISNAN(TemplateGreyValues_[p]))
297  &&active_[p]) {
298  BIASERR("bad value for active pixel..");
299  BIASABORT;
300  }
301  }
302  }
303 
304  // ------------- account for appearance / illumination changes ? ---------
305  if (AffineBrightnessInvariance_) {
306  // use only active ?
307  // inactive pixels might not have a correct grey value !
308  // active however have a strong gradient which is not suitable for
309  // brightness estiamtion!
310 
311  // compute mean and scatter of patch
312  double mean = 0;
313  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
314  mean += TemplateGreyValues_[i];
315  }
316  mean1_ = mean / TemplateGreyValues_.size();
317 
318  double scatter = 0;
319  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
320  scatter += (TemplateGreyValues_[i]-mean1_) *
321  (TemplateGreyValues_[i]-mean1_);
322  }
323  scatter = scatter/(TemplateGreyValues_.size()-1);
324  if (scatter>0.01*intensitySigma_) dev1_ = sqrt(scatter);
325  else dev1_ = 0.1*intensitySigma_;
326 
327  // now rescale grey values such that mean=0 and sigma=1 afterwards
328  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
329  TemplateGreyValues_[i] = (TemplateGreyValues_[i]-mean1_)/dev1_;
330  TemplateGradients_[i] /= dev1_;
331  }
332  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
333  "Affine normalization of gradients and grey values yielded mean="
334  <<mean1_<<" and dev="<<dev1_);
335  } else {
336  dev1_ = 1.0;
337  mean1_ = 0.0;
338  }
339 
340  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "compute jacobians + hessian");
341  // prepare compositional inverse update (start with 0)
342  TextureTransform *pUpdateWarp = T_->Clone();
343 
344 #ifdef BIAS_DEBUG
345  if (GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES) {
346  int numberOfPixelsPerRow =
347  int(ceil(sqrt(double(TemplateGreyValues_.size()))));
348  Image<float> thetemplate(numberOfPixelsPerRow,numberOfPixelsPerRow,1);
349  Image<float> thegradients(numberOfPixelsPerRow,numberOfPixelsPerRow,3);
350  thegradients.SetZero();
351  float *pF = thetemplate.GetImageData();
352  float *pG = thegradients.GetImageData();
353  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
354  *pF++ = TemplateGreyValues_[i];
355  if (active_[i]) {
356  // write active into green
357  pG++;*pG++ = TemplateGradients_[i].NormL2();pG++;
358  } else {
359  // write low into into red
360  *pG++ = TemplateGradients_[i].NormL2();pG++;pG++;
361  }
362  }
363  /*
364  ImageIO::Save(string("template-")+FileHandling::toString(iterationDebug_)
365  +string(".mip"), thetemplate);
366  ImageIO::Save(string("gradients-")+FileHandling::toString(iterationDebug_)
367  +string(".mip"), thegradients); */
368 
369  ImageIO::Save(string("template-")
370  +FileHandling::toString(iterationDebug_)
371  +string(".mip"),
372  thetemplate);
373  ImageIO::Save(string("gradients-")
374  +FileHandling::toString(iterationDebug_)
375  +string(".mip"),
376  thegradients);
377  }
378 #endif
379 
380  Vector<double> parameterUpdate;
381  T_->GetParameters(parameterUpdate);
382  for (unsigned int i=0; i<parameterUpdate.Size(); i++)
383  parameterUpdate[i] = 0;
384  pUpdateWarp->SetParameters(parameterUpdate);
385 
386  Matrix<double> Jac;
387  Matrix<double> Hessian(parameterUpdate.Size(), parameterUpdate.Size(),
388  MatrixZero);
389  Matrix<double> invCovInv;
390  unsigned int numMeasurements = 0;
391  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
392  // need new jacobian ?
393  if (i==0 || !ParameterJacobianIsConstant)
394  if (pUpdateWarp->ParameterJacobianBackward(Jac, ImagePositions_[i])!=0)
395  return -7;
396 
397  // no gradient, skip!
398  if (!active_[i]) continue;
399 
400  Jac.MultLeft(TemplateGradients_[i], SteepestDescentImages_[i]);
401  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Steepest descent image is "<<
402  SteepestDescentImages_[i]);
403  // add to hessian"
404  Hessian +=
405  SteepestDescentImages_[i].OuterProduct(SteepestDescentImages_[i]);
406  numMeasurements++;
407  }
408 
409  // incorporate prior on the warp parameters ?
410  if (Dampening_>0.0) {
411  // if the intensity noise is 1, then a dampening of 1 leads to the
412  // maximum a posteriori estimate of p (without brightness invariance mode)
413 
414 #ifdef BIAS_DEBUG
415  bool CovInvgood = true;
416  for (int i=0; i<CovInv.num_rows(); i++) {
417  for (int j=0; j<CovInv.num_cols(); j++) {
418  if (BIAS_ISNAN(CovInv[i][j]) || BIAS_ISINF(CovInv[i][j])) {
419  CovInvgood = false;
420  }
421  }
422  }
423  if (!CovInvgood) {
424  BIASERR("Bad CovInv "<<CovInv<<" cov was "<<Cov<<" lastPIJ="<<PIJ);
425  BIASABORT;
426  }
427 #endif
428 
429  SVD covinvsvd(false);
430  if (covinvsvd.Compute(CovInv)==0) {
431  invCovInv = covinvsvd.Invert();
432  Hessian += invCovInv * intensitySigma_ * intensitySigma_ *
433  Dampening_ / (dev1_*dev1_);
434  } else {
435  // cannot decompose
436  BIASERR("Bad covariance, cannot decompose: "<<CovInv);
437  }
438  }
439 
440  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Finished Hessian, "
441  <<Hessian<<", inverting ...");
442  // the hessian must have full rank here, otherwise we have a bad feature:
443  // "highdimensional aperture problem"
444 
445 #ifdef BIAS_DEBUG
446  //cout<<"Hessian is "<<setprecision(20)<<Hessian<<endl;
447  bool hessiangood = true;
448  for (int i=0; i<Hessian.num_rows(); i++) {
449  for (int j=0; j<Hessian.num_cols(); j++) {
450  if (BIAS_ISNAN(Hessian[i][j]) || BIAS_ISINF(Hessian[i][j])) {
451  hessiangood = false;
452  }
453  }
454  }
455  if (!hessiangood) {
456  BIASERR("Bad Hessian !!!");
457  cout<<"Last Jacobian was "<< Jac<<endl;
458  for (unsigned int i=0; i<SteepestDescentImages_.size(); i++) {
459  cout<<"SDI "<<i<<" ";
460  if (active_[i]) {
461  cout<<SteepestDescentImages_[i];
462  } else {
463  cout<<"is disabled."<<endl;
464  }
465  }
466  }
467 #endif
468 
469  SVD Hsvd(Hessian);
470 
471  Vector<double> S = Hsvd.GetS();
472  if (Dampening_<0.0) {
473  for (int i=0; i<Hessian.num_cols(); i++) {
474  Hessian[i][i] -= S[0] * Dampening_;
475  }
476  Hsvd.Compute(Hessian);
477  } else if ((S[0]<1e-9) || (S[S.Size()-1]/S[0]<1e-16)) {
478  // cannot solve !
479  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Not enough structure for "
480  <<" uniquely solving for all params in H: "<<Hessian
481  <<", singular values are "<<S);
482  return -1;
483  }
484  Hessian = Hsvd.Invert();
485 
486  //--------------------- iterate --------------------
487  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Starting iterations");
488 
489  int iter = 1;
490  bool converged = false;
491  Vector<double> errorImage(TemplateGreyValues_.size());
492  vector<double> greyValuesI2(TemplateGreyValues_.size());
493 #ifdef ANISOTROPIC
494  vector<Matrix2x2<double> > pyramidLevelsI2(TemplateGreyValues_.size());
495 #else
496  vector<double> pyramidLevelsI2(TemplateGreyValues_.size());
497 #endif
498  Vector<double> steepestDescentImageSum(parameterUpdate.Size());
499 
500  // remember the best-so-far residual
501  double lastresidual = DBL_MAX;
502  double residual = 0;
503  double pyrLevel = 0;
504 
505  // remember best parameters so far
506  Vector<double> lastGoodParameters;
507  pUpdateWarp->GetParameters(lastGoodParameters);
508  while (!converged) {
509  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Iteration "<<iter);
510  // warp I2 with compositional parameters and compute intensity difference
511  HomgPoint2D curPos(0,0);
512  steepestDescentImageSum.SetZero();
513 #ifdef BIAS_DEBUG
514  if (GetDebugLevel() & D_IMAGEALIGNMENT_PARAMETER) {
515  pUpdateWarp->GetParameters(parameterUpdate);
516  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER,
517  "Current parameter update onto prediction is "
518  <<parameterUpdate);
519  }
520 #endif
521  // fetch predicted, raw, grey value
522  if (PyramidLevel<0) {
523  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, "Cov is "<<Cov);
524  // use resolution from covariance
525  Matrix<double> JacParams;
526  BIAS::Matrix<double> covI2;
527  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
528  HomgPoint2D &x(ImagePositions_[i]);
529  pUpdateWarp->MapBackward(x, curPos);
530  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Looking up Pixel at "<< curPos);
531  // first iteration: need parameter jacobian fpr pyrLevel calculation:
532  if (iter==1) {
533  if ((i==0 || !ParameterJacobianIsConstant)) {
534  if (pUpdateWarp->ParameterJacobianBackward(JacParams, x)!=0)
535  return -8;
536  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
537  " PJBW="<<JacParams);
538 #ifdef ANISOTROPIC
539  covI2 = JacParams*Cov*JacParams.Transpose();
540  double scale = covI2.Trace();
541 #else
542  double scale =
544  (JacParams*Cov*JacParams.Transpose()).Trace());
545 #endif
546  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
547  " position uncertainty "<<scale);
548  // compute best pyramid level to access and save grey value"
549  pyrLevel = 0.0;
550  if (scale<=0.5) scale = 0.5;
551  else if (scale>I1.size()-1.0) {
552  scale = I1.size()-1.0;
553  pyrLevel = log2(scale) + 1.0;
554  } else {
555  pyrLevel = log2(scale) + 1.0;
556  }
557  }
558 #ifdef ANISOTROPIC
559  pyramidLevelsI2[i] = covI2;
560 #else
561  pyramidLevelsI2[i] = pyrLevel;
562 #endif
563 #ifdef BIAS_DEBUG
564  // save debug image with weights
565  if ((GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES) &&
566  (i+1==TemplateGreyValues_.size())) {
567  int numberOfPixelsPerRow =
568  int(ceil(sqrt(double(pyramidLevelsI2.size()))));
569 #ifdef ANISOTROPIC
571  theweights(numberOfPixelsPerRow,
572  numberOfPixelsPerRow,3);
573 #else
575  theweights(numberOfPixelsPerRow,
576  numberOfPixelsPerRow,1);
577 #endif
578  theweights.SetZero();
579  float *pF = theweights.GetImageData();
580  for (unsigned int j=0; j<pyramidLevelsI2.size(); j++) {
581 #ifdef ANISOTROPIC
582  *pF++ = pyramidLevelsI2[j][0][0];
583  *pF++ = pyramidLevelsI2[j][0][1];
584  *pF++ = pyramidLevelsI2[j][1][1];
585 #else
586  *pF++ = pyramidLevelsI2[j];
587 #endif
588  }
589  //ImageIO::Save(string("levels-")+FileHandling::toString(iterationDebug_)
590  // +string(".mip"), theweights);
591  ImageIO::Save(string("levels-")
592  +FileHandling::toString(iterationDebug_)
593  +string(".mip"),
594  theweights);
595  }
596 #endif
597  }
598 
599 #ifdef ANISOTROPIC
600  if (I2.GetAnisotropicImageValue(curPos[0],curPos[1],pyramidLevelsI2[i],
601  greyValuesI2[i])!=0) {
602  // remove from active set!
603  greyValuesI2[i] = TemplateGreyValues_[i];
604  active_[i] = false;
605  }
606 
607 #else
608  if (I2.GetTrilinearImageValue(curPos[0], curPos[1], pyramidLevelsI2[i],
609  greyValuesI2[i])!=0) {
610  // remove from active set!
611  greyValuesI2[i] = TemplateGreyValues_[i];
612  active_[i] = false;
613  }
614 #endif
615 
616  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Using "<<curPos
617  <<" at pyramid level "<<pyramidLevelsI2[i]
618  <<" with grey value(2) "<<greyValuesI2[i]);
619  INFNANCHECK(greyValuesI2[i]);
620  }
621  } else {
622  // user specified pyramid level
623  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
624  pUpdateWarp->MapBackward(ImagePositions_[i], curPos);
625  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Looking up Pixel at "<< curPos);
626  if (I2.IsInROI(curPos[0], curPos[1], PyramidLevel))
627  greyValuesI2[i] =
628  I2.GetImageValue(curPos[0], curPos[1], PyramidLevel);
629  else {
630  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Not in ROI, disabling! ");
631  greyValuesI2[i] = TemplateGreyValues_[i];
632  active_[i] = false;
633  }
634  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Grey value is "<< greyValuesI2[i]);
635  INFNANCHECK(greyValuesI2[i]);
636  }
637  }
638 
639  if (AffineBrightnessInvariance_) {
640 
641  // check active here ?!
642 
643  // compute mean+scatter of patch
644  double mean = 0;
645  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
646  mean += greyValuesI2[i];
647  }
648  mean2_ = mean / TemplateGreyValues_.size();
649  double scatter = 0;
650  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
651  scatter += (greyValuesI2[i] - mean2_)*(greyValuesI2[i] - mean2_);
652  }
653  scatter /= TemplateGreyValues_.size()-1;
654  if (scatter>0.01*intensitySigma_) dev2_ = sqrt(scatter);
655  else dev2_ = 0.1*intensitySigma_;
656 
657  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
658  "Affine normalization of grey values(2) yielded mean="
659  <<mean2_<<" and dev="<<dev2_);
660 
661  // compute normalized grey value residuum
662  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
663  greyValuesI2[i] = (greyValuesI2[i]-mean2_)/dev2_;
664  }
665  }
666 #ifdef BIAS_DEBUG
667  if (GetDebugLevel() & D_IMAGEALIGNMENT_IMAGES) {
668  int numberOfPixelsPerRow = int(ceil(sqrt(double(greyValuesI2.size()))));
669  Image<float> theimage(numberOfPixelsPerRow,numberOfPixelsPerRow,1);
670  float *pF = theimage.GetImageData();
671  for (unsigned int g=0; g<greyValuesI2.size(); g++) {
672  *pF++ = greyValuesI2[g];
673  }
674  /*
675  ImageIO::Save(string("image-")+FileHandling::toString(iterationDebug_)
676  +string("-")+FileHandling::toString(iter)+string(".mip"),
677  theimage); */
678  ImageIO::Save(string("image-")
679  +FileHandling::toString(iterationDebug_)
680  +string("-")
682  +string(".mip"),
683  theimage);
684  }
685 #endif
686 
687  //Vector<double> b(TemplateGreyValues_.size());
688  //Matrix<double> A(TemplateGreyValues_.size(), 2, MatrixZero);
689 
690  // now weight steepestDescent images by residuum
691  residual = 0;
692  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
693  // no good gradient ...
694  if (!active_[i]) {
695  // A[i][0] = 0;
696  // A[i][1] = 0;
697  // b[i] = 0;
698  continue;
699  }
700  // A[i][0] = dev2_ * greyValuesI2[i] + mean2_;
701  // A[i][1] = 1;
702  // b[i] = dev1_ * TemplateGreyValues_[i] + mean1_;
703 
704  errorImage[i] = greyValuesI2[i] - TemplateGreyValues_[i];
705  residual += errorImage[i]*errorImage[i];
706  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,"Grey value difference is "<<
707  errorImage[i]);
708 
709  steepestDescentImageSum += errorImage[i] * SteepestDescentImages_[i];
710 
711  }
712  // SVD mysvd(A.Transpose()*A);
713  // Vector<double> result = mysvd.Invert()*A.Transpose()*b;
714  // cout<<" brightness is "<<result<<endl;
715 
716  residual = sqrt(residual/numMeasurements);
717  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "RMS residual is "<<residual);
718  if (residual>lastresidual) {
719  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Error increase from "
720  <<lastresidual<<". Stopping.");
721  if (LineSearch_>0.0) {
722  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "Step did not improve, trying a "
723  <<LineSearch_<<" smaller step.");
724  pUpdateWarp->SetParameters(lastGoodParameters);
725  // reduce update
726  parameterUpdate *= LineSearch_;
727  } else {
728  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS, "No further improvement.Stopping.");
729  // error did not improve ...
730  pUpdateWarp->SetParameters(lastGoodParameters);
731  status = 2;
732  break;
733  }
734  } else {
735  pUpdateWarp->GetParameters(lastGoodParameters);
736  lastresidual = residual;
737  // solve for parameter update
738  if (Dampening_<=0.0) {
739  Hessian.Mult(steepestDescentImageSum, parameterUpdate);
740  } else {
741  // For MAP estimation with a prior centered on the original parameters
742  // its original mean must be considered here. Otherwise the prior
743  // moves with iterations from the current parameter set.
744 
745  // obtain weighting of "correct" prior with scaled intensities
746  double weightfactor = (intensitySigma_*intensitySigma_) / (dev2_*dev2_);
747  parameterUpdate = Hessian * (steepestDescentImageSum -
748  weightfactor*invCovInv*pUpdateWarp->GetInverseParameters());
749 
750 #ifdef BIAS_DEBUG
751  for (unsigned int i=0; i<parameterUpdate.Size(); i++) {
752  if (BIAS_ISINF(parameterUpdate[i]) || BIAS_ISNAN(parameterUpdate[i])) {
753  BIASERR("bad result:"<<parameterUpdate<<" invCovinv="<<invCovInv
754  <<" puw="<<pUpdateWarp->GetInverseParameters()
755  <<" hessian="<<Hessian<<" sdis="<<steepestDescentImageSum);
756 
757 
758 
759  cout<<"Grey value data ("<<TemplateGreyValues_.size()<<")"<<endl;
760  for (unsigned int r=0; r<TemplateGreyValues_.size(); r++) {
761  // no good gradient ...
762  if (!active_[r]) continue;
763  cout<< errorImage[r] <<"="
764  <<greyValuesI2[r]<<"-"<< TemplateGreyValues_[r];
765 
766  cout<<"SDI="<< SteepestDescentImages_[i]<<endl;
767  }
768  BIASABORT;
769  }
770  }
771 #endif
772  }
773  }
774  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER,
775  "inv.compositional parameter update is "
776  <<parameterUpdate);
777  if (parameterUpdate.NormL2()>1000) {
778  // we expect a change close to the identity => p=0
779  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Diverged! ");
780  return -20;
781  }
782  // concatenate parameters
783  pUpdateWarp->ComposeWithInverseDeltaP(parameterUpdate);
784  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Update of "
785  <<parameterUpdate.NormL2()<<" thresh="<<MaxUpdate_);
786  if (parameterUpdate.NormL2()<MaxUpdate_) {
787  converged = true;
788  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Convergence ! Update = "
789  <<parameterUpdate.NormL2());
790  } else if (++iter>MaxIterations_) {
791  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Too many iterations: "<<iter);
792  status = 1;
793  break;
794  }
795  }
796  pUpdateWarp->GetParameters(parameterUpdate);
797  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
798  "Finished. Total parameter update onto prediction is "
799  <<parameterUpdate);
800  // merge both direction parameters
801  T_->ComposeWithInverseDeltaP(parameterUpdate);
802  // and output parameters
803  T_->GetParameters(params);
804 #ifdef BIAS_DEBUG
805  for (unsigned int i=0; i<params.Size(); i++) {
806  INFNANCHECK(params[i]);
807  }
808 #endif
809  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Final warp parameters are "<<params);
810 
811  // update covariance
812  if (numMeasurements>params.Size()) {
813  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER,"residual is now "<<residual);
814  double additionalresidual = 0;
815  for (unsigned int i=0; i<TemplateGreyValues_.size(); i++) {
816  // no good gradient ...
817  if (!active_[i]) continue;
818  // if we used this pixel not at the highest resolution, we must expect
819  // the grey value residual to be larger in reality
820  if (TemplateScales_[i]>0.5)
821  additionalresidual +=
822  (TemplateGradients_[i]*(TemplateScales_[i]-0.5)).NormL2();
823  }
824  residual += additionalresidual/double(numMeasurements);
825  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER,"residual changed to "<<residual);
826 
827 
828  // avoid singular ref variance by adding 1% of intensity sigma
829  double refvar = 0.0001 +
830  (residual*residual*dev1_*dev1_) * double(numMeasurements) /
831  (double(numMeasurements-params.size())*intensitySigma_*intensitySigma_);
832 
833 
834  // assume parameterUpdate is nearly zero
835  Matrix<double> upJac;
836  if (pUpdateWarp->ParameterInversionJacobian(upJac)!=0) return -10;
837 
838 #ifdef BIAS_DEBUG
839  // cout<<"Hessian is "<<setprecision(20)<<Hessian<<endl;
840  bool hessiangood = true;
841  for (int i=0; i<upJac.num_rows(); i++) {
842  for (int j=0; j<upJac.num_cols(); j++) {
843  if (BIAS_ISNAN(upJac[i][j]) || BIAS_ISINF(upJac[i][j])) {
844  hessiangood = false;
845  }
846  }
847  }
848  if (!hessiangood) {
849  Vector<double> badparams(6);
850  pUpdateWarp->GetParameters(badparams);
851  BIASERR("Bad upjac !!!"<< upJac<<" or PIJ "<<PIJ<<" for params "
852  <<badparams);
853  }
854 #endif
855 
856  // combine jacobians
857  SVD covtransformSVD(upJac*PIJ);
858  PIJ = covtransformSVD.Invert();
859  Hessian *= refvar;
860  Cov = PIJ * Hessian * PIJ.Transpose();
861  } else {
862  // no redundancy, perfect linear fit ...
863  Cov.SetIdentity();
864  Cov *= 1e20;
865  BIASWARN("Could not estimate covariance, setting large value");
866  }
867 
868  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Covariance changed to "<<Cov);
869  return status;
870 }
871 
872 
875  int numberOfPixelsPerRow)
876 {
877  // automatic resolution ?
878  if (numberOfPixelsPerRow<1) {
879  // use feature area ("all" pixels at full resolution
880  numberOfPixelsPerRow = int(rint(sqrt(4.0*LAF.GetA().det())))+1;
881  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"NumOfPixelsPerRow automatically "
882  <<" computed as "<<numberOfPixelsPerRow<<" with A="<<LAF.GetA());
883  }
884 
885  double pixeldistance = 2.0/(numberOfPixelsPerRow-1);
886  if (numberOfPixelsPerRow<2) pixeldistance = 1.0;
887 
888  int numberOfPixels = numberOfPixelsPerRow * numberOfPixelsPerRow;
889  TemplateGreyValues_.resize(numberOfPixels);
890  TemplateGradients_.resize(numberOfPixels);
891  TemplatePositions_.resize(numberOfPixels);
892  TemplateScales_.resize(numberOfPixels);
893  ImagePositions_.resize(numberOfPixels);
894  SteepestDescentImages_.resize(numberOfPixels);
895  active_.resize(numberOfPixels);
896  ControlPositions_.resize(4);
897  const double offset = -1.0;
898  for (int y=0; y<numberOfPixelsPerRow; y++) {
899  for (int x=0; x<numberOfPixelsPerRow; x++) {
900  int index = y*numberOfPixelsPerRow+x;
901  TemplatePositions_[index] =
902  LAF.LocalToGlobal(HomgPoint2D(x*pixeldistance+offset,
903  y*pixeldistance+offset));
904  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
905  "Using Position "<<TemplatePositions_[y*numberOfPixelsPerRow+x]);
906  }
907  }
908  ControlPositions_[0] = TemplatePositions_[0];
909  ControlPositions_[1] = TemplatePositions_[numberOfPixelsPerRow-1];
910  ControlPositions_[2] =
911  TemplatePositions_[numberOfPixels-numberOfPixelsPerRow-1];
912  ControlPositions_[3] =
913  TemplatePositions_[numberOfPixels-1];
914 
915  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Have "<<numberOfPixels
916  <<" pixels with distance "<<pixeldistance<<" in local affine frame");
917 
918 }
919 
920 
921 
922 
924  const PyramidImage<float>& I2,
925  Vector<double>& params,
926  Matrix<double>& Cov) {
927  int returnvalue = 0;
928  double oldLineSearch = LineSearch_;
929 
930  double trace = Cov.Trace();
931  double lasttrace = trace + 1e100;
932  Matrix<double> initialCov(Cov);
933  double desiredAccuracy = GetMaxUpdate();
934  const unsigned int numparams = params.size();
935  BIASASSERT(Cov.num_rows()==int(numparams));
936  BIASASSERT(Cov.num_cols()==int(numparams));
937  iterationDebug_ = 0;
938  Matrix<double> lastCov(4.0*Cov);
939  try {
940  while (trace<lasttrace) {
941  lasttrace = trace*0.95; // want 5% improvement to continue
942 
943  // make symmetric and slightly decrease
944  for (unsigned int i=0; i<numparams; i++) {
945  for (unsigned int j=i; j<numparams; j++) {
946  if (BIAS_ISINF(Cov[j][i]) || BIAS_ISNAN(Cov[j][i])) {
947  BIASERR("corrupted cov, giving up "<<Cov);
948  return -1;
949  }
950  Cov[j][i] = Cov[i][j] =
951  (Cov[i][j] + Cov[j][i] + 0.5*lastCov[j][i])*0.25;
952  }
953  // if any diagonal element got more uncertain, set row/col back
954  if (Cov[i][i]>lastCov[i][i]) {
955  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER, "Cov increased:"<<Cov
956  <<" from "<<lastCov);
957  if (lastCov[i][i]<1e-10) lastCov[i][i] = 1e-10;
958  double factor = sqrt(lastCov[i][i]/Cov[i][i]);
959  for (unsigned int j=0; j<numparams; j++) {
960  Cov[i][j] = Cov[j][i] = Cov[j][i]*factor;
961  }
962  Cov[i][i] *= factor;
963  BIASDOUT(D_IMAGEALIGNMENT_PARAMETER, "Cov clipped to "<<Cov);
964  }
965  Cov[i][i] += 1e-10; // avoid zero digonal
966  }
967  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Aligning with Cov="<<Cov);
968  // we expect uncertainty in the range of sqrt(trace), if update is
969  // much smaller (e.g. only 10%) declare convergence
970  double newthresh = 0.03*sqrt(trace);
971  if (newthresh<desiredAccuracy) newthresh = desiredAccuracy;
972  SetUpdateThreshold(newthresh);
973  Vector<double> lastP = params;
974  lastCov = Cov;
975 
976 
977 
978 
979 
980  // now call align with new resolution
981  returnvalue = Align(I1, I2, params, Cov, -1);
982 
983 
984  //cout<<"One alignment iteration finshed... press enter!"<<endl;
985  //getchar();
986 
987 
988 
989  if (returnvalue<0) {
990  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Align returned "<<returnvalue
991  <<". Stopping iteration.");
992  throw int(1);
993  }
994  if (iterationDebug_++ > MaxIterations_) {
995  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Too many iterations: "
996  <<iterationDebug_);
997  returnvalue = 1;
998  throw int(2);
999  }
1000  trace = Cov.Trace();
1001 
1002 
1003  // finish not before best layer is used for each pixel !
1004  // use "control pixels"
1005 
1006  /*
1007  lastP -= params;
1008  if (lastP.NormL2()<desiredAccuracy) {
1009  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Covariance reached desired accuracy");
1010  break;
1011  }
1012  */
1013  if (CheckConvergence_(params, Cov, 0.1)==0) {
1014  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
1015  "Covariance reached desired accuracy: "<<Cov);
1016  break;
1017  }
1018  }
1019  }
1020  catch (...) {
1021  SetUpdateThreshold(desiredAccuracy);
1022  iterationDebug_ = 0;
1023  LineSearch_ = oldLineSearch;
1024  Cov = initialCov;
1025  return returnvalue;
1026  }
1027  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Covariance trace="<<trace
1028  <<" while last time it was "<<lasttrace
1029  <<", now using full resolution");
1030 
1031  SetUpdateThreshold(desiredAccuracy);
1032  LineSearch_ = oldLineSearch;
1033  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Aligning with full resolution.");
1034  returnvalue = Align(I1, I2, params, Cov, 0);
1035  iterationDebug_ = 0;
1036  return returnvalue;
1037 }
1038 
1039 
1040 
1041 
1042 
1044  const PyramidImage<float>& I2,
1045  Vector<double>& params,
1046  Matrix<double>& Cov) {
1047  BIASASSERT(I1.size()>0);
1048  BIASASSERT(I1.size()==I2.size());
1049 
1050  int returnvalue = 0;
1051  double oldLineSearch = LineSearch_;
1052 #ifdef BIAS_DEBUG
1053  double lasttrace = 1e200;
1054  double trace = 1e100;
1055 #endif
1056 
1057 
1058  double desiredAccuracy = GetMaxUpdate();
1059 #ifdef BIAS_DEBUG
1060  const unsigned int numparams = params.size();
1061 #endif
1062  BIASASSERT(Cov.num_rows()==int(numparams));
1063  BIASASSERT(Cov.num_cols()==int(numparams));
1064  iterationDebug_ = 0;
1065  Matrix<double> lastCov(Cov), initialCov(Cov);
1066  try {
1067  for (int pyrLevel = int(I1.size()-1); pyrLevel>0; pyrLevel--) {
1068 
1069  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Aligning on level "<<pyrLevel);
1070 #ifdef BIAS_DEBUG
1071  for (unsigned int i=0; i<numparams; i++) {
1072  INFNANCHECK(params[i]);
1073  }
1074 #endif
1075  // now call align with new resolution
1076  Vector<double> oldparams(params);
1077  Cov = initialCov;// reset prior
1078  returnvalue = Align(I1, I2, params, Cov, pyrLevel);
1079  if (returnvalue<0) {
1080  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Align returned "<<returnvalue
1081  <<". Trying smaller pyramid level.");
1082  continue;
1083  } else {
1084  //cout<<pyrLevel<<": params optimized from "<<oldparams<<" to "<<params<<endl;
1085  }
1086  if (iterationDebug_++ > MaxIterations_) {
1087  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Too many iterations: "
1088  <<iterationDebug_);
1089  returnvalue = 1;
1090  throw int(2);
1091  }
1092 #ifdef BIAS_DEBUG
1093  trace = Cov.Trace();
1094 #endif
1095 
1096  if (CheckConvergence_(params, Cov, 0.1)==0) {
1097  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
1098  "Covariance reached desired accuracy");
1099  break;
1100  }
1101  }
1102  }
1103  catch (...) {
1104  SetUpdateThreshold(desiredAccuracy);
1105  iterationDebug_ = 0;
1106  LineSearch_ = oldLineSearch;
1107  return returnvalue;
1108  }
1109  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Covariance did not improve, trace="<<trace
1110  <<" while last time it was "<<lasttrace);
1111 
1112  SetUpdateThreshold(desiredAccuracy);
1113  LineSearch_ = oldLineSearch;
1114  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,"Aligning with full resolution.");
1115  returnvalue = Align(I1, I2, params, Cov, 0);
1116  iterationDebug_ = 0;
1117  return returnvalue;
1118 }
1119 
1120 
1121 
1124  const double& pixelAccuracy)
1125 {
1126  // make sure the user has set a warp model
1127  BIASASSERT(T_!=NULL);
1128 
1129  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
1130  "compute template and gradients from warped I1 with prediction "
1131  <<params);
1132  T_->SetParameters(params);
1133 
1134  // check whether we have to re-evaluate for each pixel:
1135  const bool ParameterJacobianIsConstant = T_->ParameterJacobianIsConstant();
1136 
1137  BIASDOUT(D_IMAGEALIGNMENT_PROGRESS,
1138  "auto pyramid, determine required scale for each pixel");
1139  Matrix<double> JacParams;
1140  for (unsigned int p=0; p<ControlPositions_.size(); p++) {
1141  // which pixel in the source image ?
1142  const HomgPoint2D& x(ControlPositions_[p]);
1143  // need new jacobian ?
1144  if (p==0 || !ParameterJacobianIsConstant)
1145  T_->ParameterJacobianForward(JacParams, x);
1146 
1147  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL,
1148  "propagate parameter uncertainty "<<Cov);
1149  // approximate isotropic 2d uncertainty of this pixel's position
1150  double scale =
1151  sqrt(BIAS::Matrix<double>(JacParams*Cov*JacParams.Transpose()).Trace());
1152  BIASDOUT(D_IMAGEALIGNMENT_PERPIXEL, " to position uncertainty "<<scale);
1153 
1154  if (scale>pixelAccuracy) return 1;
1155  }
1156  return 0;
1157 }
virtual Vector< double > GetInverseParameters() const
returns parameter vector which undoes the current warp
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
T det() const
calculate the determinante
Definition: Matrix2x2.hh:266
computes and holds the singular value decomposition of a rectangular (not necessarily quadratic) Matr...
Definition: SVD.hh:92
Subscript num_cols() const
Definition: cmat.h:320
unsigned size() const
deprecated interface
double GetImageValue(const double &x, const double &y, unsigned int scale, int channel=0) const
bilinear value from scale space: (x,y) is position in pyramid[0], 0&lt;=scale&lt;=size()-1 is pyramid level...
Matrix< T > Transpose() const
transpose function, storing data destination matrix
Definition: Matrix.hh:823
double NormL2() const
Return the L2 norm: sqrt(a^1 + a^2 + ...)
Definition: Vector.hh:416
affine transformation of 2D image plane which relates image coordinate system and local affine featur...
int GetTrilinearImageValue(const double &x, const double &y, const double &scale, double &T, int channel=0) const
trilinear value from scale space: (x,y) is position in pyramid[0], 0&lt;=scale&lt;=size()-1 is pyramid leve...
virtual int ParameterJacobianBackward(Matrix< double > &Jac, const HomgPoint2D &sink)
transformed position change when parameters change
int StrictPyramidAlign(const PyramidImage< float > &I1, const PyramidImage< float > &I2, Vector< double > &params, Matrix< double > &Cov)
aligns image1 with image2 with respect to parameters params, traditional pyramid based alignment is u...
virtual int MapBackward(const HomgPoint2D &sink, HomgPoint2D &source) const =0
map a point in image &quot;source&quot; to a point in image &quot;sink&quot;
int AutoAlign(const PyramidImage< float > &I1, const PyramidImage< float > &I2, Vector< double > &params, Matrix< double > &Cov)
aligns image1 with image2 with respect to parameters params, scale space tracking is used recursively...
class for representing parameterized image warps, such as homography, displacement, ...
unsigned int Size() const
length of the vector
Definition: Vector.hh:143
int Compute(const Matrix< double > &M, double ZeroThreshold=DEFAULT_DOUBLE_ZERO_THRESHOLD)
set a new matrix and compute its decomposition.
Definition: SVD.cpp:102
int CheckConvergence_(const Vector< double > &params, const Matrix< double > &Cov, const double &pixelAccuracy=0.3)
check for given cov and params, whether control points move more than pixelAccuracy ...
double NormL2() const
Return the L2 norm: a^2 + b^2 + c^2 + ...
Definition: Matrix.hh:878
BIAS::HomgPoint2D LocalToGlobal(const BIAS::HomgPoint2D &x) const
tranform a point in the feature coordinate system into the iamge coordinate system ...
void SetAlignmentPixelsRectangular(const LocalAffineFrame &LAF, int numberOfPixels=-1)
choose any rectangular tracking region and resolution
int Align(const PyramidImage< float > &I1, const PyramidImage< float > &I2, Vector< double > &params, Matrix< double > &Cov, int PyramidLevel=-1)
aligns image1 with image2 with respect to parameters params
const BIAS::Matrix2x2< double > & GetA() const
return affine matrix
virtual int ParameterInversionJacobian(Matrix< double > &Jac) const
compute parameters for inverse operation and obtain the jacobian of the inverse parameters with respe...
void GetParameters(Vector< double > &p) const
get the current parameter vector
virtual TextureTransform * Clone() const =0
virtual covariant copy constructor, caller must eventually destroy the created object ...
const Vector< double > & GetS() const
return S which is a vector of the singular values of A in descending order.
Definition: SVD.hh:167
static int Save(const std::string &filename, const ImageBase &img, const enum TFileFormat FileFormat=FF_auto, const bool sync=BIAS_DEFAULT_SYNC, const int c_jpeg_quality=BIAS_DEFAULT_IMAGE_QUALITY, const bool forceNewID=BIAS_DEFAULT_FORCENEWID, const bool &writeMetaData=true)
Export image as file using extrnal libs.
Definition: ImageIO.cpp:725
void SetIdentity()
Converts matrix to identity matrix.
Definition: Matrix.cpp:383
static std::string toString(const T thenumber, int numzeros=DEFAULT_LEADING_ZEROS)
Converts number to string with leading zeros.
Definition: FileHandling.hh:98
const StorageType * GetImageData() const
overloaded GetImageData() from ImageBase
Definition: Image.hh:137
bool IsInROI(double x, double y, int layer) const
int GetAnisotropicImageValue(const double &xsource, const double &ysource, const Matrix2x2< double > &Cov, double &T, unsigned int channel=0) const
computes (Gaussian) expectation value across a region, used e.g.
Matrix< double > Invert()
returns pseudoinverse of A = U * S * V^T A^+ = V * S^+ * U^T
Definition: SVD.cpp:214
void MultLeft(const Matrix< T > &arg)
in Place matrix multiplication this is equal to M = arg*M, but faster
Definition: Matrix.hh:947
virtual void SetParameters(const Vector< double > &p)=0
override the current state with the new parameters, the meaning of the parameters is defined in the d...
T Trace() const
Definition: Matrix.hh:903
Subscript num_rows() const
Definition: cmat.h:319
virtual void ComposeWithInverseDeltaP(const Vector< double > &deltaP)=0
concatenate *this and an inverse transform with param deltaP and save new parameter vector to *this...
void SetZero()
zeroes the image
Definition: ImageBase.hh:83
Subscript size() const
Definition: vec.h:262
class BIASGeometryBase_EXPORT HomgPoint2D