Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
BackwardMapping.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 "BackwardMapping.hh"
26 #include <Geometry/RMatrix.hh>
27 #include <Base/Image/ImageConvert.hh>
28 #include <Base/Geometry/HomgPoint2D.hh>
29 #include <Base/Common/BIASpragma.hh>
30 #include <Image/AffineMapping.hh>
31 #include <Base/Math/Random.hh>
32 #include <Base/Image/ImageIO.hh>
33 #include <Base/Common/FileHandling.hh>
34 
35 
36 
37 // backward compatibility to gcc < 4.1, all other must have omp.h !
38  #ifdef BIAS_HAVE_OPENMP
39  # if !defined(_OPENMP) && defined(WIN32)
40  # error Please check your OpenMP flags and defines - seem inconsistent.
41  # endif
42  # include <omp.h>
43  #endif
44 
45 #define LOG2 0.30103
46 
47 using namespace std;
48 
49 namespace BIAS {
50 
51 #define MIN_SIGMA_ANISOTROPIC 0.3
52 #define STEP_THRESH (1.0-MIN_SIGMA_ANISOTROPIC)
53 
54 template <class InputStorageType, class OutputStorageType>
55 int BackwardMapping<InputStorageType, OutputStorageType>::
56 GetSourceCoordinates_(const HomgPoint2D& sink,
57  HomgPoint2D& source) const
58 {
59  BIASERR("you have to implement derived class function !");
60  // for instance, if you simply want to magnify your source image by 2 do a:
61  source[0] = sink[0] * 0.5;
62  source[1] = sink[1] * 0.5;
63  BIASABORT;
64  return -1;
65 }
66 
67 template <class InputStorageType, class OutputStorageType>
69 GetJacobian_(const HomgPoint2D& sink, Matrix2x2<double>& Jacobian) const
70 {
71  // numerical approximatioin of jacobian
72  // compute mapped points for 4-neighborhood
73  // this is actually a very good way for the jacobian approximation
74  // since we use is exactly for the step size which we assumed for
75  // computation
76 
77  HomgPoint2D source_left, source_right, source_up, source_down;
78  HomgPoint2D locsink(sink);
79  locsink[0] -= 0.5;
80  if (GetSourceCoordinates(locsink, source_left)<0) return -1;
81  locsink[0] += 1.0;
82  if (GetSourceCoordinates(locsink, source_right)<0) return -1;
83  locsink[0] -= 0.5;
84  locsink[1] -= 0.5;
85  if (GetSourceCoordinates(locsink, source_up)<0) return -1;
86  locsink[1] += 1.0;
87  if (GetSourceCoordinates(locsink, source_down)<0) return -1;
88 
89  // difference vectors are rows of jacobian
90  Jacobian[0][0] = source_right[0] - source_left[0];
91  Jacobian[1][0] = source_right[1] - source_left[1];
92  Jacobian[0][1] = source_down[0] - source_up[0];
93  Jacobian[1][1] = source_down[1] - source_up[1];
94  return 0;
95 }
96 
97 template <class InputStorageType, class OutputStorageType>
99 UpdatePyramidSize(const ROI& ROIsink, int srcwidth, int srcheight) {
100 
101  int ulx,uly,lrx,lry;
102  ROIsink.GetCorners(ulx,uly,lrx,lry);
103  lrx -= 1;
104  lry -= 1;
105  int midx = (ulx+lrx)/2;
106  int midy = (ulx+lrx)/2;
107  double width = lrx-ulx;
108  double height = lry-uly;
109 
110  // compute maximum size of pyramid
111  HomgPoint2D testpoint,tmp;
112  double maxscale = 0.0, scale = 0.0;
113 
114  // map some points and find maximum local scaling
115  // ATTENTION: all points that are mapped to (x,y,0) in the source image
116  // cause a warping of infinity. It might be incorrect to assume that only
117  // the corners define the scale range !
118  // Therefore 20 points in the image are mapped and tested
119  Random R;
120  for (unsigned int c=0; c<20; c++) {
121  switch (c) {
122  // image corners
123  case 0:testpoint = HomgPoint2D(ulx, uly); break;
124  case 1:testpoint = HomgPoint2D(ulx, lry); break;
125  case 2:testpoint = HomgPoint2D(lrx, lry); break;
126  case 3:testpoint = HomgPoint2D(lrx, uly); break;
127  // half edges
128  case 4:testpoint = HomgPoint2D(midx, uly); break;
129  case 5:testpoint = HomgPoint2D(midx, lry); break;
130  case 6:testpoint = HomgPoint2D(ulx, midy); break;
131  case 7:testpoint = HomgPoint2D(lrx, midy); break;
132  // 4 at negative diagonal
133  case 8:
134  case 9:
135  case 10:
136  case 11: {
137  double fraction(double(c-7)/6.0);
138  testpoint = HomgPoint2D(ulx + fraction * width,
139  uly + fraction * height);
140  break;
141  }
142  // 4 at positive diagonal
143  case 12:
144  case 13:
145  case 14:
146  case 15: {
147  double fraction(double(c-11)/6.0);
148  testpoint = HomgPoint2D(ulx + fraction * width,
149  uly + (1.0-fraction) * height);
150  break;
151  }
152  default:testpoint =
154  R.GetUniformDistributed(uly, lry));
155 
156  }
157  if (GetSourceCoordinates(testpoint, tmp)!=0) continue;
158  // compute local sampling frequency for antialiasing
159  Matrix2x2<double> Jacobian;
160  GetJacobian(testpoint, Jacobian);
161 
162  // this is actually dx = NormL2( Jac * (1;0) )
163  // and dy = NormL2( Jac * (0;1) )
164  // It tells how far we move in src if we move by one in sink.
165  // This is important for complying with the sampling theorem.
166  double
167  dx = sqrt(Jacobian[0][0]*Jacobian[0][0]+
168  Jacobian[1][0]*Jacobian[1][0]),
169  dy = sqrt(Jacobian[0][1]*Jacobian[0][1]+
170  Jacobian[1][1]*Jacobian[1][1]);
171  if (dx<JACOBI_THRESH) dx = JACOBI_THRESH;
172  if (dy<JACOBI_THRESH) dy = JACOBI_THRESH;
173 
174  // use larger step to select pyramid level
175  scale = (dx > dy)? log(dx)*ONE_OVER_LOG2 : log(dy)*ONE_OVER_LOG2;
176 
177  if (scale>maxscale) maxscale = scale;
178  }
179 
180  // obtain maximal pyramid size
181  double maxpossiblepyrsize = log(min(srcwidth, srcheight)/16.0) / LOG2;
182  if (maxpossiblepyrsize<1.0) maxpossiblepyrsize = 1.0;
183 
184  // + 0.5 (round) + 1.0 (offset of original resolution) + 0.5 layer safety
185  SetPyramidSize(min(int(maxscale+2.0), int(maxpossiblepyrsize)));
186 
187  // restore automatic mode when this function is invoked
188  autoPyramidSize_ = true;
189 
190 }
191 
192 template <class InputStorageType, class OutputStorageType>
194 SetPyramidSize(const int newsize)
195 {
196  int oldsize = pyramid_.size();
197  if (oldsize>0 && oldsize<newsize) {
198  // too small, make extra layers
199  pyramid_.CreateAdditionalLayer(newsize-oldsize, 4);
200  } else {
201  // not initialized yet
202  if (oldsize==0) pyramid_.Init(newsize);
203  else {
204  Image<InputStorageType> backupIm(*pyramid_[0]);
205  pyramid_.Init(backupIm, newsize);
206  // why do we waste time in making a smaller pyramid ????
207  BIASWARN("Potential performance problem, pyramid re-initialized."
208  "old size="<<oldsize<<" newsize="<<newsize);
209  }
210  }
211  // user is responsible now
212  pyramidSize_ = newsize;
213  autoPyramidSize_ = false;
214 }
215 
216 
217 template <class InputStorageType, class OutputStorageType>
219 BuildPyramid_(const Image<InputStorageType>& source, bool forceInit,
220  int numlayers)
221 {
222  // double check that pyramidSize_ is not too big for the given image
223  int thesize = int(log(min(source.GetWidth(), source.GetHeight())/4.0)/LOG2);
224  if (thesize<1) thesize = 1;
225  if ((int)pyramidSize_>thesize) {
226  BIASWARNONCE("Backwardmapping: required/specified pyramid size is "
227  <<pyramidSize_<<", but image size "
228  <<min(source.GetWidth(), source.GetHeight())
229  <<" allows only pyramid size of " <<thesize<<". ");
230 
231  pyramidSize_ = thesize;
232  }
233 
234 
235  // if forceinit we use source to build the pyramid, otherwise
236  // we check if there is already a pyramid
237  if (forceInit) {
238  if (!pyramid_.IsEmpty()) pyramid_.Clear();
239  }
240 
241  if (pyramid_.IsEmpty()) {
242  if (numlayers>0) // this is for init with one layer first...
243  pyramid_.InitFromImageBase(source, numlayers);
244  else
245  pyramid_.InitFromImageBase(source, pyramidSize_);
246  }
247 
248 #ifdef BIAS_DEBUG
249  //static int mycounter = 0;
250  //pyramid_.WriteImages("BWMpyramid"+toString(mycounter++));
251 #endif
252 }
253 
254 // ##################### dispatcher functions #########################
255 template <class InputStorageType, class OutputStorageType>
259  InterpolationMethod interpolationQuality, bool newSink,
260  double SuperSampling)
261 {
262 #ifdef BIAS_DEBUG
263  // sanity checks
264  if (!newSink){
265  if (sink.IsEmpty()){
266  BIASERR("sink is empty !");
267  BIASABORT;
268  }
269  if (sink.GetChannelCount()!=source.GetChannelCount() ||
270  sink.GetColorModel()!=source.GetColorModel()) {
271  BIASERR("different image types !");
272  BIASABORT;
273  }
274  if (sink.IsPlanar() && sink.GetChannelCount()>1) {
275  BIASERR("not implemented for planar images !");
276  BIASABORT;
277  }
278  }
279  if (source.IsPlanar() && source.GetChannelCount()>1){
280  BIASERR("not implemented for planar images !");
281  BIASABORT;
282  }
283 #endif
284  width_ = source.GetWidth();
285  height_ = source.GetHeight();
286  if (newSink) {
287  CalcCoordOffset_(sink, source);
288  ChangeImgSize_(sink, source);
289  } else {
290  offsetX_=0; offsetY_=0;
291  }
292 
293  // record interpolation mode since the simple map interface doesnt
294  interpolationType_ = interpolationQuality;
295 
296 #ifdef BIAS_DEBUG
297  switch (interpolationQuality) {
298  case MapNearestNeighbor:
299  case MapBilinear:
300  case MapBicubic:
301  case MapTrilinear:
302  case MapAnisotropic:break;
303  default: BIASERR("unknown interpolation method."); BIASABORT;
304  }
305 #endif
306 
307  Image<OutputStorageType>* pSink = &sink;
308  // if supersampling is active generate temporary large image
309  if (SuperSampling>1.0) {
310  int newwidth = int(rint(double(sink.GetWidth()+2)*SuperSampling)),
311  newheight = int(rint(double(sink.GetHeight()+2)*SuperSampling));
312  pSink = new Image<OutputStorageType>(newwidth, newheight,
313  sink.GetChannelCount());
314  pSink->SetZero();
315  superSampling_ = SuperSampling;
316  }
317  incomplete_ = false;
318  int result = 0;
319  if (interpolationQuality>=MapTrilinear) {
320  // here we work with a pyramid
321  // to avoid computational overhead, we save the pyramid and thus the
322  // source image. We dont need to calculate it every time
323 
324  // check if pyramid is large enough for current mapper
325  // if (autoPyramidSize_) UpdatePyramidSize(*pSink);
326  // init pyramid of size 1 (for auto mode), later, after ROI is determined, actually
327  // required pyramid size is determined
328  if (autoPyramidSize_) BuildPyramid_(source, true, 1);
329  else BuildPyramid_(source, true);
330 
331  ROI_ = *source.GetROI();
332  result = MapTri_(*pSink);
333  } else {
334  ROI_ = *source.GetROI();
335  result = MapBi_(source, *pSink, interpolationQuality);
336  }
337 
338  // now map from large image to small image
339  if (SuperSampling>1.0) {
340  //cout<<"writing supersampled image to ./supersampled.mip ..."<<endl;
341  //ImageIO::Save("supersampled.mip", *pSink);
343  Resizer.SetAffineTransformation(SuperSampling, 0, 0, SuperSampling,
344  0.5*SuperSampling, 0.5*SuperSampling);
345  int res2 = Resizer.Map(*pSink, sink, MapTrilinear, false, 1.0);
346  if (result==0) result = res2;
347  delete pSink;
348  superSampling_ = 1.0;
349  }
350 
351  // check if alle pixels could be filled
352  if (incomplete_) {
353  incomplete_ = false;
354  if (result==0) result = 1;
355  }
356  return result;
357 }
358 
359 
360 template <class InputStorageType, class OutputStorageType>
363  if (interpolationType_ < MapTrilinear) {
364  BIASERR("Call complete map function, no image caching necessary for"
365  " simple mapper, dont have your input image any more !");
366  BIASABORT;
367 
368  }
369  incomplete_ = false;
370  int result = MapTri_(sink);
371  if (incomplete_) {
372  incomplete_ = false;
373  if (result==0) result = 1;
374  }
375  return result;
376 }
377 
378 // #################### bi-Interpolation ################################
379 
380 template <class InputStorageType, class OutputStorageType>
384  InterpolationMethod interpolationQuality)
385 {
386  HomgPoint2D p_sink(0,0), p_source(0,0);
387  p_sink[2] = 1;
388 
389  OutputStorageType** Data = sink.GetImageDataArray();
390  double tmp = 0;
391  const unsigned int ccs = sink.GetChannelCount();
392  // calculate interesting region in sink
393  int tlx,tly, brx1,bry1;
394  GetBoundingBox(source.GetWidth(), source.GetHeight(),
395  sink.GetWidth(), sink.GetHeight(),
396  tlx, tly, brx1, bry1);
397  tlx-=offsetX_; brx1-=offsetX_; tly-=offsetY_; bry1-=offsetY_;
398  // clip interesting region with sink roi
399  ClipBoundingBoxToROICorners_(sink, tlx, tly, brx1, bry1);
400 
401  const int brx = brx1, bry = bry1;
402 
403  if (alphaImg_.GetSizeByte()>0) {
404  if ( (bry-tly)>(int)alphaImg_.GetHeight() ||
405  (brx-tlx)>(int)alphaImg_.GetWidth() )
406  BIASERR("alphaImg too small:"<<alphaImg_.GetWidth()<<"x"<<
407  alphaImg_.GetHeight()<<" minimum size:"<<
408  (brx-tlx)<<"x"<<(bry-tly));
409  }
410  const bool alphaMap = (alphaImg_.GetSizeByte()>0);
411  //cout<<"offsetX_:"<<offsetX_<<" offsetY_:"<<offsetY_<<endl;
412  //cout<<"tl:"<<tlx<<","<<tly<<" br:"<<brx<<","<<bry<<endl;
413  OutputStorageType value;
414  // iterate over sink and calculate the corresponding pix in source
415  // check interpolation modes in speed order
416  if (interpolationQuality==MapNearestNeighbor) { // nearest neighbor
417  const InputStorageType** ppImageData
418  = (const InputStorageType**)source.GetImageDataArray();
419  for (int y=tly; y<bry; y++) {
420  for (int x=tlx; x<brx; x++) {
421  // image point in homogeneous coordinates
422  p_sink[0] = x + offsetX_;
423  p_sink[1] = y + offsetY_;
424  if (GetSourceCoordinates(p_sink, p_source)!=0) {
425  incomplete_ = true;
426  continue;
427  }
428  // skip if pixel is not in ROI
429  if (!ROI_.IsInROI(int(rint(p_source[0])),
430  int(rint(p_source[1])))) {
431  incomplete_ = true;
432  continue;
433  }
434  // run over color channels
435  for (unsigned int j=0; j<ccs; j++) {
436  //value= Data[y][x*ccs+j];
437  ClipValue_(ppImageData[(int)rint(p_source[1])]
438  [(int)rint(p_source[0])*ccs+j], value);
439  if (alphaMap) {
440  Data[y][x*ccs+j]=
441  OutputStorageType(alphaImg_.GetImageDataArray()[y-tly][x-tlx] *
442  float(Data[y][x*ccs+j]) +
443  (1.0-alphaImg_.GetImageDataArray()[y-tly][x-tlx]) *
444  float(value)
445  );
446  } else
447  Data[y][x*ccs+j]=value;
448  }
449  }
450  }
451  } else if (interpolationQuality==MapBilinear) { // bilinear
452  for (int y=tly; y<bry; y++) {
453  for (int x=tlx; x<brx; x++) {
454  // image point in homogeneous coordinates
455  p_sink[0] = x + offsetX_;
456  p_sink[1] = y + offsetY_;
457  if (GetSourceCoordinates(p_sink, p_source)!=0) {
458  incomplete_ = true;
459  continue;
460  }
461  // skip if pixel and 2x2 neighbors are not in ROI
462  //if (!ROI_.IsInROI(p_source[0], p_source[1])) {
463  if (!ROI_.CheckBilinearInterpolation(p_source[0], p_source[1])) {
464  incomplete_ = true;
465  continue;
466  }
467  for (unsigned int j=0; j<ccs; j++) {
468  // value= Data[y][x*ccs+j];
469  ClipValue_(source.BilinearInterpolationRGBInterleaved(p_source[0],
470  p_source[1],j),
471  value);
472  if (alphaMap) {
473  Data[y][x*ccs+j]=
474  OutputStorageType(alphaImg_.GetImageDataArray()[y-tly][x-tlx] *
475  float(Data[y][x*ccs+j]) +
476  (1.0-alphaImg_.GetImageDataArray()[y-tly][x-tlx]) *
477  float(value)
478  );
479  } else
480  Data[y][x*ccs+j]=value;
481  }
482  }
483  }
484  } else if (interpolationQuality==MapBicubic) { // bicubic
485  for (int y=tly; y<bry; y++) {
486  for (int x=tlx; x<brx; x++) {
487  // image point in homogeneous coordinates
488  p_sink[0] = x + offsetX_;
489  p_sink[1] = y + offsetY_;;
490  if (GetSourceCoordinates(p_sink, p_source)!=0) {
491  incomplete_ = true;
492  continue;
493  }
494  // skip if pixel and 3x3 neighbors are not in ROI
495  //if (!ROI_.IsInROI(p_source[0], p_source[1])) {
496  if (!ROI_.CheckBicubicInterpolation(p_source[0], p_source[1])) {
497  incomplete_ = true;
498  continue;
499  }
500  // run over color channels
501  for (unsigned int j=0; j<ccs; j++) {
502  if (GetImageValue_(p_source[0], p_source[1], source, j, tmp)==0) {
503  // value= Data[y][x*ccs+j];
504  ClipValue_(tmp, value);
505  if (alphaMap) {
506  Data[y][x*ccs+j]=
507  OutputStorageType(alphaImg_.GetImageDataArray()[y-tly][x-tlx] *
508  float(Data[y][x*ccs+j]) +
509  (1.0-alphaImg_.GetImageDataArray()[y-tly][x-tlx]) *
510  float(value)
511  );
512  } else
513  Data[y][x*ccs+j]=value;
514  }
515  }
516  }
517  }
518  }
519 
520  return 0;
521 }
522 
523 // ########################## tri-Interpolatioon #########################
524 
525 template <class InputStorageType, class OutputStorageType>
528 {
529  // sanity check (who knows ;-) )
530  if (pyramid_.IsEmpty()) {
531  BIASERR("Called function on uninitialized image !");
532  sink.FillImageWithConstValue( (OutputStorageType)(255) );
533  return -1;
534  }
535 
536  // check if we can use fast path or must go the hard way:
537  if (alphaImg_.GetSizeByte()==0 && offsetX_==0 && offsetY_==0 &&
538  sink.GetChannelCount()==1 && pConcatenation_==NULL &&
539  interpolationType_==MapTrilinear && superSampling_==1.0) {
540  // fast path / default case: grey without offset and special effects
541  return MapTrilinearGreySimple_(sink);
542  }
543 
544  HomgPoint2D p_sink(0,0), p_source(0,0);
545 
546  OutputStorageType** Data = sink.GetImageDataArray();
547  double tmp = 0, localscale = 0;
548  const unsigned int ccs = sink.GetChannelCount();
549 
550  // calculate interesting region in sink by forward mapping of corners
551  int tlx,tly, brx1, bry1;
552  GetBoundingBox(pyramid_[0]->GetWidth(), pyramid_[0]->GetHeight(),
553  sink.GetWidth(), sink.GetHeight(),
554  tlx,tly,brx1,bry1);
555  tlx-=offsetX_; brx1-=offsetX_; tly-=offsetY_; bry1-=offsetY_;
556  // clip interesint region with sink roi
557  ClipBoundingBoxToROICorners_(sink, tlx, tly, brx1, bry1);
558  const int brx = brx1, bry = bry1;
559 
560  // check if pyramid is still large enough, mapping might have changed,
561  // e.g. other homography on same image !
562  if (autoPyramidSize_) {
563  ROI roisink;
564  roisink.Resize(sink.GetWidth(), sink.GetHeight());
565  roisink.SetCorners(tlx,tly,brx,bry);
566  UpdatePyramidSize(roisink,
567  pyramid_[0]->GetWidth(), pyramid_[0]->GetHeight());
568  }
569 
570  if (alphaImg_.GetSizeByte()>0) {
571  if ( (bry-tly)>(int)alphaImg_.GetHeight() ||
572  (brx-tlx)>(int)alphaImg_.GetWidth() )
573  BIASERR("alphaImg too small:"<<alphaImg_.GetWidth()<<"x"<<
574  alphaImg_.GetHeight()<<" minimum size:"<<
575  (brx-tlx)<<"x"<<(bry-tly));
576  }
577  const bool alphaMap = (alphaImg_.GetSizeByte()>0);
578 
579  Matrix2x2<double> Jacobian;
580  OutputStorageType value;
581 
582  //cout<<"backwardmapping is running from "<<tlx<<";"<<tly<<" to "<<brx<<";"<<bry<<endl;
583 
584 # ifdef BIAS_HAVE_OPENMP
585  /*
586  OpenMP parallelization:
587 
588  - All variables which are first written, the read in each loop
589  iteration should be declared private.
590  - Also all loop indices must be declared private except the index of
591  the parallelized loop (in our case the outermost), which is private by
592  default.
593  - All variables declared in the parallel region are private by default.
594  - p_sink and p_source are declared firstprivate, so that their
595  initialized values are visible in the parallel region (tmp and value
596  are overwritten before read).
597  - tlx and tly are only read and therefore safe to share.
598  - Data is only addressed through the (private) loop indices and is safe
599  to share.
600  - default(none) disables the default scoping behavior, so that each
601  variable must be scoped manually.
602 
603  It is important not to use static variables in the parallel region,
604  they are not thread-safe and cause race conditions.
605 
606  For an OpenMP tutorial visit:
607  https://computing.llnl.gov/tutorials/openMP/
608 
609  */
610 #pragma omp parallel for if(omp_get_num_procs() > 1) \
611  shared(tlx,tly,Data) \
612  private(tmp,localscale,Jacobian,value) \
613  firstprivate(p_sink,p_source)
614 # endif
615 
616  // iterate over sink and calculate the corresponding pix in source
617  for ( int y=tly; y<bry; y++) {
618  for ( int x=tlx; x<brx; x++) {
619  // image point in homogenous coords
620  p_sink[0] = x + offsetX_;
621  p_sink[1] = y + offsetY_;
622  // this is so slow we can make the decision in the innerst loop:
623  if (interpolationType_ == MapTrilinear) {
624  if (GetSourceCoordinates(p_sink, p_source)!=0) {
625  incomplete_ = true;
626  continue;
627  }
628  // skip if pixel not in roi
629  if (!ROI_.IsInROI(p_source[0], p_source[1])) {
630  incomplete_ = true;
631  continue;
632  }
633  // compute layer for trilinear filtering, 0 means best resolution
634  ComputeLocalPyramidLayer_(p_sink, localscale);
635 
636  // compute color channels
637  // this can be speeded up, all the weights are the same for all
638  // channels, dont recompute them every time !
639  // However for now this code does it ..
640  for (unsigned int j=0; j<sink.GetChannelCount(); j++) {
641  if (GetTrilinearImageValue_(p_source[0], p_source[1],
642  localscale, j, tmp) == 0) {
643  // value= Data[y][x*ccs+j];
644  ClipValue_(tmp, value);
645  if (alphaMap) {
646  Data[y][x*ccs+j]=
647  OutputStorageType(alphaImg_.GetImageDataArray()[y-tly][x-tlx] *
648  float(Data[y][x*ccs+j]) +
649  (1.0-alphaImg_.GetImageDataArray()[y-tly][x-tlx]) *
650  float(value)
651  );
652  } else {
653  Data[y][x*ccs+j]=value;
654  }
655  } else {
656  incomplete_ = true;
657  }
658  }
659  } else {
660  // anisotropic filtering !
661  if (GetSourceCoordinates(p_sink, p_source)!=0) {
662  incomplete_ = true;
663  continue;
664  }
665 
666  // skip if pixel not in roi
667  if (!ROI_.IsInROI(p_source[0], p_source[1])) {
668  incomplete_ = true;
669  continue;
670  }
671 
672  // compute local sampling frequency matrix
673  GetJacobian(p_sink, Jacobian);
674 
675  // compute color channels
676  // this can be speeded up, all the weights are the same for all
677  // channels, dont recompute them every time !
678  // However for now this code does it ..
679  for (unsigned int j=0; j<sink.GetChannelCount(); j++) {
680  if (GetAnisotropicImageValue_(p_source[0], p_source[1],
681  Jacobian, j, tmp) == 0) {
682  //value= Data[y][x*ccs+j];
683  ClipValue_(tmp, value);
684  if (alphaMap) {
685 
686  // should use clipvalue here
687 
688  Data[y][x*ccs+j]=
689  OutputStorageType(alphaImg_.GetImageDataArray()[y-tly][x-tlx]*
690  float(Data[y][x*ccs+j]) +
691  (1.0 -
692  alphaImg_.GetImageDataArray()[y-tly][x-tlx])
693  * float(value)
694  );
695  } else
696  Data[y][x*ccs+j]=value;
697  } else {
698  incomplete_ = true;
699  }
700  }
701  }
702  }
703  }
704 #ifdef BIAS_DEBUG
705  if (!rangeChecked_) {
706  rangeChecked_ = true;
707  BIASWARN("You may have got artefacts in your image because overflow "
708  <<"checking of your data types combination "
709  <<typeid(InputStorageType).name()<<" -> "
710  <<typeid(InputStorageType).name()<<" is not yet implemented."
711  <<" Please do it in CheckValueRange_()."<<endl
712  <<" Example: If your input image is type int and your output "
713  <<" image is type uchar, unpredictable things happen when your"
714  <<" input image contains/creates numbers>255 or numbers<0. You"
715  <<" may want to round or clip, which is done only heuristically"
716  <<" at the moment for your data type combination.");
717  }
718 #endif
719  if (aliasing_) {
720  aliasing_ = false;
721  BIASWARN("You probably got aliasing, because your pyramid was too small."
722  "If you did not set the pyramid size manually, the specialized"
723  " implementation of this->UpdatePyramidSize() is wrong and you"
724  " have such a large local minification that you need a"
725  " smaller pyramid top than currently automatically generated "
726  " (or the whole image is mapped to only a few pixels)!"
727  " Workaround for too little pyramids: You could"
728  " try somthing like manual this->SetPyramidSize(log2(imagesize))"
729  <<endl<<" Most likely however, your transformation"
730  " (this->GetSourceCoordinates, e.g. homography)"
731  " is NOT CONTINUOUS (e.g. wrap-around for homography)"
732  ", which is a crucial assumption in antialiased BackwardMapping");
733  }
734 
735  return 0;
736 }
737 
738 
739 template <class InputStorageType, class OutputStorageType>
742  // sanity check (who knows ;-) )
743  if (pyramid_.IsEmpty()) {
744  BIASERR("Called function on uninitialized image !");
745  sink.FillImageWithConstValue( (OutputStorageType)(255) );
746  return -1;
747  }
748  // check if pyramid is still large enough, mapping might have changed,
749  // e.g. other homography on same image !
750  if (autoPyramidSize_)
751  UpdatePyramidSize(*sink.GetROI(),
752  pyramid_[0]->GetWidth(), pyramid_[0]->GetHeight());
753  HomgPoint2D p_sink(0,0), p_source(0,0);
754 
755  OutputStorageType** Data = sink.GetImageDataArray();
756  double localscale = 0;
757 
758  // calculate interesting region in sink by forward mapping of corners
759  int tlx,tly, brx1, bry1;
760  GetBoundingBox(pyramid_[0]->GetWidth(), pyramid_[0]->GetHeight(),
761  sink.GetWidth(), sink.GetHeight(),
762  tlx,tly,brx1,bry1);
763 
764  // clip interesint region with sink roi
765  ClipBoundingBoxToROICorners_(sink, tlx, tly, brx1, bry1);
766  const int brx = brx1, bry = bry1;
767 
768  BIASASSERT(alphaImg_.GetSizeByte()==0);
769  BIASASSERT(offsetX_==0);
770  BIASASSERT(offsetY_==0);
771  BIASASSERT(sink.GetChannelCount()==1);
772  BIASASSERT(pConcatenation_==NULL);
773  BIASASSERT(superSampling_==1.0);
774  Matrix2x2<double> Jacobian;
775 
776  // iterate over sink and calculate the corresponding pix in source
777  for ( int y=tly; y<bry; y++) {
778  OutputStorageType *pLine = Data[y];
779  for ( int x=tlx; x<brx; x++) {
780  // image point in homogenous coords
781  p_sink[0] = x;
782  p_sink[1] = y;
783 
784  if (GetSourceCoordinates_(p_sink, p_source)!=0) {
785  incomplete_ = true;
786  continue;
787  }
788  // skip if pixel not in roi
789  if (!ROI_.IsInROI(p_source[0], p_source[1])) {
790  incomplete_ = true;
791  continue;
792  }
793  // compute layer for trilinear filtering, 0 means best resolution
794 
795  GetJacobian_(p_sink, Jacobian);
796  double
797  dx = sqrt(Jacobian[0][0]*Jacobian[0][0]+
798  Jacobian[1][0]*Jacobian[1][0]),
799  dy = sqrt(Jacobian[0][1]*Jacobian[0][1]+
800  Jacobian[1][1]*Jacobian[1][1]);
801  if (dx<JACOBI_THRESH) dx = JACOBI_THRESH;
802  if (dy<JACOBI_THRESH) dy = JACOBI_THRESH;
803  // use larger step to select pyramid level
804  localscale = (dx > dy)? log(dx)*ONE_OVER_LOG2 : log(dy)*ONE_OVER_LOG2;
805  // clip scale to zero since we have no better resolution image than 0
806  if (localscale<0.0) localscale = 0.0;
807 
808  // compute a weight for the smaller and one for the larger pyramid level
809  // then compute the colors at these levels and average them according
810  // to their weights
811 
812  double value1 = 0, value2 = 0;
813  double scale_error = localscale - int(localscale);
814  int scale1 = int(localscale);
815  if (scale1>=(int)pyramid_.size()-1) {
816  // pyramid is too small, create new levels on the fly
817  if (pyramid_.CreateAdditionalLayer(scale1 - pyramid_.size() + 2)!=0) {
818  // we might get aliasing, pyramid is too small !!!
819  scale1 = pyramid_.size()-1;
820  scale_error = 0.0;
821  aliasing_ = true;
822  }
823  } else if (scale1<0) {
824  // use max resolution image, dont need to upsample in pyramid
825  scale1 = 0;
826  scale_error = 0.0;
827  }
828 
829  // compute coordinates in smaller image
830  double x1 = p_source[0], y1 = p_source[1];
831  const double offset = pyramid_.GetPositionOffset();
832  for (int s=0; s<scale1; s++) {
833  // offset necessary because of offset in 2^n downsampling
834  x1 = (x1 - offset) / pyramid_.GetRescaleFactor();
835  y1 = (y1 - offset) / pyramid_.GetRescaleFactor();
836  }
837  // if scale 1 fits perfectly (at lowest highest pyramid layer)
838  // we dont need scale2, set it to same value to avoid access violations
839  const int scale2 = (scale_error==0.0)? scale1 : scale1 + 1;
840  const double x2 = (scale_error==0.0)? x1 : (x1 - offset) / pyramid_.GetRescaleFactor();
841  const double y2 = (scale_error==0.0)? y1 : (y1 - offset) / pyramid_.GetRescaleFactor();
842 
843  double weight1 = 1.0 - scale_error, weight2 = scale_error;
844  BIAS::Image<InputStorageType> &im = *pyramid_[scale1];
845 
846 
847  // compute value only in image (no pyramid), first try bicubic
848  if ((scale1==0) &&
849  (x1 < (double)im.GetROI()->GetCornerLowerRightX() - 2.0) &&
850  (y1 < (double)im.GetROI()->GetCornerLowerRightY() - 2.0) &&
851  (x1 >= (double)im.GetROI()->GetCornerUpperLeftX() + 1.0) &&
852  (y1 >= (double)im.GetROI()->GetCornerUpperLeftY() + 1.0) ) {
853  value1 = im.BicubicInterpolation(x1, y1, 0);
854  } else if ((x1 < (double)im.GetROI()->GetCornerLowerRightX()-1.0) &&
855  (y1 < (double)im.GetROI()->GetCornerLowerRightY()-1.0) &&
856  (x1 > (double)im.GetROI()->GetCornerUpperLeftX()) &&
857  (y1 > (double)im.GetROI()->GetCornerUpperLeftY()) ) {
858  // we are near the border, bilinear
859  value1 = im.FastBilinearInterpolationGrey(x1, y1);
860  } else {
861  // we are AT the border, we could do nearest neighbor here or
862  // something else, but for now, skip this pixel
863  weight1=0;
864  }
865 
866  // compute second scale only if necessary
867  if (weight2>0.0) {
868  const ROI& theROI = *pyramid_[scale2]->GetROI();
869 
870  if (x2 < (double)theROI.GetCornerLowerRightX()-1.0 &&
871  y2 < (double)theROI.GetCornerLowerRightY()-1.0 &&
872  x2 > (double)theROI.GetCornerUpperLeftX() &&
873  y2 > (double)theROI.GetCornerUpperLeftY()) {
874  // we are near the border, bilinear
875  value2 = pyramid_[scale2]->FastBilinearInterpolationGrey(x2, y2);
876  } else {
877  // we are AT the border, we could do nearest neighbor here or
878  // something else, but for now, skip this pixel
879  weight2=0;
880  }
881  }
882  // check if we computed any value at all
883  const double weightsum = weight1 + weight2;
884  if ((weightsum)>DBL_EPSILON) {
885  ClipValue_((weight1 * value1 + weight2 * value2)/weightsum, pLine[x]);
886  } else {
887  incomplete_ = true;
888  }
889  }
890  }
891 #ifdef BIAS_DEBUG
892  if (!rangeChecked_) {
893  rangeChecked_ = true;
894  BIASERR("You may have got artefacts in your image because overflow "
895  <<"checking of your data types combination "
896  <<typeid(InputStorageType).name()<<" -> "
897  <<typeid(InputStorageType).name()<<" is not yet implemented."
898  <<" Please do it in CheckValueRange_().");
899  }
900 #endif
901 
902  if (aliasing_) {
903  aliasing_ = false;
904  BIASERR("You probably got aliasing, because your pyramid was too small."
905  "If you did not set the pyramid size manually, the specialized "
906  " implementation of this->UpdatePyramidSize() is wrong!");
907  }
908  return 0;
909 }
910 
911 template <class InputStorageType, class OutputStorageType>
913 GetTrilinearImageValue_(const double& xsource,
914  const double& ysource,
915  const double& scale,
916  unsigned int channel,
917  double& thereturnvalue)
918 {
919  // compute a weight for the smaller and a weight for the larger pyramid level
920  // then compute the colors at these levels and average them according
921  // to their weights
922  double value1 = 0, value2 = 0;
923  double scale_error = scale - int(scale);
924  int scale1 = int(scale);
925 
926  if (scale1>=(int)pyramid_.size()-1) {
927  // pyramid is too small, create new levels on the fly
928  if (pyramid_.CreateAdditionalLayer(scale1 - pyramid_.size() + 2)!=0) {
929  // we might get aliasing, pyramid is too small !!!
930  scale1 = pyramid_.size()-1;
931  scale_error = 0.0;
932  aliasing_ = true;
933  }
934  } else if (scale1<0) {
935  // use max resolution image, dont need to upsample in pyramid
936  scale1 = 0;
937  scale_error = 0.0;
938  }
939 
940  // compute coordinates in smaller image
941  double x1 = xsource, y1 = ysource;
942  const double offset = pyramid_.GetPositionOffset();
943  for (int s=0; s<scale1; s++) {
944  // offset necessary because of offset in 2^n downsampling
945  x1 = (x1 - offset) / pyramid_.GetRescaleFactor();
946  y1 = (y1 - offset) / pyramid_.GetRescaleFactor();
947  }
948 
949  // if scale 1 fits perfectly (at lowest highest pyramid layer)
950  // we dont need scale2, set it to the same value to avoid access violations
951  const int scale2 = (scale_error==0.0)? scale1 : scale1 + 1;
952  const double x2 = (scale_error==0.0)? x1 : (x1 - offset) / pyramid_.GetRescaleFactor();
953  const double y2 = (scale_error==0.0)? y1 : (y1 - offset) / pyramid_.GetRescaleFactor();
954 
955  double weight1 = 1.0 - scale_error, weight2 = scale_error;
956 
957  if (GetImageValue_(x1, y1, *pyramid_[scale1], channel, value1)!=0) weight1=0;
958 
959  // compute second scale only if necessary
960  if (weight2>0.0) {
961  if (GetImageValue_(x2, y2, *pyramid_[scale2], channel, value2)!=0) {
962  weight2=0;
963  }
964  }
965  // check if we computed any value at all
966  const double weightsum = weight1 + weight2;
967  if ((weightsum)<=DBL_EPSILON) return -1;
968 
969  // this is the tri in trilinear:
970  thereturnvalue = (weight1 * value1 + weight2 * value2) / weightsum;
971 
972  return 0;
973 }
974 
975 
976 template <class InputStorageType, class OutputStorageType>
978 GetAnisotropicImageValue_(const double& xsource,
979  const double& ysource,
980  Matrix2x2<double>& Jacobian,
981  unsigned int channel,
982  double& thereturnvalue)
983 {
984  // compute eigenvalues of jacobian, which determine the largest step sizes
985  Vector2<double> ev1, ev2;
986 
987  // make postive definite jTj for gauss distribution
988  //Jacobian = Jacobian.Transpose() * Jacobian;
989  Matrix2x2<double> Smoother;
990  Jacobian.GetSystemMatrix(Smoother);
991 
992  // compute eigenvalues = squared step sizes in source
993  double d1 = 0.0, d2 = 0.0;
994  Smoother.EigenvalueDecomposition(d1, ev1, d2, ev2);
995 
996  // remove square, require min eigenvalue 1, because we have no finer image
997  // information
998  // (should rarely happen for meaningful mappings)
999  if (d1<STEP_THRESH) d1 = sqrt(STEP_THRESH); else d1 = sqrt(d1);
1000  if (d2<STEP_THRESH) d2 = sqrt(STEP_THRESH); else d2 = sqrt(d2);
1001 
1002  // make sure d1 is not smaller than d2
1003  if (d1<d2) {
1004  std::swap(d1, d2);
1005  }
1006 
1007  // compute pyramid levels
1008  const double largescale = log(d1)*ONE_OVER_LOG2;
1009 
1010  if (largescale<=0.1) {
1011  // bicubic is sufficient since we are only enlarging
1012  return GetImageValue_(xsource, ysource,
1013  *pyramid_[0], channel, thereturnvalue);
1014  }
1015 
1016 
1017  return pyramid_.GetAnisotropicImageValue(xsource, ysource, Smoother,
1018  thereturnvalue, channel);
1019 }
1020 
1021 
1022 // ################## data type clipping: double -> char, ... ############
1023 
1024 template <class InputStorageType, class OutputStorageType>
1027  int& tlx,
1028  int& tly,
1029  int& brx,
1030  int& bry) const
1031 {
1032  unsigned int minx, miny, maxx, maxy;
1033  sink.GetROICorners(minx, miny, maxx, maxy);
1034  if (tlx<(int)minx) tlx = minx;
1035  if (tly<(int)miny) tly = miny;
1036  if (brx>(int)maxx) brx = maxx;
1037  if (bry>(int)maxy) bry = maxy;
1038 }
1039 
1040 template <class InputStorageType, class OutputStorageType>
1043  const Image<InputStorageType> &source) {
1044  //sink bounding box
1045  int xl=0; int xr=0; int yl=0; int yr=0;
1046  GetBoundingBox(sink.GetWidth(), sink.GetHeight(),
1047  source.GetWidth(), source.GetHeight(), xl, yl, xr, yr);
1048 
1049  // sink corner coordinates
1050  double xls=0; //double xrs=sink.GetWidth();
1051  double yls=0; //double yrs=sink.GetHeight();
1052 
1053 
1054  // result bounding box
1055  double xlr=0; double ylr=0;
1056  //double xrr=0; double yrr=0;
1057  if (xl<xls) xlr=xl; else xlr=xls;
1058  if (yl<yls) ylr=yl; else ylr=yls;
1059  //if (xr>xrs) xrr=xr; else xrr=xrs;
1060  //if (yr>yrs) yrr=yr; else yrr=yrs;
1061 
1062  offsetX_= (int)ceil(xlr);
1063  offsetY_= (int)ceil(ylr);
1064 
1065  return 0;
1066 }
1067 
1068 template <class InputStorageType, class OutputStorageType>
1071  const Image<InputStorageType> &source) {
1073  int xl=0; int xr=0; int yl=0; int yr=0;
1074 
1075  GetBoundingBox(source.GetWidth(), source.GetHeight(),
1076  sink.GetWidth(), sink.GetHeight(), xl, yl, xr, yr);
1077 
1078  // sink corner coordinates
1079  double xls=0; double xrs=sink.GetWidth();
1080  double yls=0; double yrs=sink.GetHeight();
1081 
1082  // result bounding box
1083  double xlr=0; double xrr=0; double ylr=0; double yrr=0;
1084  if (xl<xls) xlr=xl; else xlr=xls;
1085  if (yl<yls) ylr=yl; else ylr=yls;
1086  if (xr>xrs) xrr=xr; else xrr=xrs;
1087  if (yr>yrs) yrr=yr; else yrr=yrs;
1088 
1089  int x_abs = (int)ceil(abs(xlr-xrr));
1090  int y_abs = (int)ceil(abs(ylr-yrr));
1091 
1092 
1093  const int numchannels = sink.GetChannelCount();
1094  temp.Init(x_abs,y_abs,numchannels);
1095  temp.SetZero();
1096 
1097  int w = sink.GetWidth();
1098  int h = sink.GetHeight();
1099 
1100 
1101  OutputStorageType** data = sink.GetImageDataArray();
1102  OutputStorageType** datac = temp.GetImageDataArray();
1103  int xt = (int)abs(offsetX_);
1104  int yt = (int)abs(offsetY_);
1105  cout << "translation at tnc " << xt << " " << yt << "\n";
1106 
1107  for (int hi =0; hi<h; hi++) {
1108  for (int wi=0; wi<w; wi++) {
1109  for (int c=0; c<numchannels; c++) {
1110  datac[hi+yt][(wi+xt)*numchannels+c] = data[hi][wi*numchannels+c];
1111  //cout << hi << " " << wi << "\n";
1112  // cout << hi + 3*c +yt << " " << wi +xt << "\n";
1113  }
1114  }
1115  }
1116  sink=temp;
1117 
1118  return 0;
1119 }
1120 
1121 //Computes Displacement Map, saving the unmapped texture-coordinates of sink
1122 //pixels into an image as described in the header-file
1123 template <class InputStorageType, class OutputStorageType>
1126  int width,
1127  int height) {
1128  Image<InputStorageType> src (width, height);
1129  return GetDisplacementMap(dismap, src);
1130 }
1131 
1132 //Computes Displacement Map, saving the unmapped texture-coordinates of sink
1133 //pixels into an image as described in the header-file
1134 template <class InputStorageType, class OutputStorageType>
1137  Image<InputStorageType> &source) {
1138 
1139  if(dismap.GetChannelCount() != 3){
1140  cout << "Displacement Map must have 3 Channels but has only "
1141  << dismap.GetChannelCount() << endl;
1142  return -1;
1143  }
1144 
1145  if(!dismap.IsInterleaved()){
1146  cout << "Displacement map must be INTERLEAVED" << endl;
1147  return -1;
1148  }
1149 
1150  float* Data = dismap.GetImageData();
1151  //TEXTURE Coordinates of unmapped pixels
1152  double gl_x, gl_y;
1153 
1154  HomgPoint2D sink_p, src_p;
1155  int count = 0;
1156 
1157  sink_p[2] = 1;
1158  for(unsigned int j = 0; j < dismap.GetHeight(); j++){
1159  sink_p[1] = j + offsetY_;
1160 
1161  for(unsigned int i = 0; i < dismap.GetWidth(); i++){
1162  sink_p[0] = i + offsetX_;
1163 
1164  if(GetSourceCoordinates(sink_p, src_p) != -1 &&
1165  src_p[0] < source.GetWidth() &&
1166  src_p[1] < source.GetHeight() &&
1167  src_p[0] >= 0 &&
1168  src_p[1] >= 0){
1169  // src_p.Homogenize();
1170  //converting to TEXTURE-Coordinates
1171  source.BIASToTextureCoordinates(src_p[0], src_p[1],
1172  gl_x, gl_y);
1173 
1174 
1175  Data[count ] = (float)gl_x;
1176  Data[count+1] = (float)gl_y;
1177  Data[count+2] = 1.0;
1178  }
1179  else{
1180  Data[count ] = -1.0;
1181  Data[count+1] = -1.0;
1182  Data[count+2] = -1.0;
1183  }
1184  count+=3;
1185  }
1186  }
1187 
1188  // dismap.Flip();
1189 
1190  return 0;
1191 }
1192 
1193 template <class InputStorageType, class OutputStorageType>
1195 GetLookupTableSize(unsigned int& width, unsigned int& channels,
1196  InterpolationMethod method)
1197 {
1198  switch (method)
1199  {
1200  case MapNearestNeighbor:
1201  BIASERR("not yet implemented!");
1202  return -1;
1203  break;
1204  case MapBilinear:
1205  {
1206  //if lookups were not computed
1207  if(pLUT_Bil_ == NULL){
1208  BIASERR("LOOKUP-TABLE FOR BILINEAR IS NOT READY!"
1209  "Call PrepareLookupTableMapping");
1210  return -1;
1211  }
1212 
1213  //prepare image size
1214  width = pLUT_Bil_size+1;
1215  channels = BWM_LUT_Entry_Bilinear::GetSerializedSize();
1216  }
1217  break;
1218  case MapTrilinear:
1219  {
1220  //if lookups were not computed
1221  if(pLUT_Tri_ == NULL){
1222  BIASERR("LOOKUP-TABLE FOR BILINEAR IS NOT READY!"
1223  "Call PrepareLookupTableMapping");
1224  return -1;
1225  }
1226  width = pLUT_Tri_size+1;
1227  channels = BWM_LUT_Entry_Trilinear::GetSerializedSize();
1228  }
1229  break;
1230  default:
1231  BIASERR("not yet implemented!");
1232  return -1;
1233  break;
1234  }
1235 
1236  return 0;
1237 }
1238 
1239 template <class InputStorageType, class OutputStorageType>
1241 GetLookupTable(const string& filename, InterpolationMethod method)
1242 {
1243  switch (method)
1244  {
1245  case MapNearestNeighbor:
1246  BIASERR("not yet implemented!");
1247  return -1;
1248  break;
1249  case MapBilinear:
1250  {
1251  //if lookups were not computed
1252  if(pLUT_Bil_ == NULL){
1253  BIASERR("LOOKUP-TABLE FOR BILINEAR IS NOT READY!"
1254  "Call PrepareLookupTableMapping");
1255  return -1;
1256  }
1257 
1258  // use a binary stream for lookup table
1259  fstream stream;
1260  stream.open(filename.c_str(), fstream::out |
1261  fstream::binary);
1262 
1263  unsigned int sizeInFloats = pLUT_Bil_size * BWM_LUT_Entry_Bilinear::GetSerializedSize() + 1;
1264 
1265  //allocate buffer for transfer (mem -> file)
1266  float* buffer = new float[sizeInFloats];
1267 
1268  //write LUT to buffer...
1269  if (GetLookupTable(buffer, method) != 0) {
1270  delete[] buffer;
1271  return -1;
1272  }
1273 
1274  //first sizeof(unsigned int) bytes in file for LUT-size in floats
1275  for (unsigned int i=0; i<sizeof(unsigned int); i++)
1276  stream.put( ((char*)(&sizeInFloats))[i] );
1277 
1278  // write to file and close
1279  for (unsigned int i = 0; i < sizeInFloats; i++)
1280  stream.write((const char*)&(buffer[i]), sizeof(float));
1281  stream.close();
1282 
1283  delete[] buffer;
1284  }
1285  break;
1286  case MapTrilinear:
1287  {
1288  //if lookups were not computed
1289  if(pLUT_Tri_ == NULL){
1290  BIASERR("LOOKUP-TABLE FOR TRILINEAR IS NOT READY!"
1291  "Call PrepareLookupTableMapping");
1292  return -1;
1293  }
1294 
1295  // use a binary stream for lookup table
1296  fstream stream;
1297  stream.open(filename.c_str(), fstream::out |
1298  fstream::binary);
1299 
1300  unsigned int sizeInFloats = pLUT_Tri_size * BWM_LUT_Entry_Trilinear::GetSerializedSize() + 1;
1301 
1302  //allocate buffer for transfer (mem -> file)
1303  float* buffer = new float[sizeInFloats];
1304 
1305  //write LUT to buffer...
1306  if (GetLookupTable(buffer, method) != 0) {
1307  delete[] buffer;
1308  return -1;
1309  }
1310 
1311  //first sizeof(unsigned int) bytes in file for LUT-size in floats
1312  for (unsigned int i=0; i<sizeof(unsigned int); i++)
1313  stream.put( ((char*)(&sizeInFloats))[i] );
1314 
1315  //...write LUT to file and close
1316  for (unsigned int i = 0; i < sizeInFloats; i++)
1317  stream.write((const char*)&(buffer[i]), sizeof(float));
1318  stream.close();
1319  delete[] buffer;
1320  }
1321  break;
1322  default:
1323  BIASERR("not yet implemented!");
1324  return -1;
1325  break;
1326  }
1327 
1328  return 0;
1329 }
1330 
1331 
1332 template <class InputStorageType, class OutputStorageType>
1334 GetLookupTable(float* lutImageRow, InterpolationMethod method)
1335 {
1336  float* pixel = lutImageRow;
1337  switch (method)
1338  {
1339  case MapNearestNeighbor:
1340  BIASERR("not yet implemented!");
1341  return -1;
1342  break;
1343  case MapBilinear:
1344  {
1345  //if lookups were not computed
1346  if(pLUT_Bil_ == NULL){
1347  BIASERR("LOOKUP-TABLE FOR BILINEAR IS NOT READY!"
1348  "Call PrepareLookupTableMapping");
1349  return -1;
1350  }
1351 
1352  *pixel = static_cast<float>(method);
1353  pixel++;
1354 
1355  for(int i = 0; i < pLUT_Bil_size; i++){
1356  pixel = pLUT_Bil_[i].SerializedOut(pixel);
1357  }
1358  }
1359  break;
1360  case MapTrilinear:
1361  {
1362  //if lookups were not computed
1363  if(pLUT_Tri_ == NULL){
1364  BIASERR("LOOKUP-TABLE FOR TRILINEAR IS NOT READY!"
1365  "Call PrepareLookupTableMapping");
1366  return -1;
1367  }
1368  *pixel = static_cast<float>(method);
1369  pixel++;
1370 
1371  for(int i = 0; i < pLUT_Tri_size; i++){
1372  pixel = pLUT_Tri_[i].SerializedOut(pixel);
1373  }
1374  }
1375  break;
1376  default:
1377  BIASERR("not yet implemented!");
1378  return -1;
1379  break;
1380  }
1381 
1382  return 0;
1383 }
1384 
1385 template <class InputStorageType, class OutputStorageType>
1387 SetLookupTable(const string& filename, InterpolationMethod& method)
1388 {
1389  // try to open file
1390  fstream stream;
1391  stream.open(filename.c_str(), fstream::in |
1392  fstream::binary);
1393  if (!stream.good())
1394  {
1395  BIASERR("Can not read file!");
1396  return -1;
1397  }
1398 
1399  //read size of LUT
1400  unsigned int sizeInFloats;
1401  for (unsigned int i=0; i<(sizeof(unsigned int)/sizeof(char)); i++) {
1402  stream.get( ((char*)(&sizeInFloats))[i] );
1403  if (!stream) {
1404  BIASERR("Error reading header from file: " << filename);
1405  return -1;
1406  }
1407  }
1408 
1409  //create buffer for LUT
1410  char* buffer = new char[sizeInFloats*(sizeof(float)/sizeof(char))];
1411 
1412  float *ptrToBuffer = (float*)buffer;
1413 
1414  //fill buffer from file
1415  unsigned int count = 0;
1416  while(stream.good()) {
1417  stream.get(*buffer++);
1418  count++;
1419  }
1420  //check for correct size of filled buffer
1421  if (count-1 != sizeInFloats*(sizeof(float)/sizeof(char))) {
1422  BIASERR("Error reading LUT, wrong file size!");
1423  delete[] buffer;
1424  return -1;
1425  }
1426 
1427  //finally create LUT from filled buffer
1428  int res = SetLookupTable(ptrToBuffer, sizeInFloats, method);
1429  stream.close();
1430 
1431  return res;
1432 }
1433 
1434 template <class InputStorageType, class OutputStorageType>
1436 SetLookupTable(const float* lutImageRow, unsigned int width,
1437  InterpolationMethod& method)
1438 {
1439  const float* pixel = lutImageRow;
1440  int methodTmp = *pixel;
1441  method = (InterpolationMethod)methodTmp;
1442  //const InterpolationMethod method = ;
1443  pixel++;
1444 
1445  switch (method)
1446  {
1447  case MapNearestNeighbor:
1448  BIASERR("not yet implemented!");
1449  return -1;
1450  break;
1451  case MapBilinear:
1452  {
1453  delete[] pLUT_Bil_;
1454  pLUT_Bil_size = (width-1) / BWM_LUT_Entry_Bilinear::GetSerializedSize(); //subtract one for interpolation identifier
1455  pLUT_Bil_ = new BWM_LUT_Entry_Bilinear[pLUT_Bil_size];
1456 
1457  for(int i = 0; i < pLUT_Bil_size; i++){
1458  pixel = pLUT_Bil_[i].SerializedIn(pixel);
1459  }
1460  }
1461  break;
1462  case MapTrilinear:
1463  delete[] pLUT_Tri_;
1464  pLUT_Tri_size = (width-1) / BWM_LUT_Entry_Trilinear::GetSerializedSize(); //subtract one for interpolation identifier
1465  pLUT_Tri_ = new BWM_LUT_Entry_Trilinear[pLUT_Tri_size];
1466 
1467  for(int i = 0; i < pLUT_Tri_size; i++){
1468  pixel = pLUT_Tri_[i].SerializedIn(pixel);
1469  }
1470  break;
1471  default:
1472  BIASERR("not yet implemented!");
1473  return -1;
1474  break;
1475  }
1476 
1477  return 0;
1478 }
1479 
1480 
1481 template <class InputStorageType, class OutputStorageType>
1485  InterpolationMethod method, bool newSink) {
1486 
1487  // generate multichannel channel image newsink with x,y and sink.size
1488  // if trilinear: pyramid (layer in 3rd channel),normalize (use BIASToTexture)
1489  // call appropriate this->Map()
1490  // generate pLUT_ with size of sink.pixelcount with correct interpolationtype
1491  // fill pLut from newsink
1492  BIASASSERT(sink.GetPixelCount()!=0);
1493 
1494  incomplete_ = false;
1495 
1496  ROI_ = *src.GetROI();
1497 
1498  //USED FOR NN MAPPING
1499  BWM_LUT_Entry_Nearest* current_NN = NULL;
1500 
1501  //USED FOR BILINEAR MAPPING
1502  BWM_LUT_Entry_Bilinear* current_Bi = NULL;
1503 
1504  //USED FOR TRILINEAR MAPPING
1505  BWM_LUT_Entry_Trilinear* current_Tri = NULL;
1506 
1507  if (pLUT_Nea_ != NULL) {
1508  delete[] pLUT_Nea_;
1509  pLUT_Nea_ = NULL;
1510  }
1511  if (pLUT_Bil_ != NULL) {
1512  delete[] pLUT_Bil_;
1513  pLUT_Bil_ = NULL;
1514  }
1515  if (pLUT_Tri_ != NULL) {
1516  delete[] pLUT_Tri_;
1517  pLUT_Tri_ = NULL;
1518  }
1519 
1520  switch (method)
1521  {
1522  case MapNearestNeighbor:
1523  pLUT_Nea_ = new BWM_LUT_Entry_Nearest[sink.GetPixelCount()];
1524  current_NN = &pLUT_Nea_[0];
1525  pLUT_Nea_size = sink.GetPixelCount();
1526  break;
1527  case MapBilinear:
1528  pLUT_Bil_ = new BWM_LUT_Entry_Bilinear[sink.GetPixelCount()];
1529  current_Bi = &pLUT_Bil_[0];
1530  pLUT_Bil_size = sink.GetPixelCount();
1531  break;
1532  case MapBicubic:
1533  break;
1534  case MapTrilinear:
1535  // check if pyramid is still large enough, mapping might have changed,
1536  // e.g. other homography on same image! P.S. Check if pyramid exists too
1537  //pyramid_.SetPyramidSize(1);
1538  pyramidSize_ = 1;
1539  if (pyramid_.IsEmpty()) {
1540  BuildPyramid_(src);
1541  }
1542  if (autoPyramidSize_) UpdatePyramidSize(*sink.GetROI(),
1543  src.GetWidth(), src.GetHeight());
1544  pLUT_Tri_ = new BWM_LUT_Entry_Trilinear[sink.GetPixelCount()];
1545  current_Tri = &pLUT_Tri_[0];
1546  pLUT_Tri_size = sink.GetPixelCount();
1547  break;
1548  case MapAnisotropic:
1549  //pLUT_Ani_ = new BWM_LUT_Entry_Anisotropic[sink.GetPixelCount()];
1550  break;
1552  break;
1553  }
1554  HomgPoint2D sink_p, src_p;
1555 
1556  for(unsigned int j = 0; j < sink.GetHeight(); j++){
1557  for(unsigned int i = 0; i < sink.GetWidth(); i++){
1558  sink_p[0] = i + offsetX_;
1559  sink_p[1] = j + offsetY_;
1560  sink_p[2] = 1;
1561 
1562  //if pixel can be unmapped and its unmapped coordinates are within the
1563  //source image, then:
1564 
1565  //WARNING: it checks if the pixels coordinates are within
1566  // [0,width-1]x[0,height-1] range, which can be a bit confusing
1567  // while the unmapped coordinates, like (-0.3; -0.4) theoretical-
1568  // -ly can be (and are) used by Nearest Neighbour Mapping, for
1569  // example (and it WON't be used by lookup-Nearest Neighbour
1570  // mapping in our case, so the images made by Direct-Mapping and
1571  // lookuptable-mapping can be slightly different)
1572  int off=0;
1573  if(method==MapBilinear)off=1;//avoid reading outside the src image during filtering
1574  //TODO check if this is required for other filter methods too!
1575  if(GetSourceCoordinates(sink_p, src_p) != -1 &&
1576  src_p[0]+off < src.GetWidth() &&
1577  src_p[1]+off < src.GetHeight() &&
1578  src_p[0] >= 0 &&
1579  src_p[1] >= 0){
1580  double xx = src_p[0];
1581  double yy = src_p[1];
1582 
1583  double xx_dx = xx - floor(xx);
1584  double yy_dy = yy - floor(yy);
1585 
1586  double local = 0;
1587  int local_low = 0;
1588  double local_dif = 0;
1589 
1590  double offset = pyramid_.GetPositionOffset();
1591 
1592  int src_channels = src.GetChannelCount();
1593  int src_width = src.GetWidth();
1594 
1595  int pyr_channels_low, pyr_channels_high;
1596  int pyr_wid_low, pyr_wid_high;
1597 
1598  switch (method)
1599  {
1600  case MapNearestNeighbor:
1601  //mapped-check
1602  current_NN->mapped = true;
1603  current_NN->index =
1604  src_channels*
1605  ((int)rint(yy)*src_width+(int)rint(xx));
1606  current_NN++;
1607  break;
1608  case MapBilinear:
1609  //mapped-check
1610  current_Bi->mapped = true;
1611 
1612  //pixel-indexes
1613  current_Bi->index_0_0 =
1614  src_channels*
1615  ((int)floor(yy)*src_width+(int)floor(xx));
1616  current_Bi->index_0_1 =
1617  src_channels*
1618  ((int)floor(yy+1)*src_width+(int)floor(xx));
1619  current_Bi->index_1_0 =
1620  src_channels*
1621  ((int)floor(yy)*src_width+(int)floor(xx+1));
1622  current_Bi->index_1_1 =
1623  src_channels*
1624  ((int)floor(yy+1)*src_width+(int)floor(xx+1));
1625 
1626  BIASASSERT(current_Bi->index_0_0<=(int)(src.GetPixelCount()*src_channels));
1627  BIASASSERT(current_Bi->index_0_1<=(int)(src.GetPixelCount()*src_channels));
1628  BIASASSERT(current_Bi->index_1_0<=(int)(src.GetPixelCount()*src_channels));
1629  BIASASSERT(current_Bi->index_1_1<=(int)(src.GetPixelCount()*src_channels));
1630 
1631  //pixel-weights
1632  current_Bi->XY_weight = xx_dx * yy_dy;
1633  current_Bi->Xy_weight = xx_dx *(1-yy_dy);
1634  current_Bi->xY_weight = (1-xx_dx)* yy_dy;
1635  current_Bi->xy_weight = (1-xx_dx)*(1-yy_dy);
1636 
1637  current_Bi++;
1638  break;
1639  case MapBicubic:
1640  break;
1641  case MapTrilinear:
1642  // WARNING: Direct mapping without LUT creates additional pyramid levels
1643  // whenever needed to avoid aliasing. LUT mapping clips these extra values
1644  // resulting in small differences between images mapped with these methods.
1645 
1646  //mapped-check
1647  current_Tri->mapped = true;
1648 
1649  //compute layer for trilinear filtering, 0 means best resolution
1650  ComputeLocalPyramidLayer_(sink_p, local);
1651  if (local > (int)pyramid_.size()-1)
1652  local = (int)pyramid_.size()-1;
1653  if (local < 0)
1654  local = 0;
1655 
1656  local_low = (int)floor(local);
1657  local_dif = local - (double)local_low;
1658 
1659  pyr_channels_low = pyramid_[local_low]->GetChannelCount();
1660  pyr_wid_low = pyramid_[local_low]->GetWidth();
1661 
1662  // check if local_low+1 is a level of the pyramid else clip
1663  if (local_low + 1 < (int)pyramid_.size())
1664  {
1665  pyr_channels_high = pyramid_[local_low+1]->GetChannelCount();
1666  pyr_wid_high = pyramid_[local_low+1]->GetWidth();
1667  current_Tri->pyr_level_high = local_low+1;
1668  }
1669  else
1670  {
1671  pyr_channels_high = pyramid_[local_low]->GetChannelCount();
1672  pyr_wid_high = pyramid_[local_low]->GetWidth();
1673  current_Tri->pyr_level_high = local_low;
1674  }
1675 
1676  //pyramid-level information
1677  current_Tri->pyr_level_low = local_low;
1678 
1679  current_Tri->pyr_diff = local_dif;
1680  current_Tri->pyr_diff_inv = 1.0-local_dif;
1681 
1682  //STAGE 0
1683 
1684  //xx and yy for the lower pyramid-level
1685  for(int loc = 0; loc < local_low; loc++){
1686  xx = (xx - offset)*0.5;
1687  yy = (yy - offset)*0.5;
1688  }
1689 
1690  // check indixes
1691  double test;
1692  if (GetImageValue_(xx, yy, *(pyramid_[local_low]), 0, test))
1693  {
1694  current_Tri->mapped = false;
1695  current_Tri++;
1696  break;
1697  }
1698 
1699  //Indexes for the lower level
1700 
1701  current_Tri->stage_0.index_0_0 =
1702  pyr_channels_low*
1703  ((int)floor(yy)*pyr_wid_low+(int)floor(xx));
1704  current_Tri->stage_0.index_0_1 =
1705  pyr_channels_low*
1706  ((int)floor(yy+1)*pyr_wid_low+(int)floor(xx));
1707  current_Tri->stage_0.index_1_0 =
1708  pyr_channels_low*
1709  ((int)floor(yy)*pyr_wid_low+(int)floor(xx+1));
1710  current_Tri->stage_0.index_1_1 =
1711  pyr_channels_low*
1712  ((int)floor(yy+1)*pyr_wid_low+(int)floor(xx+1));
1713 
1714  xx_dx = xx - floor(xx);
1715  yy_dy = yy - floor(yy);
1716 
1717  //Weights for the lower level
1718  current_Tri->stage_0.XY_weight = xx_dx * yy_dy;
1719  current_Tri->stage_0.Xy_weight = xx_dx *(1-yy_dy);
1720  current_Tri->stage_0.xY_weight = (1-xx_dx)* yy_dy;
1721  current_Tri->stage_0.xy_weight = (1-xx_dx)*(1-yy_dy);
1722 
1723  //STAGE 1
1724  if (local != (int)pyramid_.size()-1){
1725  xx = (xx - offset)*0.5;
1726  yy = (yy - offset)*0.5;
1727  }
1728 
1729  // check indixes
1730  if (current_Tri->pyr_level_high!=local_low) {
1731  if (GetImageValue_(xx, yy, *(pyramid_[current_Tri->pyr_level_high]), 0, test) != 0) {
1732  current_Tri->mapped = false;
1733  current_Tri++;
1734  break;
1735  }
1736  }
1737  //Indexes for the higher level
1738  current_Tri->stage_1.index_0_0 =
1739  pyr_channels_high*
1740  ((int)floor(yy)*pyr_wid_high+(int)floor(xx));
1741  current_Tri->stage_1.index_0_1 =
1742  pyr_channels_high*
1743  ((int)floor(yy+1)*pyr_wid_high+(int)floor(xx));
1744  current_Tri->stage_1.index_1_0 =
1745  pyr_channels_high*
1746  ((int)floor(yy)*pyr_wid_high+(int)floor(xx+1));
1747  current_Tri->stage_1.index_1_1 =
1748  pyr_channels_high*
1749  ((int)floor(yy+1)*pyr_wid_high+(int)floor(xx+1));
1750 
1751  xx_dx = xx - floor(xx);
1752  yy_dy = yy - floor(yy);
1753 
1754  //Weights for the higher level
1755  current_Tri->stage_1.XY_weight = xx_dx * yy_dy;
1756  current_Tri->stage_1.Xy_weight = xx_dx *(1-yy_dy);
1757  current_Tri->stage_1.xY_weight = (1-xx_dx)* yy_dy;
1758  current_Tri->stage_1.xy_weight = (1-xx_dx)*(1-yy_dy);
1759 
1760  current_Tri++;
1761  break;
1762  case MapAnisotropic:
1763  break;
1765  break;
1766  }
1767  } else {
1768  incomplete_ = true;
1769  switch (method)
1770  {
1771  case MapNearestNeighbor:
1772  //mapped-check
1773  current_NN->mapped = false;
1774  current_NN++;
1775  break;
1776  case MapBilinear:
1777  //mapped-check
1778  current_Bi->mapped = false;
1779  current_Bi++;
1780  break;
1781  case MapBicubic:
1782  break;
1783  case MapTrilinear:
1784  //mapped-check
1785  current_Tri->mapped = false;
1786  current_Tri++;
1787  break;
1788  case MapAnisotropic:
1789  break;
1791  break;
1792  }
1793  }
1794  }
1795  }
1796  if (incomplete_) {
1797  incomplete_ = false;
1798  return +1;
1799  }
1800  return 0;
1801 }
1802 
1803 template <class InputStorageType, class OutputStorageType>
1807  InterpolationMethod method) {
1808  //ROI_ = *src.GetROI();
1809 #ifdef BIAS_DEBUG
1810  if (src.IsPlanar() || sink.IsPlanar()) {
1811  BIASERR("not implemented for planar images! (not yet ?)");
1812  BIASABORT;
1813  }
1814 #endif
1815  OutputStorageType* Data = sink.GetImageData();
1816 
1817  int wid = sink.GetWidth();
1818  int hei = sink.GetHeight();
1819  unsigned int channels = src.GetChannelCount();
1820  BIASASSERT(channels == sink.GetChannelCount());
1821 
1822  int srcwid = src.GetWidth();
1823  int srchei = src.GetHeight();
1824  int biggy2 = srcwid*srchei;
1825  int biggy = wid*hei;
1826  int biggy3 = biggy2 * channels;
1827  //USED FOR NN MAPPING
1828  BWM_LUT_Entry_Nearest* current_NN;
1829 
1830  //USED FOR BILINEAR MAPPING
1831  BWM_LUT_Entry_Bilinear* current_Bi;
1832 
1833  switch (method)
1834  {
1835  case MapNearestNeighbor:
1836  //if lookups were not computed
1837  if(pLUT_Nea_ == NULL){
1838  BIASWARN("LOOKUP-TABLE FOR NN IS NOT READY! generating!!!");
1839  PrepareLookupTableMapping(src, sink, method);
1840  }
1841 
1842  //Lookup-Table must be same size as sink
1843  BIASASSERT(biggy == pLUT_Nea_size);
1844 
1845  current_NN = &pLUT_Nea_[0];
1846 
1847  for(int i = 0; i < biggy; i++){
1848  if(pLUT_Nea_[i].mapped){
1849  if(current_NN->index < biggy3) {
1850  if (channels == 1)
1851  *Data++ =(OutputStorageType)src.GetImageData()[current_NN->index];
1852  else {
1853  for (unsigned int c=0;c<channels;c++) {
1854  Data[c] =(OutputStorageType)
1855  src.GetImageData()[(current_NN->index)+c];
1856  } // for channels
1857  Data+=channels;
1858  } // else if (channels==1)
1859  } // if (current_NN->index < biggy2)
1860  else {
1861  //don't do this if preinitialization of
1862  //of image background shall be effective
1863  //(e.g. color unmapped areas in rectified image pair differently)
1864  // ClipValue_(0, *Data++);
1865  Data+=channels;
1866  }
1867  }
1868  else{
1869  //don't do this if preinitialization of
1870  //of image background shall be effective
1871  //(e.g. color unmapped areas in rectified image pair differently)
1872  // ClipValue_(0, *Data++);
1873  Data+=channels;
1874  }
1875  current_NN++;
1876  }
1877  break;
1878  case MapBilinear:
1879  //if lookups were not computed
1880  if(pLUT_Bil_ == NULL){
1881  BIASWARN("LOOKUP-TABLE FOR BILINEAR IS NOT READY! generating!!!");
1882  PrepareLookupTableMapping(src, sink, method);
1883  }
1884 
1885  //Lookup-Table must be same size as sink
1886  BIASASSERT(biggy == pLUT_Bil_size);
1887 
1888  current_Bi = &pLUT_Bil_[0];
1889 
1890  for(int i = 0; i < biggy; i++){
1891  for(unsigned int c=0; c<channels; c++) {
1892  if(current_Bi->mapped){
1893  ClipValue_(
1894  (current_Bi->xy_weight*
1895  src.GetImageData()[current_Bi->index_0_0+c] +
1896  current_Bi->xY_weight*
1897  src.GetImageData()[current_Bi->index_0_1+c] +
1898  current_Bi->Xy_weight*
1899  src.GetImageData()[current_Bi->index_1_0+c] +
1900  current_Bi->XY_weight*
1901  src.GetImageData()[current_Bi->index_1_1+c]),
1902  *Data++ ) ;
1903  }
1904  else{
1905  //don't do this if preinitialization of
1906  //of image background shall be effective
1907  //(e.g. color unmapped areas in rectified image pair differently)
1908  // ClipValue_(0, *Data++);
1909  Data++;
1910  }
1911  }//end of channels
1912  current_Bi++;
1913  }
1914  break;
1915  case MapBicubic:
1916  BIASERR("LOOKUP-TABLES FOR BICUBIC ARE NOT IMPLEMENTED!");
1917  BIASABORT;
1918 
1919  //Lookup-Table must be same size as sink
1920  //BIASASSERT(biggy == sizeof(pLUT_Bic_));
1921 
1922  break;
1923  case MapTrilinear: {
1924  //if lookups were not computed
1925  if(pLUT_Tri_ == NULL){
1926  BIASWARN("LOOKUP-TABLE FOR TRILINEAR IS NOT READY! generating!!!");
1927  PrepareLookupTableMapping(src, sink, method);
1928  }
1929 
1930  //Lookup-Table must be same size as sink
1931  BIASASSERT(biggy == pLUT_Tri_size);
1932 
1933  //build pyramid for mapping
1934  BuildPyramid_(src);
1935  if (autoPyramidSize_)
1936  UpdatePyramidSize(*sink.GetROI(), src.GetWidth(), src.GetHeight());
1937 
1938 // cout << sizeof(BWM_LUT_Entry_Bilinear) << endl;
1939 // cout << sizeof(BWM_LUT_Entry_Trilinear) << endl;
1940 
1941  BWM_LUT_Entry_Trilinear* current_Tri = &pLUT_Tri_[0];
1942  for(int i = 0; i < biggy; i++){
1943  for(unsigned int c=0; c<channels; c++) {
1944  if(current_Tri->mapped) {
1945  // average bilinear values from two closest pyramid levels
1946  const BWM_LUT_Entry_Bilinear& pstruct_0 = current_Tri->stage_0;
1947  const BWM_LUT_Entry_Bilinear& pstruct_1 = current_Tri->stage_1;
1948 
1949  const InputStorageType* pPyrLow =
1950  pyramid_[current_Tri->pyr_level_low]->GetImageData();
1951  const InputStorageType* pPyrHigh =
1952  pyramid_[current_Tri->pyr_level_high]->GetImageData();
1953  // shouldnt we use clipvalue here to avoid range errors ?
1954  ClipValue_(
1955  (current_Tri->pyr_diff_inv *
1956  (pstruct_0.xy_weight * pPyrLow[pstruct_0.index_0_0+c] +
1957  pstruct_0.xY_weight * pPyrLow[pstruct_0.index_0_1+c] +
1958  pstruct_0.Xy_weight * pPyrLow[pstruct_0.index_1_0+c] +
1959  pstruct_0.XY_weight * pPyrLow[pstruct_0.index_1_1+c]) +
1960  current_Tri->pyr_diff *
1961  (pstruct_1.xy_weight * pPyrHigh[pstruct_1.index_0_0+c] +
1962  pstruct_1.xY_weight * pPyrHigh[pstruct_1.index_0_1+c] +
1963  pstruct_1.Xy_weight * pPyrHigh[pstruct_1.index_1_0+c] +
1964  pstruct_1.XY_weight * pPyrHigh[pstruct_1.index_1_1+c])),
1965  *Data++);
1966 
1967  } else {
1968  // write zero, we might as well only increase pointer here
1969  // and leave the init up to the caller
1970  //*Data++ = OutputStorageType(0);
1971  Data++;
1972  }
1973  }
1974  current_Tri++;
1975  }
1976  break;
1977  }
1978  case MapAnisotropic:
1979  BIASERR("LOOKUP-TABLES FOR ANISOTROPIC ARE NOT IMPLEMENTED!");
1980  BIASABORT;
1981 
1982  //Lookup-Table must be same size as sink
1983  //BIASASSERT(biggy == sizeof(pLUT_Ani_));
1984 
1985  break;
1986  default:
1987  BIASERR("FALSE INTERPOLATION METHOD!");
1988  return -1;
1989  }
1990  return 0;
1991  // BIASASSERT(sink.samesize(pLUT_));
1992  // switch interpolationmethod_
1993  // cast plut to pluntinterpolationmethod
1994  // run over all pixels in sink,
1995 }
1996 
1997 
1998 
1999 
2000 
2001 
2002 template <class InputStorageType, class OutputStorageType>
2004 GenerateTestImage(Image<InputStorageType>& im, bool highFrequencyCross,
2005  InputStorageType dark,
2006  InputStorageType bright,
2007  const Matrix3x3<double>& Hinv) {
2008  InputStorageType dbright = bright - dark;
2009  if (im.IsEmpty()) {
2010  im.Init(640, 480);
2011  }
2012  BIASASSERT(im.GetWidth()>10);
2013  BIASASSERT(im.GetHeight()>10);
2014  BIASASSERT(im.GetChannelCount()==1);
2015 
2016  HomgPoint2D point(0.0, 0.0, 1.0);
2017  Random R;
2018 
2019 #define OFFSET 0.5
2020  for(unsigned int y=0; y<im.GetHeight(); y++) {
2021  for(unsigned int x=0; x<im.GetWidth(); x++) {
2022  double angle = 0, angleaverage = 0;
2023  // avoid aliasing in original image by presmoothing:
2024  for (unsigned int offset = 0; offset<5; offset++) {
2025  point[1] = double(y)-(im.GetHeight()/2.0);
2026  point[0] = double(x)-(im.GetWidth()/2.0);
2027  switch (offset) {
2028  case 0: point[0] -= OFFSET; point[1] -= OFFSET; break;
2029  case 1: point[0] += OFFSET; point[1] -= OFFSET; break;
2030  case 2: point[0] += OFFSET; point[1] += OFFSET; break;
2031  case 3: point[0] -= OFFSET; point[1] += OFFSET; break;
2032  }
2033  point = Hinv * point;
2034  point.Homogenize();
2035  angle = atan2(point[1]-OFFSET, point[0]-OFFSET);
2036  if (angle<0.0) angle += M_PI*2.0;
2037  //angle = int(rint(angle / (M_PI*2.0) * 32.0))%2;
2038  angle = (angle / (M_PI*2.0) * 32.0);
2039  angleaverage += fabs((angle / 2.0) - rint((angle / 2.0)));
2040 
2041  }
2042  angleaverage *= 0.2;
2043 
2044  im.GetImageDataArray()[y][x] = dark +
2045  (InputStorageType)(angleaverage * 2.0*double(dbright));
2046  }
2047  }
2048  if (highFrequencyCross) {
2049  int crosspos = 2; // 2 mean half image size, 3 means third
2050  int offset = 12; // dont go closer than this to center
2051  for(unsigned int y=0; y<im.GetHeight()/crosspos-offset; y++) {
2052  im.GetImageDataArray()[y][im.GetWidth()/crosspos] =
2053  (InputStorageType)(dark+bright*((y+1)%2));
2054  im.GetImageDataArray()[y][im.GetWidth()/crosspos+1] =
2055  (InputStorageType)(dark+dbright*(y%2));
2056  }
2057  for(unsigned int x=0; x<im.GetWidth()/crosspos-offset; x++) {
2058  im.GetImageDataArray()[im.GetHeight()/crosspos-2][x] =
2059  im.GetImageDataArray()[im.GetHeight()/crosspos-1][x] =
2060  im.GetImageDataArray()[im.GetHeight()/crosspos][x] =
2061  im.GetImageDataArray()[im.GetHeight()/crosspos+1][x] =
2062  (InputStorageType)(dark+dbright*((x/4)%2));
2063  }
2064  for(unsigned int y=im.GetHeight()/crosspos+offset; y<im.GetHeight()-3;
2065  y++) {
2066  im.GetImageDataArray()[y][im.GetWidth()/crosspos-1] =
2067  im.GetImageDataArray()[y][im.GetWidth()/crosspos-2] =
2068  im.GetImageDataArray()[y][im.GetWidth()/crosspos] =
2069  im.GetImageDataArray()[y][im.GetWidth()/crosspos+1] =
2070  im.GetImageDataArray()[y][im.GetWidth()/crosspos+2] =
2071  (InputStorageType)(dark+dbright*((y/5)%2));
2072  }
2073  for(unsigned int x=im.GetWidth()/crosspos+offset; x<im.GetWidth()-1; x++) {
2074  im.GetImageDataArray()[im.GetHeight()/crosspos-1][x] =
2075  im.GetImageDataArray()[im.GetHeight()/crosspos][x] =
2076  im.GetImageDataArray()[im.GetHeight()/crosspos+1][x] =
2077  (InputStorageType)(dark+dbright*((x/3)%2));
2078  }
2079 
2080  }
2081 }
2082 
2083 // ######################### template instantiation ########################
2084 
2085 
2087 template class BackwardMapping<float, float>;
2090 
2091 #if defined(BUILD_IMAGE_CHAR)
2093 template class BackwardMapping<char, char>;
2094 #endif
2095 
2096 #if defined(BUILD_IMAGE_USHORT)
2098 #endif
2099 
2100 #if defined(BUILD_IMAGE_SHORT)
2101 template class BackwardMapping<short, short>;
2102 #endif
2103 
2104 #if defined(BUILD_IMAGE_SHORT)&&defined(BUILD_IMAGE_USHORT)
2106 #endif
2107 
2108 #if defined(BUILD_IMAGE_INT)
2109 template class BackwardMapping<int,int>;
2110 #endif
2111 
2112 #if defined(BUILD_IMAGE_USHORT)
2114 #endif
2115 
2116 #if defined(BUILD_IMAGE_USHORT) && defined(BUILD_IMAGE_INT)
2118 #endif
2119 
2120 #if defined(BUILD_IMAGE_DOUBLE)
2121 template class BackwardMapping<double,double>;
2122 #endif
2123 
2124 } // namespace BIAS
float xy_weight
PIXEL X- weight for (1-dx)*(1-dy)
int index
nearest x,y-position in the source image, written as an index
double BilinearInterpolationRGBInterleaved(const double x, const double y, unsigned int channel) const
Bilinear interpolation for interleaved RGB images.
Definition: Image.hh:460
class for handling different region of interest (ROI) representations...
Definition: ROI.hh:118
InterpolationMethod
accuracy for resampling
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
int index_1_1
int SetCorners(unsigned UpperLeftX, unsigned UpperLeftY, unsigned LowerRightX, unsigned LowerRightY)
Sets a rectangular region of interest.
Definition: ROI.cpp:287
void Resize(unsigned width, unsigned height)
Resizes parent image.
Definition: ROI.cpp:227
BWM_LUT_Entry_Bilinear stage_1
void GetCorners(unsigned &UpperLeftX, unsigned &UpperLeftY, unsigned &LowerRightX, unsigned &LowerRightY) const
Return the region of interest, by saving the coordinates within the variables defined by the paramete...
Definition: ROI.hh:443
bool IsEmpty() const
check if ImageData_ points to allocated image buffer or not
Definition: ImageBase.hh:245
bool IsInterleaved() const
Definition: ImageBase.hh:491
Abstract base class to map an image (texture) src to an image sink with an arbitrary continuous funct...
unsigned GetWidth() const
width capacity of roi (image width)
Definition: ROI.hh:250
float xY_weight
PIXEL – X- weight for (1-dx)*dy.
float XY_weight
PIXEL – -X weight for dx*dy.
StorageType FastBilinearInterpolationGrey(const double x, const double y) const
Fast bilinear interpolation for grey-value images.
Definition: Image.hh:402
bool IsPlanar() const
Definition: ImageBase.hh:484
double GetUniformDistributed(const double min, const double max)
on succesive calls return uniform distributed random variable between min and max ...
Definition: Random.hh:84
unsigned int GetWidth() const
Definition: ImageBase.hh:312
int index_0_0
const float * SerializedIn(const float *pointerToHeadOfInputMem)
Retrieves the content of object from serialized mem.
void GetROICorners(unsigned int &UpperLeftX, unsigned int &UpperLeftY, unsigned int &LowerRightX, unsigned int &LowerRightY) const
access region of interest rectangle JW
Definition: ImageBase.cpp:1067
base class for storing precomputed lookup information for trilinear interpolation in BackwardMapping ...
double BicubicInterpolation(const double &x, const double &y, const unsigned short int channel=0) const
Generic bicubic interpolation.
Definition: Image.cpp:1403
BWM_LUT_Entry_Bilinear stage_0
pixel values on both lower and higher pyramid levels
ROI * GetROI()
Returns a pointer to the roi object.
Definition: ImageBase.hh:615
base class for storing precomputed look-up information in BackwardMapping
float pyr_diff_inv
pyr_diff_inv + pyr_diff = 1
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
unsigned int GetHeight() const
Definition: ImageBase.hh:319
int Map(const Image< InputStorageType > &src, Image< OutputStorageType > &sink, InterpolationMethod=MapTrilinear, bool newSink=false, double SuperSampling=1.0)
backward mapping with various interpolations
Maps image src to image sink with affine transformation.
void FillImageWithConstValue(StorageType Value)
fill grey images
Definition: Image.cpp:456
int index_0_1
bool mapped
boolean showing if the pixel can be correctly mapped
int pyr_level_high
higher pyramid-level-index
void Init(unsigned int Width, unsigned int Height, unsigned int channels=1, enum EStorageType storageType=ST_unsignedchar, const bool interleaved=true)
calls Init from ImageBase storageType is ignored, just dummy argument
Definition: Image.cpp:421
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
float pyr_diff
pyr_level + pyr_diff = exact pyramid-level of the pixel
enum EColorModel GetColorModel() const
Definition: ImageBase.hh:407
bool mapped
boolean showing if the pixel can be correctly mapped
void BIASToTextureCoordinates(const double &biasx, const double &biasy, double &gl_x, double &gl_y) const
transfer BIAS image coordinates [0..w-1] x [0..h-1] to GL texture coordinates [0..1[ x [0..1[
Definition: ImageBase.hh:141
base class for storing precomputed lookup information for bilinear interpolation in BackwardMapping ...
const float * SerializedIn(const float *pointerToHeadOfInputMem)
Retrieves the content of object from serialized mem.
int index_1_0
int pyr_level_low
lower pyramid-level-index
unsigned GetCornerLowerRightY() const
fetch individual ROI corner coordinate
Definition: ROI.hh:207
void SetAffineTransformation(const double &a11, const double &a12, const double &a21, const double &a22, const double &dx, const double &dy)
set A (source = A * sink) before calling Map()
float Xy_weight
PIXEL -X weight for dx*(1-dy)
unsigned long int GetPixelCount() const
returns number of pixels in image
Definition: ImageBase.hh:422
void GetSystemMatrix(Matrix< T > &dest) const
compute square system matrix dest = A^T * A
Definition: Matrix.hh:1075
class for producing random numbers from different distributions
Definition: Random.hh:51
unsigned GetCornerUpperLeftY() const
fetch individual ROI corner coordinate
Definition: ROI.hh:195
unsigned GetCornerLowerRightX() const
fetch individual ROI corner coordinate
Definition: ROI.hh:201
void SetZero()
zeroes the image
Definition: ImageBase.hh:83
bool mapped
boolean showing if the pixel can be correctly mapped
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase
Definition: Image.hh:153
unsigned GetCornerUpperLeftX() const
fetch individual ROI corner coordinate
Definition: ROI.hh:189