GRASS 8 Programmer's Manual 8.6.0dev(2026)-55de52a352
Loading...
Searching...
No Matches
georef_tps.c
Go to the documentation of this file.
1/****************************************************************************
2 *
3 * MODULE: imagery library
4 * AUTHOR(S): Markus Metz
5 *
6 * PURPOSE: Image processing library
7 * SPDX-FileCopyrightText: 2013 GRASS Development Team
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 *
10 *****************************************************************************/
11
12#include <stdlib.h>
13#include <math.h>
14#include <grass/gis.h>
15#include <grass/imagery.h>
16#include <grass/glocale.h>
17#include <signal.h>
18
19/* STRUCTURE FOR USE INTERNALLY WITH THESE FUNCTIONS. THESE FUNCTIONS EXPECT
20 SQUARE MATRICES SO ONLY ONE VARIABLE IS GIVEN (N) FOR THE MATRIX SIZE */
21
22struct MATRIX {
23 int n; /* SIZE OF THIS MATRIX (N x N) */
24 double *v;
25};
26
27/* CALCULATE OFFSET INTO ARRAY BASED ON R/C */
28
29#define M(row, col) m->v[(((row) - 1) * (m->n)) + (col) - 1]
30
31#define MSUCCESS 1 /* SUCCESS */
32#define MNPTERR 0 /* NOT ENOUGH POINTS */
33#define MUNSOLVABLE -1 /* NOT SOLVABLE */
34#define MMEMERR -2 /* NOT ENOUGH MEMORY */
35#define MPARMERR -3 /* PARAMETER ERROR */
36#define MINTERR -4 /* INTERNAL ERROR */
37
38#define MAXORDER 3 /* HIGHEST SUPPORTED ORDER OF TRANSFORMATION */
39
40/***********************************************************************
41
42 FUNCTION PROTOTYPES FOR STATIC (INTERNAL) FUNCTIONS
43
44************************************************************************/
45
46static int calccoef(struct Control_Points *, double **, double **);
47static int calcls(struct Control_Points *, struct MATRIX *, double *, double *,
48 double *, double *);
49
50static double tps_base_func(const double x1, const double y1, const double x2,
51 const double y2);
52static int solvemat(struct MATRIX *, double *, double *, double *, double *);
53
54/***********************************************************************
55
56 TRANSFORM A SINGLE COORDINATE PAIR.
57
58************************************************************************/
59
60int I_georef_tps(double e1, /* EASTING TO BE TRANSFORMED */
61 double n1, /* NORTHING TO BE TRANSFORMED */
62 double *e, /* EASTING, TRANSFORMED */
63 double *n, /* NORTHING, TRANSFORMED */
64 double *E, /* EASTING COEFFICIENTS */
65 double *N, /* NORTHING COEFFICIENTS */
66 struct Control_Points *cp, int fwd)
67{
68 int i, j;
69 double dist, *pe, *pn;
70
71 if (fwd) {
72 pe = cp->e1;
73 pn = cp->n1;
74 }
75 else {
76 pe = cp->e2;
77 pn = cp->n2;
78 }
79
80 /* global affine (1st order poly) */
81 *e = E[0] + e1 * E[1] + n1 * E[2];
82 *n = N[0] + e1 * N[1] + n1 * N[2];
83
84 for (i = 0, j = 0; i < cp->count; i++) {
85 if (cp->status[i] > 0) {
86
87 dist = tps_base_func(e1, n1, pe[i], pn[i]);
88
89 *e += E[j + 3] * dist;
90 *n += N[j + 3] * dist;
91 j++;
92 }
93 }
94
95 return MSUCCESS;
96}
97
98/***********************************************************************
99
100 COMPUTE THE FORWARD AND BACKWARD GEOREFFERENCING COEFFICIENTS
101 BASED ON A SET OF CONTROL POINTS
102
103************************************************************************/
104
106 double **N12tps, double **E21tps,
107 double **N21tps)
108{
109 double *tempptr;
110 int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */
111 int status, i;
112 double xmax, xmin, ymax, ymin;
113 double delx, dely;
114 double xx, yy;
115 double sumx, sumy, sumx2, sumy2, sumxy;
116 double SSxx, SSyy, SSxy;
117
118 /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */
119
120 for (i = numactive = 0; i < cp->count; i++) {
121 if (cp->status[i] > 0)
122 numactive++;
123 }
124
125 if (numactive < 3)
126 return MNPTERR;
127
128 if (numactive > 100000) /* arbitrary, admittedly */
129 return MNPTERR;
130
131 xmin = xmax = cp->e1[0];
132 ymin = ymax = cp->n1[0];
133
134 sumx = sumy = sumx2 = sumy2 = sumxy = 0.0;
135
136 for (i = 0; i < cp->count; i++) {
137 if (cp->status[i] > 0) {
138 xx = cp->e1[i];
139 yy = cp->n1[i];
140
141 xmax = MAX(xmax, xx);
142 xmin = MIN(xmin, xx);
143 ymax = MAX(ymax, yy);
144 ymin = MIN(ymin, yy);
145
146 sumx += xx;
147 sumx2 += xx * xx;
148 sumy += yy;
149 sumy2 += yy * yy;
150 sumxy += xx * yy;
151 }
152 }
153
154 delx = xmax - xmin;
155 dely = ymax - ymin;
156
157 SSxx = sumx2 - sumx * sumx / numactive;
158 SSyy = sumy2 - sumy * sumy / numactive;
159 SSxy = sumxy - sumx * sumy / numactive;
160
161 if (delx < 0.001 * dely || dely < 0.001 * delx ||
162 fabs(SSxy * SSxy / (SSxx * SSyy)) > 0.99) {
163 /* points are colinear */
164 return MUNSOLVABLE;
165 }
166
167 xmin = xmax = cp->e2[0];
168 ymin = ymax = cp->n2[0];
169
170 sumx = sumy = sumx2 = sumy2 = sumxy = 0.0;
171 for (i = 0; i < cp->count; i++) {
172 if (cp->status[i] > 0) {
173 xx = cp->e2[i];
174 yy = cp->n2[i];
175
176 xmax = MAX(xmax, xx);
177 xmin = MIN(xmin, xx);
178 ymax = MAX(ymax, yy);
179 ymin = MIN(ymin, yy);
180
181 sumx += xx;
182 sumx2 += xx * xx;
183 sumy += yy;
184 sumy2 += yy * yy;
185 sumxy += xx * yy;
186 }
187 }
188
189 delx = xmax - xmin;
190 dely = ymax - ymin;
191
192 SSxx = sumx2 - sumx * sumx / numactive;
193 SSyy = sumy2 - sumy * sumy / numactive;
194 SSxy = sumxy - sumx * sumy / numactive;
195
196 if (delx < 0.001 * dely || dely < 0.001 * delx ||
197 fabs(SSxy * SSxy / (SSxx * SSyy)) > 0.99) {
198 /* points are colinear */
199 return MUNSOLVABLE;
200 }
201
202 /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */
203
204 G_message(_("Calculating forward transformation coefficients"));
205 status = calccoef(cp, E12tps, N12tps);
206
207 if (status != MSUCCESS)
208 return status;
209
210 /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS */
211
212 tempptr = cp->e1;
213 cp->e1 = cp->e2;
214 cp->e2 = tempptr;
215 tempptr = cp->n1;
216 cp->n1 = cp->n2;
217 cp->n2 = tempptr;
218
219 /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */
220
221 G_message(_("Calculating backward transformation coefficients"));
222 status = calccoef(cp, E21tps, N21tps);
223
224 /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS BACK */
225
226 tempptr = cp->e1;
227 cp->e1 = cp->e2;
228 cp->e2 = tempptr;
229 tempptr = cp->n1;
230 cp->n1 = cp->n2;
231 cp->n2 = tempptr;
232
233 return status;
234}
235
236/***********************************************************************
237
238 COMPUTE THE GEOREFFERENCING COEFFICIENTS
239 BASED ON A SET OF CONTROL POINTS
240
241************************************************************************/
242
243static int calccoef(struct Control_Points *cp, double **E, double **N)
244{
245 struct MATRIX m;
246 double *a;
247 double *b;
248 int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */
249 int status, i;
250
251 /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */
252
253 for (i = numactive = 0; i < cp->count; i++) {
254 if (cp->status[i] > 0)
255 numactive++;
256 }
257
258 /* INITIALIZE MATRIX */
259
260 m.n = numactive + 3;
261
262 m.v = G_calloc(m.n * m.n, sizeof(double));
263 if (m.v == NULL)
264 G_fatal_error(_("%s: out of memory"),
265 "I_compute_georef_equations_tps()");
266 a = G_calloc(m.n, sizeof(double));
267 if (a == NULL)
268 G_fatal_error(_("%s: out of memory"),
269 "I_compute_georef_equations_tps()");
270 b = G_calloc(m.n, sizeof(double));
271 if (b == NULL)
272 G_fatal_error(_("%s: out of memory"),
273 "I_compute_georef_equations_tps()");
274
275 /* equation coefficients */
276 *E = G_calloc(m.n, sizeof(double));
277 if (*E == NULL)
278 G_fatal_error(_("%s: out of memory"),
279 "I_compute_georef_equations_tps()");
280 *N = G_calloc(m.n, sizeof(double));
281 if (*N == NULL)
282 G_fatal_error(_("%s: out of memory"),
283 "I_compute_georef_equations_tps()");
284
285 status = calcls(cp, &m, a, b, *E, *N);
286
287 G_free(m.v);
288 G_free(a);
289 G_free(b);
290
291 return status;
292}
293
294/***********************************************************************
295
296 CALCULATE THE TRANSFORMATION COEFFICIENTS FOR THIN PLATE SPLINE
297 INTERPOLATION.
298 THIS ROUTINE USES THE LEAST SQUARES METHOD TO COMPUTE THE COEFFICIENTS.
299
300************************************************************************/
301
302static int calcls(struct Control_Points *cp, struct MATRIX *m, double a[],
303 double b[], double E[], /* EASTING COEFFICIENTS */
304 double N[] /* NORTHING COEFFICIENTS */
305)
306{
307 int i, j, n, o, numactive = 0;
308
309 /* double dist = 0.0, dx, dy, regularization; */
310
311 /* INITIALIZE THE MATRIX AND THE TWO COLUMN VECTORS */
312
313 for (i = 1; i <= m->n; i++) {
314 for (j = i; j <= m->n; j++) {
315 M(i, j) = 0.0;
316 if (i != j)
317 M(j, i) = 0.0;
318 }
319 a[i - 1] = b[i - 1] = 0.0;
320 }
321
322 /* SUM THE UPPER HALF OF THE MATRIX AND THE COLUMN VECTORS ACCORDING TO
323 THE LEAST SQUARES METHOD OF SOLVING OVER DETERMINED SYSTEMS */
324
325 for (n = 0; n < cp->count; n++) {
326 if (cp->status[n] > 0) {
327
328 a[numactive + 3] = cp->e2[n];
329 b[numactive + 3] = cp->n2[n];
330
331 numactive++;
332 M(1, numactive + 3) = 1.0;
333 M(2, numactive + 3) = cp->e1[n];
334 M(3, numactive + 3) = cp->n1[n];
335
336 M(numactive + 3, 1) = 1.0;
337 M(numactive + 3, 2) = cp->e1[n];
338 M(numactive + 3, 3) = cp->n1[n];
339 }
340 }
341
342 if (numactive < m->n - 3)
343 return MINTERR;
344
345 i = 0;
346 for (n = 0; n < cp->count; n++) {
347 if (cp->status[n] > 0) {
348 i++;
349
350 j = 0;
351 for (o = 0; o <= n; o++) {
352 if (cp->status[o] > 0) {
353 j++;
354 M(i + 3, j + 3) = tps_base_func(cp->e1[n], cp->n1[n],
355 cp->e1[o], cp->n1[o]);
356
357 if (i != j)
358 M(j + 3, i + 3) = M(i + 3, j + 3);
359
360 /* dx = cp->e1[n] - cp->e1[o];
361 dy = cp->n1[n] - cp->n1[o];
362 dist += sqrt(dx * dx + dy * dy); */
363 }
364 }
365 }
366 }
367
368 /* regularization */
369 /* dist /= (numactive * numactive);
370 regularization = 0.01 * dist * dist; */
371
372 /* set diagonal to regularization, but not the first 3x3 (global affine) */
373
374 return solvemat(m, a, b, E, N);
375}
376
377/***********************************************************************
378
379 SOLVE FOR THE 'E' AND 'N' COEFFICIENTS BY USING A SOMEWHAT MODIFIED
380 GAUSSIAN ELIMINATION METHOD.
381
382 | M11 M12 ... M1n | | E0 | | a0 |
383 | M21 M22 ... M2n | | E1 | = | a1 |
384 | . . . . | | . | | . |
385 | Mn1 Mn2 ... Mnn | | En-1 | | an-1 |
386
387 and
388
389 | M11 M12 ... M1n | | N0 | | b0 |
390 | M21 M22 ... M2n | | N1 | = | b1 |
391 | . . . . | | . | | . |
392 | Mn1 Mn2 ... Mnn | | Nn-1 | | bn-1 |
393
394************************************************************************/
395
396static int solvemat(struct MATRIX *m, double a[], double b[], double E[],
397 double N[])
398{
399 int i, j, i2, j2, imark;
400 double factor, temp;
401 double pivot; /* ACTUAL VALUE OF THE LARGEST PIVOT CANDIDATE */
402
403 for (i = 1; i <= m->n; i++) {
404 G_percent(i - 1, m->n, 4);
405 j = i;
406
407 /* find row with largest magnitude value for pivot value */
408
409 pivot = M(i, j);
410 imark = i;
411 for (i2 = i + 1; i2 <= m->n; i2++) {
412 temp = fabs(M(i2, j));
413 if (temp > fabs(pivot)) {
414 pivot = M(i2, j);
415 imark = i2;
416 }
417 }
418
419 /* if the pivot is very small then the points are nearly co-linear */
420 /* co-linear points result in an undefined matrix, and nearly */
421 /* co-linear points results in a solution with rounding error */
422
423 if (pivot == 0.0)
424 return MUNSOLVABLE;
425
426 /* if row with highest pivot is not the current row, switch them */
427
428 if (imark != i) {
429 for (j2 = 1; j2 <= m->n; j2++) {
430 temp = M(imark, j2);
431 M(imark, j2) = M(i, j2);
432 M(i, j2) = temp;
433 }
434
435 temp = a[imark - 1];
436 a[imark - 1] = a[i - 1];
437 a[i - 1] = temp;
438
439 temp = b[imark - 1];
440 b[imark - 1] = b[i - 1];
441 b[i - 1] = temp;
442 }
443
444 /* compute zeros above and below the pivot, and compute
445 values for the rest of the row as well */
446
447 for (i2 = 1; i2 <= m->n; i2++) {
448 if (i2 != i) {
449 factor = M(i2, j) / pivot;
450 for (j2 = j; j2 <= m->n; j2++)
451 M(i2, j2) -= factor * M(i, j2);
452 a[i2 - 1] -= factor * a[i - 1];
453 b[i2 - 1] -= factor * b[i - 1];
454 }
455 }
456 }
457 G_percent(1, 1, 1);
458
459 /* SINCE ALL OTHER VALUES IN THE MATRIX ARE ZERO NOW, CALCULATE THE
460 COEFFICIENTS BY DIVIDING THE COLUMN VECTORS BY THE DIAGONAL VALUES. */
461
462 for (i = 1; i <= m->n; i++) {
463 E[i - 1] = a[i - 1] / M(i, i);
464 N[i - 1] = b[i - 1] / M(i, i);
465 }
466
467 return MSUCCESS;
468}
469
470static double tps_base_func(const double x1, const double y1, const double x2,
471 const double y2)
472{
473 /* official: r * r * log(r) */
474 double dist;
475
476 if ((x1 == x2) && (y1 == y2))
477 return 0.0;
478
479 dist = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
480
481 return dist * log(dist) * 0.5;
482}
#define NULL
Definition ccmath.h:32
void G_percent(long, long, int)
Print percent complete messages.
Definition percent.c:59
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
#define G_calloc(m, n)
Definition defs/gis.h:137
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_message(const char *,...) __attribute__((format(printf
#define N
int I_georef_tps(double e1, double n1, double *e, double *n, double *E, double *N, struct Control_Points *cp, int fwd)
Definition georef_tps.c:60
#define MNPTERR
Definition georef_tps.c:32
#define MINTERR
Definition georef_tps.c:36
int I_compute_georef_equations_tps(struct Control_Points *cp, double **E12tps, double **N12tps, double **E21tps, double **N21tps)
Definition georef_tps.c:105
#define MSUCCESS
Definition georef_tps.c:31
#define MUNSOLVABLE
Definition georef_tps.c:33
#define M(row, col)
Definition georef_tps.c:29
#define MIN(a, b)
Definition gis.h:150
#define MAX(a, b)
Definition gis.h:145
#define _(str)
Definition glocale.h:10
double b
Definition r_raster.c:37