OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimThinPlateSpline.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * $Id: thinplatespline.cpp 15547 2008-10-17 17:45:03Z rouault $
3  *
4  * Project: GDAL Warp API
5  * Purpose: Implemenentation of 2D Thin Plate Spline transformer.
6  * Author: VIZRT Development Team.
7  *
8  * This code was provided by Gilad Ronnen (gro at visrt dot com) with
9  * permission to reuse under the following license.
10  *
11  ******************************************************************************
12  * Copyright (c) 2004, VIZRT Inc.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included
22  * in all copies or substantial portions of the Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  ****************************************************************************/
32 
33 
34 #include <cstdio>
36 #include <ossim/base/ossimCommon.h>
37 #include <ossim/base/ossimNotify.h>
39 
40 #ifndef FLT_MAX
41 # define FLT_MAX 1e+37
42 # define FLT_MIN 1e-37
43 #endif
44 
48 
49 #define A(r,c) _AA[ _nof_eqs * (r) + (c) ]
50 #define Ainv(r,c) _Ainv[ _nof_eqs * (r) + (c) ]
51 
52 
53 #define VIZ_GEOREF_SPLINE_DEBUG 0
54 
55 static int matrixInvert( int N, double input[], double output[] );
56 
58 
59 {
60  int new_max = _max_nof_points*2 + 2 + 3;
61  int i;
62 
63  x.resize( new_max );
64  y.resize( new_max );
65  u.resize( new_max );
66  unused.resize( new_max );
67  index.resize( new_max );
68  for( i = 0; i < _nof_vars; i++ )
69  {
70  rhs[i].resize( new_max );
71  coef[i].resize( new_max );
72  }
73 
74  _max_nof_points = new_max - 3;
75 }
76 
77 int ossimThinPlateSpline::addPoint( const double Px, const double Py, const double *Pvars )
78 {
80  int i;
81 
83  growPoints();
84 
85  i = _nof_points;
86  //A new point is added
87  x[i] = Px;
88  y[i] = Py;
89  for ( int j = 0; j < _nof_vars; j++ )
90  rhs[j][i+3] = Pvars[j];
91  _nof_points++;
92  return 1;
93 }
94 
95 bool ossimThinPlateSpline::changePoint(int index, double Px, double Py, double* Pvars)
96 {
97  if ( index < _nof_points )
98  {
99  int i = index;
100  x[i] = Px;
101  y[i] = Py;
102  for ( int j = 0; j < _nof_vars; j++ )
103  rhs[j][i+3] = Pvars[j];
104  }
105 
106  return( true );
107 }
108 
109 bool ossimThinPlateSpline::getXy(int index, double& outX, double& outY)const
110 {
111  bool ok;
112 
113  if ( index < _nof_points )
114  {
115  ok = true;
116  outX = x[index];
117  outY = y[index];
118  }
119  else
120  {
121  ok = false;
122  outX = outY = 0.0f;
123  }
124 
125  return(ok);
126 }
127 
128 int ossimThinPlateSpline::deletePoint(const double Px, const double Py )
129 {
130  for ( int i = 0; i < _nof_points; i++ )
131  {
132  if ( ( ossim::abs(Px - x[i]) <= _tx ) && ( ossim::abs(Py - y[i]) <= _ty ) )
133  {
134  for ( int j = i; j < _nof_points - 1; j++ )
135  {
136  x[j] = x[j+1];
137  y[j] = y[j+1];
138  for ( int k = 0; k < _nof_vars; k++ )
139  rhs[k][j+3] = rhs[k][j+3+1];
140  }
141  _nof_points--;
143  return(1);
144  }
145  }
146  return(0);
147 }
148 
150 {
151  int r, c, v;
152  int p;
153 
154  // No points at all
155  if ( _nof_points < 1 )
156  {
158  return(0);
159  }
160 
161  // Only one point
162  if ( _nof_points == 1 )
163  {
165  return(1);
166  }
167  // Just 2 points - it is necessarily 1D case
168  if ( _nof_points == 2 )
169  {
170  _dx = x[1] - x[0];
171  _dy = y[1] - y[0];
172  double fact = 1.0 / ( _dx * _dx + _dy * _dy );
173  _dx *= fact;
174  _dy *= fact;
175 
177  return(2);
178  }
179 
180  // More than 2 points - first we have to check if it is 1D or 2D case
181 
182  double xmax = x[0], xmin = x[0], ymax = y[0], ymin = y[0];
183  double delx, dely;
184  double xx, yy;
185  double sumx = 0.0f, sumy= 0.0f, sumx2 = 0.0f, sumy2 = 0.0f, sumxy = 0.0f;
186  double SSxx, SSyy, SSxy;
187 
188  for ( p = 0; p < _nof_points; p++ )
189  {
190  xx = x[p];
191  yy = y[p];
192 
193  xmax = ossim::max( xmax, xx );
194  xmin = ossim::min( xmin, xx );
195  ymax = ossim::max( ymax, yy );
196  ymin = ossim::min( ymin, yy );
197 
198  sumx += xx;
199  sumx2 += xx * xx;
200  sumy += yy;
201  sumy2 += yy * yy;
202  sumxy += xx * yy;
203  }
204  delx = xmax - xmin;
205  dely = ymax - ymin;
206 
207  SSxx = sumx2 - sumx * sumx / _nof_points;
208  SSyy = sumy2 - sumy * sumy / _nof_points;
209  SSxy = sumxy - sumx * sumy / _nof_points;
210 
211  if ( delx < 0.001 * dely || dely < 0.001 * delx ||
212  fabs ( SSxy * SSxy / ( SSxx * SSyy ) ) > 0.99 )
213  {
214  int p1;
215 
217 
218  _dx = _nof_points * sumx2 - sumx * sumx;
219  _dy = _nof_points * sumy2 - sumy * sumy;
220  double fact = 1.0 / sqrt( _dx * _dx + _dy * _dy );
221  _dx *= fact;
222  _dy *= fact;
223 
224  for ( p = 0; p < _nof_points; p++ )
225  {
226  double dxp = x[p] - x[0];
227  double dyp = y[p] - y[0];
228  u[p] = _dx * dxp + _dy * dyp;
229  unused[p] = 1;
230  }
231 
232  for ( p = 0; p < _nof_points; p++ )
233  {
234  int min_index = -1;
235  double min_u = 0;
236  for ( p1 = 0; p1 < _nof_points; p1++ )
237  {
238  if ( unused[p1] )
239  {
240  if ( min_index < 0 || u[p1] < min_u )
241  {
242  min_index = p1;
243  min_u = u[p1];
244  }
245  }
246  }
247  index[p] = min_index;
248  unused[min_index] = 0;
249  }
250 
251  return(3);
252  }
253 
255 
256  _nof_eqs = _nof_points + 3;
257 
258  _AA.resize( _nof_eqs * _nof_eqs );
259  _Ainv.resize(_nof_eqs * _nof_eqs );
260 
261  // Calc the values of the matrix A
262  for ( r = 0; r < 3; r++ )
263  for ( c = 0; c < 3; c++ )
264  A(r,c) = 0.0;
265 
266  for ( c = 0; c < _nof_points; c++ )
267  {
268  A(0,c+3) = 1.0;
269  A(1,c+3) = x[c];
270  A(2,c+3) = y[c];
271 
272  A(c+3,0) = 1.0;
273  A(c+3,1) = x[c];
274  A(c+3,2) = y[c];
275  }
276 
277  for ( r = 0; r < _nof_points; r++ )
278  for ( c = r; c < _nof_points; c++ )
279  {
280  A(r+3,c+3) = baseFunc( x[r], y[r], x[c], y[c] );
281  if ( r != c )
282  A(c+3,r+3 ) = A(r+3,c+3);
283  }
284 
285 
286  // Invert the matrix
287  int status = matrixInvert( _nof_eqs, &_AA.front(), &_Ainv.front() );
288 
289  if ( !status )
290  {
292  << " There is a problem to invert the interpolation matrix\n"
293  << std::endl;
294  return 0;
295  }
296 
297  // calc the coefs
298  for ( v = 0; v < _nof_vars; v++ )
299  for ( r = 0; r < _nof_eqs; r++ )
300  {
301  coef[v][r] = 0.0;
302  for ( c = 0; c < _nof_eqs; c++ )
303  coef[v][r] += Ainv(r,c) * rhs[v][c];
304  }
305 
306  return(4);
307 }
308 
309 int ossimThinPlateSpline::getPoint( const double Px, const double Py, double *vars )const
310 {
311  int v, r;
312  double tmp, Pu;
313  double fact;
314  int leftP=0, rightP=0, found = 0;
315 
316  switch ( type )
317  {
319  for ( v = 0; v < _nof_vars; v++ )
320  vars[v] = 0.0;
321  break;
323  for ( v = 0; v < _nof_vars; v++ )
324  vars[v] = rhs[v][3];
325  break;
327  fact = _dx * ( Px - x[0] ) + _dy * ( Py - y[0] );
328  for ( v = 0; v < _nof_vars; v++ )
329  vars[v] = ( 1 - fact ) * rhs[v][3] + fact * rhs[v][4];
330  break;
332  Pu = _dx * ( Px - x[0] ) + _dy * ( Py - y[0] );
333  if ( Pu <= u[index[0]] )
334  {
335  leftP = index[0];
336  rightP = index[1];
337  }
338  else if ( Pu >= u[index[_nof_points-1]] )
339  {
340  leftP = index[_nof_points-2];
341  rightP = index[_nof_points-1];
342  }
343  else
344  {
345  for ( r = 1; !found && r < _nof_points; r++ )
346  {
347  leftP = index[r-1];
348  rightP = index[r];
349  if ( Pu >= u[leftP] && Pu <= u[rightP] )
350  found = 1;
351  }
352  }
353 
354  fact = ( Pu - u[leftP] ) / ( u[rightP] - u[leftP] );
355  for ( v = 0; v < _nof_vars; v++ )
356  vars[v] = ( 1.0 - fact ) * rhs[v][leftP+3] +
357  fact * rhs[v][rightP+3];
358  break;
360  for ( v = 0; v < _nof_vars; v++ )
361  vars[v] = coef[v][0] + coef[v][1] * Px + coef[v][2] * Py;
362 
363  for ( r = 0; r < _nof_points; r++ )
364  {
365  tmp = baseFunc( Px, Py, x[r], y[r] );
366  for ( v= 0; v < _nof_vars; v++ )
367  vars[v] += coef[v][r+3] * tmp;
368  }
369  break;
371  fprintf(stderr, " A point was added after the last solve\n");
372  fprintf(stderr, " NO interpolation - return values are zero\n");
373  for ( v = 0; v < _nof_vars; v++ )
374  vars[v] = 0.0;
375  return(0);
376  break;
378  fprintf(stderr, " A point was deleted after the last solve\n");
379  fprintf(stderr, " NO interpolation - return values are zero\n");
380  for ( v = 0; v < _nof_vars; v++ )
381  vars[v] = 0.0;
382  return(0);
383  break;
384  default :
385  return(0);
386  break;
387  }
388  return(1);
389 }
390 
391 double ossimThinPlateSpline::baseFunc( const double x1, const double y1,
392  const double x2, const double y2 )const
393 {
394  if ( ( x1 == x2 ) && (y1 == y2 ) )
395  return 0.0;
396 
397  double dist = ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 );
398 
399  return dist * log( dist );
400 }
401 
402 static int matrixInvert( int N, double input[], double output[] )
403 {
404  // Receives an array of dimension NxN as input. This is passed as a one-
405  // dimensional array of N-squared size. It produces the inverse of the
406  // input matrix, returned as output, also of size N-squared. The Gauss-
407  // Jordan Elimination method is used. (Adapted from a BASIC routine in
408  // "Basic Scientific Subroutines Vol. 1", courtesy of Scott Edwards.)
409 
410  // Array elements 0...N-1 are for the first row, N...2N-1 are for the
411  // second row, etc.
412 
413  // We need to have a temporary array of size N x 2N. We'll refer to the
414  // "left" and "right" halves of this array.
415 
416  int row, col;
417 
418 #if 0
419  fprintf(stderr, "Matrix Inversion input matrix (N=%d)\n", N);
420  for ( row=0; row<N; row++ )
421  {
422  for ( col=0; col<N; col++ )
423  {
424  fprintf(stderr, "%5.2f ", input[row*N + col ] );
425  }
426  fprintf(stderr, "\n");
427  }
428 #endif
429 
430  int tempSize = 2 * N * N;
431  std::vector<double> temp(tempSize);
432  double ftemp;
433 
434  if (temp.empty()) {
435 
436  fprintf(stderr, "matrixInvert(): ERROR - memory allocation failed.\n");
437  return false;
438  }
439 
440  // First create a double-width matrix with the input array on the left
441  // and the identity matrix on the right.
442 
443  for ( row=0; row<N; row++ )
444  {
445  for ( col=0; col<N; col++ )
446  {
447  // Our index into the temp array is X2 because it's twice as wide
448  // as the input matrix.
449 
450  temp[ 2*row*N + col ] = input[ row*N+col ]; // left = input matrix
451  temp[ 2*row*N + col + N ] = 0.0f; // right = 0
452  }
453  temp[ 2*row*N + row + N ] = 1.0f; // 1 on the diagonal of RHS
454  }
455 
456  // Now perform row-oriented operations to convert the left hand side
457  // of temp to the identity matrix. The inverse of input will then be
458  // on the right.
459 
460  int max;
461  int k=0;
462  for (k = 0; k < N; k++)
463  {
464  if (k+1 < N) // if not on the last row
465  {
466  max = k;
467  for (row = k+1; row < N; row++) // find the maximum element
468  {
469  if (fabs( temp[row*2*N + k] ) > fabs( temp[max*2*N + k] ))
470  {
471  max = row;
472  }
473  }
474 
475  if (max != k) // swap all the elements in the two rows
476  {
477  for (col=k; col<2*N; col++)
478  {
479  ftemp = temp[k*2*N + col];
480  temp[k*2*N + col] = temp[max*2*N + col];
481  temp[max*2*N + col] = ftemp;
482  }
483  }
484  }
485 
486  ftemp = temp[ k*2*N + k ];
487  if ( ftemp == 0.0f ) // matrix cannot be inverted
488  {
489  return false;
490  }
491 
492  for ( col=k; col<2*N; col++ )
493  {
494  temp[ k*2*N + col ] /= ftemp;
495  }
496 
497  for ( row=0; row<N; row++ )
498  {
499  if ( row != k )
500  {
501  ftemp = temp[ row*2*N + k ];
502  for ( col=k; col<2*N; col++ )
503  {
504  temp[ row*2*N + col ] -= ftemp * temp[ k*2*N + col ];
505  }
506  }
507  }
508  }
509 
510  // Retrieve inverse from the right side of temp
511 
512  for (row = 0; row < N; row++)
513  {
514  for (col = 0; col < N; col++)
515  {
516  output[row*N + col] = temp[row*2*N + col + N ];
517  }
518  }
519 
520 
521  return true;
522 }
double baseFunc(const double x1, const double y1, const double x2, const double y2) const
std::vector< int > unused
int deletePoint(const double Px, const double Py)
bool getXy(int index, double &x, double &y) const
T max(T a, T b)
Definition: ossimCommon.h:236
std::vector< std::vector< double > > rhs
std::vector< std::vector< double > > coef
int addPoint(const double Px, const double Py, const double *Pvars)
T abs(const T &value)
Definition: ossimCommon.h:138
#define Ainv(r, c)
#define A(r, c)
std::vector< double > _AA
vizGeorefInterType type
std::vector< double > y
T min(T a, T b)
Definition: ossimCommon.h:203
int getPoint(const double Px, const double Py, double *Pvars) const
std::vector< int > index
bool changePoint(int index, double x, double y, double *Pvars)
return status
std::vector< double > _Ainv
#define max(a, b)
Definition: auxiliary.h:76
std::vector< double > u
std::vector< double > x
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)