Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
AffineMapping.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 #include <Base/Common/BIASpragma.hh>
26 #if defined(WIN32) && defined(_MSC_VER)
27 # pragma warning (disable: 4661) /* no suitable def. for explicit instantiation */
28 #endif
29 
30 #include "AffineMapping.hh"
31 #include <Base/Geometry/HomgPoint2D.hh>
32 #include <Geometry/HMatrix.hh> // for comfortable 3x3 mults
33 #include <Base/Image/ImageIO.hh>
34 using namespace std;
35 using namespace BIAS;
36 
37 // (1.0/log(2.0));
38 #define ONE_OVER_LOG2 1.442695
39 
40 template <class InputStorageType, class OutputStorageType>
43 {}
44 
45 
46 template <class InputStorageType, class OutputStorageType>
49 { SetAffineTransformation(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); }
50 
51 
52 template <class InputStorageType, class OutputStorageType>
55  HomgPoint2D& source) const {
56  BIASASSERT(sink[2]==1.0);
57  // compute coordinates in source image with backward tranformation
58  source[0] = sink[0] * a11_ + sink[1] * a12_ + dx_;
59  source[1] = sink[0] * a21_ + sink[1] * a22_ + dy_;
60  return 0;
61 }
62 
63 template <class InputStorageType, class OutputStorageType>
65 GetJacobian_(const HomgPoint2D& sink, Matrix2x2<double>& Jacobian) const {
66  // analytic jacobian of affine transformation IS matrix A
67 
68  /*
69  // old, slow version:
70  Jacobian[0][0] = a11_;
71  Jacobian[0][1] = a12_;
72  Jacobian[1][0] = a21_;
73  Jacobian[1][1] = a22_;
74  */
75 
76  register double* pJacobian = Jacobian.GetData();
77  *pJacobian++ = a11_;
78  *pJacobian++ = a12_;
79  *pJacobian++ = a21_;
80  *pJacobian++ = a22_;
81 
82  return 0;
83 }
84 
85 template <class InputStorageType, class OutputStorageType>
87 GetBoundingBox(unsigned int sourcewidth, unsigned int sourceheight,
88  unsigned int sinkwidth, unsigned int sinkheight,
89  int &tlx, int &tly,
90  int &brx, int &bry) {
91 
92  // do a forward mapping of the corners of the source image and compute
93  // convex hull rectangle
94 
95  HomgPoint2D tl(0,0,1), br(sourcewidth, sourceheight,1), bbTL, bbBR;
96  HomgPoint2D bl(0,sourceheight,1), tr(sourcewidth,0,1), bbBL, bbTR;
97 
99  A[0][0] = a11_;
100  A[0][1] = a12_;
101  A[1][0] = a21_;
102  A[1][1] = a22_;
103  A[0][2] = dx_;
104  A[1][2] = dy_;
105 
106  HMatrix HI;
107 #ifdef BIAS_DEBUG
108  BIASASSERT(A.GetInverse(HI)==0);
109 #else
110  A.GetInverse(HI);
111 #endif
112  //HMatrix HI= (Matrix3x3<double>)A.Invert();
113  bbTL= HI*tl; bbTL.Homogenize();
114  bbBR= HI*br; bbBR.Homogenize();
115  bbBL= HI*bl; bbBL.Homogenize();
116  bbTR= HI*tr; bbTR.Homogenize();
117  //cout<<"corners of src map forward to sink as ";
118  //cout<<bbTL<<bbBR<< bbBL<< bbTR<<endl;
119  double dtlx,dtly, dbrx,dbry;
120 
121  // min(x) and min(y)
122  dtlx=(bbTL[0]);
123  if (bbBR[0]<dtlx) dtlx=(bbBR[0]);
124  if (bbBL[0]<dtlx) dtlx=(bbBL[0]);
125  if (bbTR[0]<dtlx) dtlx=(bbTR[0]);
126 
127  dtly=(bbTL[1]);
128  if (bbBR[1]<dtly) dtly=(bbBR[1]);
129  if (bbBL[1]<dtly) dtly=(bbBL[1]);
130  if (bbTR[1]<dtly) dtly=(bbTR[1]);
131 
132  // max(x) and max(y)
133  dbrx=(bbTL[0]);
134  if (bbBR[0]>dbrx) dbrx=(bbBR[0]);
135  if (bbBL[0]>dbrx) dbrx=(bbBL[0]);
136  if (bbTR[0]>dbrx) dbrx=(bbTR[0]);
137 
138  dbry=(bbTL[1]);
139  if (bbBR[1]>dbry) dbry=(bbBR[1]);
140  if (bbBL[1]>dbry) dbry=(bbBL[1]);
141  if (bbTR[1]>dbry) dbry=(bbTR[1]);
142 
143  // check upper integer limits
144  if (dtlx >= double(INT_MAX)) dtlx = INT_MAX;
145  if (dtly >= double(INT_MAX)) dtly = INT_MAX;
146  if (dbrx>double(INT_MAX)) dbrx = INT_MAX;
147  if (dbry>double(INT_MAX)) dbry = INT_MAX;
148  // check lower integer limits
149  const double minnegativeint = 3.0-double(INT_MAX);
150  if (dtlx < minnegativeint) dtlx = minnegativeint;
151  if (dtly < minnegativeint) dtly = minnegativeint;
152  if (dbrx < minnegativeint) dbrx = minnegativeint;
153  if (dbry < minnegativeint) dbry = minnegativeint;
154 
155 
156  tlx = ( int)dtlx;
157  tly = ( int)dtly;
158  brx = ( int)ceil(dbrx);
159  bry = ( int)ceil(dbry);
160 
161  return 0;
162 }
163 
164 template <class InputStorageType, class OutputStorageType>
166 ComputeScales(double& large, double& smallN, double& cosphi,
167  double& costheta) const {
168 
170  A[0][0] = a11_;
171  A[0][1] = a12_;
172  A[1][0] = a21_;
173  A[1][1] = a22_;
174 
175 #ifdef OLD_VERSION
176  Vector2<double> vector1(0.0,0.0), vector2(0.0,0.0);
178  A.GetSystemMatrix(T);
179  int numev = T.EigenvalueDecomposition(ev1, vector1, ev2, vector2);
180  double ev1 = sqrt(ev1);
181  double ev2 = sqrt(ev2);
182 
183  if (numev!=2) {
184  BIASERR("A="<<A<<" has only rank "<<numev<<". Cannot map.");
185  return -1;
186  }
187  cosphi = (fabs(ev1)>fabs(ev2))? vector1[0] : vector2[0];
188  // upper bound
189  costheta = sqrt(0.5);
190 #else
191  Matrix2x2<double> phi;
192  Matrix2x2<double> theta;
193 double ev1,ev2;
194  HMatrix::FactorizeAffineMatrixRLeft(A, phi, theta, ev1, ev2);
195  cosphi = 0.5*(phi[0][0]+phi[1][1]);
196  costheta = 0.5*(theta[0][0]+theta[1][1]);
197 #endif
198  //cout<<"eigenvalues of A are "<<ev1<<" and "<<ev2<<endl;
199  smallN = fabs((fabs(ev1)>fabs(ev2))?ev2:ev1);
200  if (smallN<1.0) smallN = 1.0;
201 
202  large = fabs((fabs(ev1)<fabs(ev2))?ev2:ev1);
203  if (large<1.0) {
204  large = 1.0;
205  }
206 
207  return 0;
208 }
209 
210 
211 
212 
213 template <class InputStorageType, class OutputStorageType>
216  Image<OutputStorageType>& sink, double scale) {
217  //cout<<"TRILINEAR GREY"<<endl;
218  if (scale<=0.0) {
219  double smallN, cosphi, costheta;
220  ComputeScales(scale, smallN, cosphi, costheta);
221  }
222  if (this->autoPyramidSize_)
223  this->UpdatePyramidSize(*sink.GetROI(), src.GetWidth(), src.GetHeight());
224  this->BuildPyramid_(src, true);
225  return TrilinearGreyAgain(sink, scale);
226 }
227 
228 
229 template <class InputStorageType, class OutputStorageType>
232  BIASASSERT(!this->pyramid_.empty());
233  int tlx=0, tly=0, brx=0, bry=0;
234  GetBoundingBox(this->pyramid_[0]->GetWidth(),
235  this->pyramid_[0]->GetHeight(), sink.GetWidth(),
236  sink.GetHeight(), tlx, tly, brx, bry);
237  this->ClipBoundingBoxToROICorners_(sink, tlx, tly, brx, bry);
238 
239  double logsmallestscale = log(scale)*ONE_OVER_LOG2;
240 
241  int scale1 = int(logsmallestscale);
242  double scale_error = logsmallestscale - scale1;
243 
244  if (scale1>(int)this->pyramid_.size()-2) {
245  // we might get aliasing, pyramid is too small !!!
246  scale1 = this->pyramid_.size()-2;
247  scale_error = 1.0;
248  this->aliasing_ = true;
249  } else if (scale1<0) {
250  // use max resolution image, dont need to upsample in pyramid
251  scale1 = 0;
252  scale_error = 0.0;
253  }
254  const int scale2 = scale1 + 1;
255  const Image<InputStorageType> &p1 = *this->pyramid_[scale1],
256  &p2=*this->pyramid_[scale2];
257 
258  const double weight1 = 1.0 - scale_error, weight2 = scale_error;
259 
260  const double offset =this->pyramid_.GetPositionOffset();
261  double scaleposition = 1.0, offsetposition = 0.0;
262 
263  for (int s=0; s<scale1; s++) {
264  // -0.5 necessary because of offset in 2^n downsampling
265  scaleposition *= 0.5;
266  offsetposition += scaleposition * offset;
267  }
268 
269  const double a11 = scaleposition * a11_;
270  const double a12 = scaleposition * a12_;
271  const double a21 = scaleposition * a21_;
272  const double a22 = scaleposition * a22_;
273  const double dx = scaleposition * dx_ - offsetposition;
274  const double dy = scaleposition * dy_ - offsetposition;
275  OutputStorageType* pO = sink.GetImageData()+tly*sink.GetWidth();
276  for (unsigned int y=tly; y<sink.GetHeight(); y++) {
277  for (unsigned int x=0; x<sink.GetWidth(); x++) {
278  // compute coordinates in smaller image
279  const double x1 = a11*x + a12*y + dx;
280  const double y1 = a21*x + a22*y + dy;
281  if (p1.IsPositionInImage(int(x1-2.001), int(y1-2.001)) &&
282  p1.IsPositionInImage(int(x1+2.501), int(y1+2.501))) {
283 
284  // better use output storagetype with clipvalue_ here (slower)
285  *pO++ =
286  OutputStorageType(weight1 * p1.FastBilinearInterpolationGrey(x1,y1) +
287  weight2 *
288  p2.FastBilinearInterpolationGrey((x1-offset)*0.5,
289  (y1-offset)*0.5));
290  } else *pO++ = 0;
291  }
292  }
293 
294  return 0;
295 }
296 
297 
298 template <class InputStorageType, class OutputStorageType>
301  Image<OutputStorageType>& sink) {
302  //cout<<"BILINEAR GREY"<<endl;
303  int tlx=0, tly=0, brx=0, bry=0;
304  GetBoundingBox(src.GetWidth(), src.GetHeight(), sink.GetWidth(),
305  sink.GetHeight(), tlx, tly, brx, bry);
306  this->ClipBoundingBoxToROICorners_(sink, tlx, tly, brx, bry);
307 
308  double destx = 0, desty = 0;
309  OutputStorageType *pSink=sink.GetImageData()+tly*sink.GetWidth();
310  // run over sink and bilinear interpolate from temp using combined transf.
311 
312 
313 
314  // we might update this to use only ROI in sink. However, then simple pointer
315  // arithmetics cannot be used. I guess this pays only if a third of
316  // sink stays empty
317 
318 
319 
320  for (unsigned int y=tly; y<sink.GetHeight(); y++) {
321  for (unsigned int x=0; x<sink.GetWidth(); x++) {
322  destx = a11_ * x + a12_ * y + dx_;
323  desty = a21_ * x + a22_ * y + dy_;
324  // use imagedatapointer here
325  if (src.IsPositionInImage(int(destx-1.1), int(desty-1.1)) &&
326  src.IsPositionInImage(int(destx+1.1), int(desty+1.1)))
327  *pSink++ =
328  OutputStorageType(src.FastBilinearInterpolationGrey(destx,
329  desty));
330  else *pSink++ = 0;
331  }
332  }
333 
334  return 0;
335 }
336 
337 
338 template <class InputStorageType, class OutputStorageType>
341  Image<OutputStorageType>& sink) {
342 
343  if (this->autoPyramidSize_)
344  this->UpdatePyramidSize(*sink.GetROI(),
345  (int)src.GetWidth(), (int)src.GetHeight());
346  this->BuildPyramid_(src);
347  return MapDirectAgain(sink);
348 }
349 
350 template <class InputStorageType, class OutputStorageType>
353  BIASASSERT(!this->pyramid_.empty());
354 #ifdef BIAS_DEBUG
355  //this->pyramid_.WriteImages("pyr");
356 #endif
357  BIASASSERT(sink.GetChannelCount()==1); // only grey for speed
358 
360  A3[0][0] = a11_;
361  A3[0][1] = a12_;
362  A3[1][0] = a21_;
363  A3[1][1] = a22_;
364  A3[0][2] = dx_;
365  A3[1][2] = dy_;
366 
367  double smallestscale = 0, largescale=0, cosphi = 0, costheta = 0;
368  if (ComputeScales(largescale, smallestscale, cosphi, costheta)!=0)
369  return -1;
370 
371  if (largescale<1.1) {
372  // bilinear is sufficient
373  return BilinearGrey(*this->pyramid_[0], sink);
374  }
375 
376  const double smoothsigma =
377  //sqrt(largescale*largescale-smallestscale*smallestscale);
378  sqrt(0.25*(largescale/smallestscale*largescale/smallestscale)-0.25);
379  //cout<<"smoothsigma would be "<<smoothsigma<<endl;
380  if (smoothsigma<0.3) {
381  // trilinear is sufficient
382  return TrilinearGreyAgain(sink, largescale);
383  } else if (smoothsigma>7.5) {
384  // we use a hws=5 kernel, the cut-off filter coefficient 6 has more than
385  // 10% weight, leaving it away (as we do) is no good idea.
386  this->aliasing_ =true;
387  BIASWARN("You may have (directional) aliasing in your image. sigma="
388  <<smoothsigma);
389  // you might try trilinear, this avoids aliasing but oversmoothes image
390  // content. Or bigger kernel (manual+slow)...
391  }
392  //cout<<"ANISOTROPIC GREY"<<endl;
393  //cout<<"smallestscale is "<<smallestscale<<" smoothsigma is "<<smoothsigma
394  // <<endl;
395  // --------------------------------------------------------------------
396  // ----------------- find roi in src image ----------------------------
397  // --------------------------------------------------------------------
398  const double cosinephi = cosphi;
399  const double sinephi = sqrt(1.0-cosinephi*cosinephi);
400  const double cosinetheta = costheta;
401  const double sinetheta = sqrt(1.0-cosinetheta*cosinetheta);
402  //cout<<"cosinephi is "<<cosinephi<<", sinephi is "<<sinephi<<endl;
403  int maxx = -1000000, maxy=-1000000, minx=1000000, miny=1000000;
404 
405  int tlx=0, tly=0, brx=0, bry=0;
406  GetBoundingBox(this->pyramid_[0]->GetWidth(),
407  this->pyramid_[0]->GetHeight(),
408  sink.GetWidth(),
409  sink.GetHeight(), tlx, tly, brx, bry);
410  this->ClipBoundingBoxToROICorners_(sink, tlx, tly, brx, bry);
411 
412  HomgPoint2D result(0,0,1), sinkpoint(tlx,tly,1);
413  HomgPoint2D CoG(0,0,0);
414  for (unsigned int corner=0; corner<4; corner++) {
415  switch (corner) {
416  case 0: sinkpoint[0] = tlx; sinkpoint[1] = tly; break;
417  case 1: sinkpoint[0] = brx; sinkpoint[1] = tly; break;
418  case 2: sinkpoint[0] = brx; sinkpoint[1] = bry; break;
419  case 3: sinkpoint[0] = tlx; sinkpoint[1] = bry; break;
420  }
421  if (GetSourceCoordinates_(sinkpoint, result)!=0) continue;
422  //cout<<"mapped "<<sinkpoint<<" to "<<result<<endl;
423 
424  if (int(rint(result[0]))>maxx) maxx = int(rint(result[0]));
425  if (int(rint(result[0]))<minx) minx = int(rint(result[0]));
426  if (int(rint(result[1]))>maxy) maxy = int(rint(result[1]));
427  if (int(rint(result[1]))<miny) miny = int(rint(result[1]));
428 
429  }
430 
431  // if (maxx>=(int)this->pyramid_[0]->GetWidth())
432  // maxx = this->pyramid_[0]->GetWidth()-1;
433  //if (maxy>=(int)this->pyramid_[0]->GetHeight())
434  // maxy = this->pyramid_[0]->GetHeight()-1;
435  //if (minx<0) minx = 0;
436  //if (miny<0) miny = 0;
437  // center of gravity for rotation
438  double rotx = 0.5*(minx + maxx);
439  double roty = 0.5*(miny + maxy);
440  //cout<<"rot center is "<<rotx<<";"<<roty<<endl;
441  // --------------------------------------------------------------------
442  // ----------------- compute dimensions of tmp ------------------------
443  // --------------------------------------------------------------------
444  maxx = -1000000, maxy=-1000000, minx=1000000, miny=1000000;
445 
446  for (unsigned int corner=0; corner<4; corner++) {
447  switch (corner) {
448  case 0: sinkpoint[0] = tlx; sinkpoint[1] = tly; break;
449  case 1: sinkpoint[0] = brx; sinkpoint[1] = tly; break;
450  case 2: sinkpoint[0] = brx; sinkpoint[1] = bry; break;
451  case 3: sinkpoint[0] = tlx; sinkpoint[1] = bry; break;
452  }
453  if (GetSourceCoordinates_(sinkpoint, result)!=0) continue;
454  sinkpoint = result;
455  result[0] = cosinetheta * (sinkpoint[0]-rotx) +
456  sinetheta * (sinkpoint[1]-roty)+rotx;
457  result[1] = -sinetheta * (sinkpoint[0]-rotx) +
458  cosinetheta *(sinkpoint[1]-roty)+roty;
459  if (int(rint(result[0]))>maxx) maxx = int(rint(result[0]));
460  if (int(rint(result[0]))<minx) minx = int(rint(result[0]));
461  if (int(rint(result[1]))>maxy) maxy = int(rint(result[1]));
462  if (int(rint(result[1]))<miny) miny = int(rint(result[1]));
463 
464  }
465  for (unsigned int corner=0; corner<4; corner++) {
466  switch (corner) {
467  case 0: sinkpoint[0] = tlx; sinkpoint[1] = tly; break;
468  case 1: sinkpoint[0] = brx; sinkpoint[1] = tly; break;
469  case 2: sinkpoint[0] = brx; sinkpoint[1] = bry; break;
470  case 3: sinkpoint[0] = tlx; sinkpoint[1] = bry; break;
471  }
472  if (GetSourceCoordinates_(sinkpoint, result)!=0) continue;
473  sinkpoint = result;
474  result[0] = cosinephi * (sinkpoint[0]-rotx) +
475  sinephi * (sinkpoint[1]-roty)+rotx;
476  result[1] = -sinephi * (sinkpoint[0]-rotx) +
477  cosinephi *(sinkpoint[1]-roty)+roty;
478  if (int(rint(result[0]))>maxx) maxx = int(rint(result[0]));
479  if (int(rint(result[0]))<minx) minx = int(rint(result[0]));
480  if (int(rint(result[1]))>maxy) maxy = int(rint(result[1]));
481  if (int(rint(result[1]))<miny) miny = int(rint(result[1]));
482 
483  }
484 
485  // if ellipse is rotated by 45 degrees, bounding box increases
486  //double enlargefactor = fabs(costheta)+sqrt(1-(costheta*costheta));
487 
488  Image<InputStorageType> axisaligned(int((maxx-minx+1)/smallestscale+1),
489  int((maxy-miny+1)/smallestscale+1), 1),
490  filtered(int((maxx-minx+1)/smallestscale+1),
491  int((maxy-miny+1)/smallestscale+1), 1);
492 
493  // --------------------------------------------------------------------
494  // ---------------- fill smoothing-aligned tmp ------------------------
495  // --------------------------------------------------------------------
496 
497  Intermediate[0][0] = cosinephi;
498  Intermediate[0][1] = -sinephi;
499  Intermediate[1][0] = sinephi;
500  Intermediate[1][1] = cosinephi;
501  Intermediate[0][2] = cosinephi*(axisaligned.GetWidth()-1.0)/-2.0 +
502  sinephi*(axisaligned.GetHeight()-1.0)/2.0;
503  Intermediate[1][2] = sinephi*(axisaligned.GetWidth()-1.0)/-2.0 +
504  cosinephi * (axisaligned.GetHeight()-1.0)/-2.0;
505  Intermediate *= smallestscale;
506  Intermediate[2][2] = 1;
507  Intermediate[0][2]+=rotx;
508  Intermediate[1][2]+=roty;
509 
510 
511 
512  //cout<<"intermediate is "<<Intermediate<<endl;
513  double logsmallestscale = log(smallestscale)*ONE_OVER_LOG2;
514 
515  int scale1 = int(logsmallestscale);
516  double scale_error = logsmallestscale - scale1;
517 
518  if (scale1>(int)this->pyramid_.size()-2) {
519  // we might get aliasing, pyramid is too small !!!
520  scale1 = this->pyramid_.size()-2;
521  scale_error = 1.0;
522  this->aliasing_ = true;
523  } else if (scale1<0) {
524  // use max resolution image, dont need to upsample in pyramid
525  scale1 = 0;
526  scale_error = 0.0;
527  }
528  const int scale2 = scale1 + 1;
529  const Image<InputStorageType> &p1 = *this->pyramid_[scale1],
530  &p2=*this->pyramid_[scale2];
531 
532  const double weight1 = 1.0 - scale_error, weight2 = scale_error;
533 
534  const double offset =this->pyramid_.GetPositionOffset();
535  double scaleposition = 1.0, offsetposition = 0.0;
536  for (int s=0; s<scale1; s++) {
537  // -0.5 necessary because of offset in 2^n downsampling
538  offsetposition += scaleposition * offset;
539  scaleposition *= 0.5;
540  }
541 
542  const double i00 = Intermediate[0][0], i01=Intermediate[0][1],
543  i02=Intermediate[0][2];
544  const double i10 = Intermediate[1][0], i11=Intermediate[1][1],
545  i12=Intermediate[1][2];
546 
547  InputStorageType* pI = axisaligned.GetImageData();
548  for (unsigned int y=0; y<axisaligned.GetHeight(); y++) {
549  for (unsigned int x=0; x<axisaligned.GetWidth(); x++) {
550  // compute coordinates in smaller image
551  const double x1 = scaleposition * (i00*x + i01*y + i02) + offsetposition;
552  const double y1 = scaleposition * (i10*x + i11*y + i12) + offsetposition;
553 
554  if (p1.IsPositionInImage(int(x1-2.1), int(y1-2.1)) &&
555  p1.IsPositionInImage(int(x1+2.6), int(y1+2.6))) {
556 
557  // better use output storagetype with clipvalue_ here
558  *pI++ =
559  InputStorageType(weight1 * p1.FastBilinearInterpolationGrey(x1,y1) +
560  weight2 *
561  p2.FastBilinearInterpolationGrey((x1-offset)*0.5,
562  (y1-offset)*0.5));
563  } else *pI++ = 0;
564  }
565  }
566 #ifdef BIAS_DEBUG
567  //ImageIO::Save("B.mip", axisaligned);
568 #endif
569  // --------------------------------------------------------------------
570  // -------------------- filter horizontal -----------------------------
571  // --------------------------------------------------------------------
572 
573  // create horizontal gauss mask with smooth sigma and run over temp
574  const double fac = -1.0/(2.0*smoothsigma);
575 
576  double h0 = 1.0;
577  double h1 = expf(fac);
578  double h2 = expf(4.0*fac);
579  double h3 = expf(9.0*fac);
580  double h4 = expf(16.0*fac);
581  double h5 = expf(25.0*fac);
582  double sum = 1.0 /(h0 + 2.0*(h1+h2+h3+h4+h5));
583  h0 *= sum; h1 *= sum; h2 *= sum; h3 *= sum; h4 *= sum; h5 *= sum;
584 
585 
586  // horizontal convolution ignoring wrap-arounds
587  InputStorageType *pS = axisaligned.GetImageData(),
588  *pD = filtered.GetImageData();
589 
590  // copy first five bytes
591  *pD++ = *pS++;
592  *pD++ = *pS++;
593  *pD++ = *pS++;
594  *pD++ = *pS++;
595  *pD++ = *pS;
596 
597  const int width = axisaligned.GetWidth(), height = axisaligned.GetHeight();
598 
599  const InputStorageType* pEndH = axisaligned.GetImageData() + width*height-5;
600  while (++pS<pEndH) {
601  *pD++ = InputStorageType(h0*pS[0]+h1*(pS[-1]+pS[1])+h2*(pS[-2]+pS[2])
602  +h3*(pS[-3]+pS[3])+h4*(pS[-4]+pS[4])+h5*(pS[-5]+pS[5]));
603  }
604  // copy last five bytes
605  *pD++ = *pS++;
606  *pD++ = *pS++;
607  *pD++ = *pS++;
608  *pD++ = *pS++;
609  *pD = *pS;
610 
611 #ifdef BIAS_DEBUG
612  //ImageIO::Save("Bfiltered.mip", filtered);
613 #endif
614 
615  // link A and scale and rotation (eliminate offset ?)
616  // Matrix3x3<double> direct(Intermediate.GetInvert() * A3);
617  Matrix3x3<double> direct;
618 #ifdef BIAS_DEBUG
619  BIASASSERT(Intermediate.GetInverse(direct) == 0);
620 #else
621  Intermediate.GetInverse(direct);
622 #endif
623  direct *= A3;
624 
625  direct *= 1.0 / direct[2][2]; // homogenize
626  const double d00=direct[0][0], d01=direct[0][1], d02=direct[0][2];
627  const double d10=direct[1][0], d11=direct[1][1], d12=direct[1][2];
628 
629  double destx = 0, desty = 0;
630  OutputStorageType *pSink=sink.GetImageData()+tly*sink.GetWidth();
631  // run over sink and bilinear interpolate from temp using combined transf.
632 
633  // compute all sink pixels here or use roi (cannot use pointer psink++ then)?
634 
635  for (unsigned int y=tly; y<sink.GetHeight(); y++) {
636  for (unsigned int x=0; x<sink.GetWidth(); x++) {
637  destx = d00 * x + d01 * y + d02;
638  desty = d10 * x + d11 * y + d12;
639  // use imagedatapointer here
640  if (filtered.IsPositionInImage(int(destx-1.1), int(desty-1.1)) &&
641  filtered.IsPositionInImage(int(destx+1.1), int(desty+1.1)))
642  *pSink++ =
643  OutputStorageType(filtered.FastBilinearInterpolationGrey(destx,
644  desty));
645  else *pSink++ = 0;
646  }
647  }
648 
649  return 0;
650 }
651 
652 
653 
654 
655 
656 
657 
658 namespace BIAS{
660 template class AffineMapping<float, float>;
663 
664 #if defined(BUILD_IMAGE_CHAR)
666 template class AffineMapping<char, char>;
667 #endif
668 
669 #if defined(BUILD_IMAGE_USHORT)
671 #endif
672 
673 #if defined(BUILD_IMAGE_SHORT)
674 template class AffineMapping<short, short>;
675 #endif
676 
677 #if defined(BUILD_IMAGE_SHORT)&&defined(BUILD_IMAGE_USHORT)
679 #endif
680 
681 #if defined(BUILD_IMAGE_INT)
682 template class AffineMapping<int,int>;
683 #endif
684 
685 #if defined(BUILD_IMAGE_USHORT)
687 #endif
688 
689 #if defined(BUILD_IMAGE_USHORT) && defined(BUILD_IMAGE_INT)
690 template class AffineMapping<unsigned short,int>;
691 #endif
692 
693 #if defined(BUILD_IMAGE_DOUBLE)
694 template class AffineMapping<double,double>;
695 #endif
696 }
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
a 3x3 Matrix describing projective transformations between planes
Definition: HMatrix.hh:39
bool IsPositionInImage(const int &x, const int &y) const
check if image contains that pixel position
Definition: ImageBase.hh:110
HomgPoint2D & Homogenize()
homogenize class data member elements to W==1 by divison by W
Definition: HomgPoint2D.hh:215
StorageType FastBilinearInterpolationGrey(const double x, const double y) const
Fast bilinear interpolation for grey-value images.
Definition: Image.hh:402
unsigned int GetWidth() const
Definition: ImageBase.hh:312
ROI * GetROI()
Returns a pointer to the roi object.
Definition: ImageBase.hh:615
int GetInverse(Matrix3x3< T > &inv) const
Matrix inversion: inverts this and stores resulty in argument inv.
Definition: Matrix3x3.cpp:373
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
unsigned int GetHeight() const
Definition: ImageBase.hh:319
Maps image src to image sink with affine transformation.
T * GetData()
get the pointer to the data array of the matrix (for faster direct memeory access) ...
Definition: Matrix.hh:185
const StorageType * GetImageData() const
overloaded GetImageData() from ImageBase
Definition: Image.hh:137
int EigenvalueDecomposition(T &value1, Vector2< T > &vector1, T &value2, Vector2< T > &vector2) const
Eigenvalue decomposition.
Definition: Matrix2x2.cpp:131
void GetSystemMatrix(Matrix< T > &dest) const
compute square system matrix dest = A^T * A
Definition: Matrix.hh:1075