• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 13800996410

12 Mar 2025 12:32AM UTC coverage: 70.443% (+0.02%) from 70.425%
13800996410

Pull #11949

github

web-flow
Merge 32cc61285 into dd6009a0e
Pull Request #11949: New Transform type: Homography

225 of 262 new or added lines in 2 files covered. (85.88%)

16076 existing lines in 30 files now uncovered.

553680 of 785998 relevant lines covered (70.44%)

222004.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.32
/alg/gdal_homography.cpp
1
/******************************************************************************
2
 *
3
 * Project:  Homography Transformer
4
 * Author:   Nathan Olson
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2025, Nathan Olson <nathanmolson at gmail dot com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11

12
#include "cpl_port.h"
13

14
#include <stdlib.h>
15
#include <string.h>
16

17
#include "cpl_atomic_ops.h"
18
#include "cpl_error.h"
19
#include "cpl_string.h"
20
#include "gdal.h"
21
#include "gdal_alg.h"
22
#include "gdallinearsystem.h"
23

24
CPL_C_START
25
CPLXMLNode *GDALSerializeHomographyTransformer(void *pTransformArg);
26
void *GDALDeserializeHomographyTransformer(CPLXMLNode *psTree);
27
CPL_C_END
28

29
struct HomographyTransformInfo
30
{
31
    GDALTransformerInfo sTI{};
32

33
    double padfForward[9]{};
34
    double padfReverse[9]{};
35

36
    volatile int nRefCount{};
37
};
38

39
/************************************************************************/
40
/*                   GDALCreateSimilarHomographyTransformer()                  */
41
/************************************************************************/
42

NEW
43
static void *GDALCreateSimilarHomographyTransformer(void *hTransformArg,
×
44
                                                    double dfRatioX,
45
                                                    double dfRatioY)
46
{
NEW
47
    VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarHomographyTransformer",
×
48
                      nullptr);
49

NEW
50
    HomographyTransformInfo *psInfo =
×
51
        static_cast<HomographyTransformInfo *>(hTransformArg);
52

NEW
53
    if (dfRatioX == 1.0 && dfRatioY == 1.0)
×
54
    {
55
        // We can just use a ref count, since using the source transformation
56
        // is thread-safe.
NEW
57
        CPLAtomicInc(&(psInfo->nRefCount));
×
58
    }
59
    else
60
    {
61
        double homography[9];
NEW
62
        for (int i = 0; i < 3; i++)
×
63
        {
NEW
64
            homography[3 * i + 1] = psInfo->padfForward[3 * i + 1] / dfRatioX;
×
NEW
65
            homography[3 * i + 2] = psInfo->padfForward[3 * i + 2] / dfRatioY;
×
NEW
66
            homography[3 * i] = psInfo->padfForward[3 * i];
×
67
        }
68
        psInfo = static_cast<HomographyTransformInfo *>(
NEW
69
            GDALCreateHomographyTransformer(homography));
×
70
    }
71

NEW
72
    return psInfo;
×
73
}
74

75
/************************************************************************/
76
/*                      GDALCreateHomographyTransformer()                      */
77
/************************************************************************/
78

79
/**
80
 * Create Homography transformer from GCPs.
81
 *
82
 * Homography Transformers are serializable.
83
 *
84
 * @param homography the forward homography.
85
 *
86
 * @return the transform argument or NULL if creation fails.
87
 */
88

89
void *GDALCreateHomographyTransformer(double adfHomography[9])
29✔
90
{
91
    /* -------------------------------------------------------------------- */
92
    /*      Allocate transform info.                                        */
93
    /* -------------------------------------------------------------------- */
94
    HomographyTransformInfo *psInfo = new HomographyTransformInfo();
29✔
95

96
    memcpy(psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
29✔
97
           strlen(GDAL_GTI2_SIGNATURE));
98
    psInfo->sTI.pszClassName = "GDALHomographyTransformer";
29✔
99
    psInfo->sTI.pfnTransform = GDALHomographyTransform;
29✔
100
    psInfo->sTI.pfnCleanup = GDALDestroyHomographyTransformer;
29✔
101
    psInfo->sTI.pfnSerialize = GDALSerializeHomographyTransformer;
29✔
102
    psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarHomographyTransformer;
29✔
103

104
    psInfo->nRefCount = 1;
29✔
105

106
    memcpy(psInfo->padfForward, adfHomography, 9 * sizeof(double));
29✔
107
    if (GDALInvHomography(psInfo->padfForward, psInfo->padfReverse))
29✔
108
    {
109
        return psInfo;
29✔
110
    }
111

NEW
112
    GDALDestroyHomographyTransformer(psInfo);
×
NEW
113
    return nullptr;
×
114
}
115

116
/************************************************************************/
117
/*                       GDALGCPsToHomography()                       */
118
/************************************************************************/
119

120
/**
121
 * \brief Generate Homography from GCPs.
122
 *
123
 * Given a set of GCPs perform least squares fit as a homography.
124
 *
125
 * A minimum of four GCPs are required to uniquely define a homography.
126
 * If there are less than four GCPs, GDALGCPsToGeoTransform() is used to
127
 * compute the transform.
128
 *
129
 * @param nGCPCount the number of GCPs being passed in.
130
 * @param pasGCPs the list of GCP structures.
131
 * @param padfHomography the nine double array in which the homography
132
 * will be returned.
133
 *
134
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
135
 * homography, or pathological geometry is detected
136
 */
137
int GDALGCPsToHomography(int nGCPCount, const GDAL_GCP *pasGCPList,
32✔
138
                         double *padfHomography)
139
{
140
    if (nGCPCount < 4)
32✔
141
    {
142
        padfHomography[6] = 1.0;
3✔
143
        padfHomography[7] = 0.0;
3✔
144
        padfHomography[8] = 0.0;
3✔
145
        return GDALGCPsToGeoTransform(nGCPCount, pasGCPList, padfHomography,
3✔
146
                                      FALSE);
3✔
147
    }
148

149
    /* -------------------------------------------------------------------- */
150
    /*      Compute source and destination ranges so we can normalize       */
151
    /*      the values to make the least squares computation more stable.   */
152
    /* -------------------------------------------------------------------- */
153
    double min_pixel = pasGCPList[0].dfGCPPixel;
29✔
154
    double max_pixel = pasGCPList[0].dfGCPPixel;
29✔
155
    double min_line = pasGCPList[0].dfGCPLine;
29✔
156
    double max_line = pasGCPList[0].dfGCPLine;
29✔
157
    double min_geox = pasGCPList[0].dfGCPX;
29✔
158
    double max_geox = pasGCPList[0].dfGCPX;
29✔
159
    double min_geoy = pasGCPList[0].dfGCPY;
29✔
160
    double max_geoy = pasGCPList[0].dfGCPY;
29✔
161

162
    for (int i = 1; i < nGCPCount; ++i)
118✔
163
    {
164
        min_pixel = std::min(min_pixel, pasGCPList[i].dfGCPPixel);
89✔
165
        max_pixel = std::max(max_pixel, pasGCPList[i].dfGCPPixel);
89✔
166
        min_line = std::min(min_line, pasGCPList[i].dfGCPLine);
89✔
167
        max_line = std::max(max_line, pasGCPList[i].dfGCPLine);
89✔
168
        min_geox = std::min(min_geox, pasGCPList[i].dfGCPX);
89✔
169
        max_geox = std::max(max_geox, pasGCPList[i].dfGCPX);
89✔
170
        min_geoy = std::min(min_geoy, pasGCPList[i].dfGCPY);
89✔
171
        max_geoy = std::max(max_geoy, pasGCPList[i].dfGCPY);
89✔
172
    }
173

174
    double EPS = 1.0e-12;
29✔
175

176
    if (std::abs(max_pixel - min_pixel) < EPS ||
58✔
177
        std::abs(max_line - min_line) < EPS ||
58✔
178
        std::abs(max_geox - min_geox) < EPS ||
87✔
179
        std::abs(max_geoy - min_geoy) < EPS)
29✔
180
    {
NEW
181
        return FALSE;  // degenerate in at least one dimension.
×
182
    }
183

184
    double pl_normalize[9], geo_normalize[9];
185

186
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
29✔
187
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
29✔
188
    pl_normalize[2] = 0.0;
29✔
189
    pl_normalize[3] = -min_line / (max_line - min_line);
29✔
190
    pl_normalize[4] = 0.0;
29✔
191
    pl_normalize[5] = 1.0 / (max_line - min_line);
29✔
192
    pl_normalize[6] = 1.0;
29✔
193
    pl_normalize[7] = 0.0;
29✔
194
    pl_normalize[8] = 0.0;
29✔
195

196
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
29✔
197
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
29✔
198
    geo_normalize[2] = 0.0;
29✔
199
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
29✔
200
    geo_normalize[4] = 0.0;
29✔
201
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
29✔
202
    geo_normalize[6] = 1.0;
29✔
203
    geo_normalize[7] = 0.0;
29✔
204
    geo_normalize[8] = 0.0;
29✔
205

206
    double inv_geo_normalize[9] = {0.0};
29✔
207
    if (!GDALInvHomography(geo_normalize, inv_geo_normalize))
29✔
208
    {
NEW
209
        return FALSE;
×
210
    }
211

212
    GDALMatrix LtL(9, 9);
58✔
213
    GDALMatrix rhs(9, 1);
58✔
214
    rhs(6, 0) = 1;
29✔
215
    LtL(6, 6) = 1;
29✔
216

217
    for (int i = 0; i < nGCPCount; ++i)
147✔
218
    {
219
        double pixel, line, geox, geoy;
220

221
        GDALApplyHomography(pl_normalize, pasGCPList[i].dfGCPPixel,
118✔
222
                            pasGCPList[i].dfGCPLine, &pixel, &line);
118✔
223
        GDALApplyHomography(geo_normalize, pasGCPList[i].dfGCPX,
118✔
224
                            pasGCPList[i].dfGCPY, &geox, &geoy);
118✔
225

226
        double Lx[] = {1, pixel, line,          0,           0,
118✔
227
                       0, -geox, -geox * pixel, -geox * line};
118✔
228
        double Ly[] = {0,           0, 0, 1, pixel, line, -geoy, -geoy * pixel,
118✔
229
                       -geoy * line};
118✔
230
        int j, k;
231
        // Populate the lower triangle of symmetric LtL matrix
232
        for (j = 0; j < 9; j++)
1,180✔
233
        {
234
            for (k = j; k < 9; k++)
6,372✔
235
            {
236
                LtL(j, k) += Lx[j] * Lx[k] + Ly[j] * Ly[k];
5,310✔
237
            }
238
        }
239
    }
240
    // Populate the upper triangle of symmetric LtL matrix
241
    for (int j = 0; j < 9; j++)
290✔
242
    {
243
        for (int k = 0; k < j; k++)
1,305✔
244
        {
245
            LtL(j, k) = LtL(k, j);
1,044✔
246
        }
247
    }
248

249
    GDALMatrix h_normalized(9, 1);
58✔
250
    if (!GDALLinearSystemSolve(LtL, rhs, h_normalized))
29✔
251
    {
NEW
252
        return FALSE;
×
253
    }
254

255
    // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
256
    double x[4] = {0, 1, 1, 0};
29✔
257
    double y[4] = {0, 0, 1, 1};
29✔
258
    for (int i = 0; i < 4; i++)
145✔
259
    {
260
        GDALApplyHomography(h_normalized.data(), x[i], y[i], &x[i], &y[i]);
116✔
261
    }
262
    for (int i = 3; i >= 0; i--)
145✔
263
    {
264
        x[i] -= x[0];
116✔
265
        y[i] -= y[0];
116✔
266
    }
267
    double cross12 = x[1] * y[2] - x[2] * y[1];
29✔
268
    double cross23 = x[2] * y[3] - x[3] * y[2];
29✔
269
    if (cross12 * cross23 <= 0.0)
29✔
270
    {
271
        return FALSE;
2✔
272
    }
273

274
    /* -------------------------------------------------------------------- */
275
    /*      Compose the resulting transformation with the normalization     */
276
    /*      homographies.                                             */
277
    /* -------------------------------------------------------------------- */
278
    double h1p2[9] = {0.0};
27✔
279

280
    GDALComposeHomographies(pl_normalize, h_normalized.data(), h1p2);
27✔
281
    GDALComposeHomographies(h1p2, inv_geo_normalize, padfHomography);
27✔
282

283
    return TRUE;
27✔
284
}
285

286
/************************************************************************/
287
/*                      GDALComposeHomographies()                      */
288
/************************************************************************/
289

290
/**
291
 * \brief Compose two homographies.
292
 *
293
 * The resulting homography is the equivalent to padfH1 and then padfH2
294
 * being applied to a point.
295
 *
296
 * @param padfH1 the first homography, nine values.
297
 * @param padfH2 the second homography, nine values.
298
 * @param padfHOut the output homography, nine values, may safely be the same
299
 * array as padfH1 or padfH2.
300
 */
301

302
void GDALComposeHomographies(const double *padfH1, const double *padfH2,
54✔
303
                             double *padfHOut)
304

305
{
306
    double hwrk[9] = {0.0};
54✔
307

308
    hwrk[1] =
54✔
309
        padfH2[1] * padfH1[1] + padfH2[2] * padfH1[4] + padfH2[0] * padfH1[7];
54✔
310
    hwrk[2] =
54✔
311
        padfH2[1] * padfH1[2] + padfH2[2] * padfH1[5] + padfH2[0] * padfH1[8];
54✔
312
    hwrk[0] =
54✔
313
        padfH2[1] * padfH1[0] + padfH2[2] * padfH1[3] + padfH2[0] * padfH1[6];
54✔
314

315
    hwrk[4] =
54✔
316
        padfH2[4] * padfH1[1] + padfH2[5] * padfH1[4] + padfH2[3] * padfH1[7];
54✔
317
    hwrk[5] =
54✔
318
        padfH2[4] * padfH1[2] + padfH2[5] * padfH1[5] + padfH2[3] * padfH1[8];
54✔
319
    hwrk[3] =
54✔
320
        padfH2[4] * padfH1[0] + padfH2[5] * padfH1[3] + padfH2[3] * padfH1[6];
54✔
321

322
    hwrk[7] =
54✔
323
        padfH2[7] * padfH1[1] + padfH2[8] * padfH1[4] + padfH2[6] * padfH1[7];
54✔
324
    hwrk[8] =
54✔
325
        padfH2[7] * padfH1[2] + padfH2[8] * padfH1[5] + padfH2[6] * padfH1[8];
54✔
326
    hwrk[6] =
54✔
327
        padfH2[7] * padfH1[0] + padfH2[8] * padfH1[3] + padfH2[6] * padfH1[6];
54✔
328
    memcpy(padfHOut, hwrk, sizeof(hwrk));
54✔
329
}
54✔
330

331
/************************************************************************/
332
/*                       GDALApplyHomography()                        */
333
/************************************************************************/
334

335
/**
336
 * Apply Homography to x/y coordinate.
337
 *
338
 * Applies the following computation, converting a (pixel, line) coordinate
339
 * into a georeferenced (geo_x, geo_y) location.
340
 * \code{.c}
341
 *  *pdfGeoX = (padfHomography[0] + dfPixel * padfHomography[1]
342
 *                                + dfLine  * padfHomography[2]) / 
343
 *             (padfHomography[6] + dfPixel * padfHomography[7]
344
 *                                + dfLine  * padfHomography[8]);
345
 *  *pdfGeoY = (padfHomography[3] + dfPixel * padfHomography[4]
346
 *                                + dfLine  * padfHomography[5]) / 
347
 *             (padfHomography[6] + dfPixel * padfHomography[7]
348
 *                                + dfLine  * padfHomography[8]);
349
 * \endcode
350
 *
351
 * @param padfGeoTransform Nine coefficient Homography to apply.
352
 * @param dfPixel Input pixel position.
353
 * @param dfLine Input line position.
354
 * @param pdfGeoX output location where geo_x (easting/longitude)
355
 * location is placed.
356
 * @param pdfGeoY output location where geo_y (northing/latitude)
357
 * location is placed.
358
 */
359

360
void CPL_STDCALL GDALApplyHomography(const double *padfHomography,
362✔
361
                                     double dfPixel, double dfLine,
362
                                     double *pdfGeoX, double *pdfGeoY)
363
{
364
    double wx = padfHomography[0] + dfPixel * padfHomography[1] +
362✔
365
                dfLine * padfHomography[2];
362✔
366
    double wy = padfHomography[3] + dfPixel * padfHomography[4] +
362✔
367
                dfLine * padfHomography[5];
362✔
368
    double w = padfHomography[6] + dfPixel * padfHomography[7] +
362✔
369
               dfLine * padfHomography[8];
362✔
370
    *pdfGeoX = wx / w;
362✔
371
    *pdfGeoY = wy / w;
362✔
372
}
362✔
373

374
/************************************************************************/
375
/*                        GDALInvHomography()                         */
376
/************************************************************************/
377

378
/**
379
* Invert Homography.
380
*
381
* This function will invert a standard 3x3 set of Homography coefficients.
382
* This converts the equation from being pixel to geo to being geo to pixel.
383
*
384
* @param padfHIn Input homography (nine doubles - unaltered).
385
* @param padfHOut Output homography (nine doubles - updated).
386
*
387
* @return TRUE on success or FALSE if the equation is uninvertable.
388
*/
389

390
int CPL_STDCALL GDALInvHomography(const double *padfHIn, double *padfHOut)
65✔
391

392
{
393
    // Special case - no rotation - to avoid computing determinate
394
    // and potential precision issues.
395
    if (padfHIn[2] == 0.0 && padfHIn[4] == 0.0 && padfHIn[1] != 0.0 &&
65✔
396
        padfHIn[5] != 0.0 && padfHIn[7] == 0.0 && padfHIn[8] == 0.0 &&
59✔
397
        padfHIn[6] != 0.0)
59✔
398
    {
399
        padfHOut[0] = -padfHIn[0] / padfHIn[1] / padfHIn[6];
59✔
400
        padfHOut[1] = 1.0 / padfHIn[1];
59✔
401
        padfHOut[2] = 0.0;
59✔
402
        padfHOut[3] = -padfHIn[3] / padfHIn[5] / padfHIn[6];
59✔
403
        padfHOut[4] = 0.0;
59✔
404
        padfHOut[5] = 1.0 / padfHIn[5];
59✔
405
        padfHOut[6] = 1.0 / padfHIn[6];
59✔
406
        padfHOut[7] = 0.0;
59✔
407
        padfHOut[8] = 0.0;
59✔
408
        return 1;
59✔
409
    }
410

411
    // Assume a 3rd row that is [1 0 0].
412

413
    // Compute determinate.
414

415
    const double det = padfHIn[1] * padfHIn[5] * padfHIn[6] -
6✔
416
                       padfHIn[2] * padfHIn[4] * padfHIn[6] +
6✔
417
                       padfHIn[2] * padfHIn[3] * padfHIn[7] -
6✔
418
                       padfHIn[0] * padfHIn[5] * padfHIn[7] +
6✔
419
                       padfHIn[0] * padfHIn[4] * padfHIn[8] -
6✔
420
                       padfHIn[1] * padfHIn[3] * padfHIn[8];
6✔
421
    const double magnitude =
422
        std::max(std::max(fabs(padfHIn[1]), fabs(padfHIn[2])),
12✔
423
                 std::max(fabs(padfHIn[4]), fabs(padfHIn[5])));
6✔
424

425
    if (fabs(det) <= 1e-10 * magnitude * magnitude)
6✔
426
        return 0;
3✔
427

428
    const double inv_det = 1.0 / det;
3✔
429

430
    // Compute adjoint, and divide by determinant.
431

432
    padfHOut[1] = (padfHIn[5] * padfHIn[6] - padfHIn[3] * padfHIn[8]) * inv_det;
3✔
433
    padfHOut[4] = (padfHIn[3] * padfHIn[7] - padfHIn[4] * padfHIn[6]) * inv_det;
3✔
434
    padfHOut[7] = (padfHIn[4] * padfHIn[8] - padfHIn[5] * padfHIn[7]) * inv_det;
3✔
435

436
    padfHOut[2] = (padfHIn[0] * padfHIn[8] - padfHIn[2] * padfHIn[6]) * inv_det;
3✔
437
    padfHOut[5] = (padfHIn[1] * padfHIn[6] - padfHIn[0] * padfHIn[7]) * inv_det;
3✔
438
    padfHOut[8] = (padfHIn[2] * padfHIn[7] - padfHIn[1] * padfHIn[8]) * inv_det;
3✔
439

440
    padfHOut[0] = (padfHIn[2] * padfHIn[3] - padfHIn[0] * padfHIn[5]) * inv_det;
3✔
441
    padfHOut[3] = (padfHIn[0] * padfHIn[4] - padfHIn[1] * padfHIn[3]) * inv_det;
3✔
442
    padfHOut[6] = (padfHIn[1] * padfHIn[5] - padfHIn[2] * padfHIn[4]) * inv_det;
3✔
443

444
    return 1;
3✔
445
}
446

447
/************************************************************************/
448
/*                      GDALCreateHomographyTransformerFromGCPs()                      */
449
/************************************************************************/
450

451
/**
452
 * Create Homography transformer from GCPs.
453
 *
454
 * Homography Transformers are serializable.
455
 *
456
 * @param nGCPCount the number of GCPs in pasGCPList.
457
 * @param pasGCPList an array of GCPs to be used as input.
458
 *
459
 * @return the transform argument or NULL if creation fails.
460
 */
461

462
void *GDALCreateHomographyTransformerFromGCPs(int nGCPCount,
22✔
463
                                              const GDAL_GCP *pasGCPList)
464
{
465
    double adfHomography[9];
466

467
    if (GDALGCPsToHomography(nGCPCount, pasGCPList, adfHomography))
22✔
468
    {
469
        return GDALCreateHomographyTransformer(adfHomography);
22✔
470
    }
NEW
471
    return nullptr;
×
472
}
473

474
/************************************************************************/
475
/*                     GDALDestroyHomographyTransformer()                      */
476
/************************************************************************/
477

478
/**
479
 * Destroy Homography transformer.
480
 *
481
 * This function is used to destroy information about a homography
482
 * transformation created with GDALCreateHomographyTransformer().
483
 *
484
 * @param pTransformArg the transform arg previously returned by
485
 * GDALCreateHomographyTransformer().
486
 */
487

488
void GDALDestroyHomographyTransformer(void *pTransformArg)
29✔
489

490
{
491
    if (pTransformArg == nullptr)
29✔
NEW
492
        return;
×
493

494
    HomographyTransformInfo *psInfo =
29✔
495
        static_cast<HomographyTransformInfo *>(pTransformArg);
496

497
    if (CPLAtomicDec(&(psInfo->nRefCount)) == 0)
29✔
498
    {
499
        delete psInfo;
29✔
500
    }
501
}
502

503
/************************************************************************/
504
/*                          GDALHomographyTransform()                          */
505
/************************************************************************/
506

507
/**
508
 * Transforms point based on homography.
509
 *
510
 * This function matches the GDALTransformerFunc signature, and can be
511
 * used to transform one or more points from pixel/line coordinates to
512
 * georeferenced coordinates (SrcToDst) or vice versa (DstToSrc).
513
 *
514
 * @param pTransformArg return value from GDALCreateHomographyTransformer().
515
 * @param bDstToSrc TRUE if transformation is from the destination
516
 * (georeferenced) coordinates to pixel/line or FALSE when transforming
517
 * from pixel/line to georeferenced coordinates.
518
 * @param nPointCount the number of values in the x, y and z arrays.
519
 * @param x array containing the X values to be transformed.
520
 * @param y array containing the Y values to be transformed.
521
 * @param z array containing the Z values to be transformed.
522
 * @param panSuccess array in which a flag indicating success (TRUE) or
523
 * failure (FALSE) of the transformation are placed.
524
 *
525
 * @return TRUE if all points have been successfully transformed.
526
 */
527

528
int GDALHomographyTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
17,543✔
529
                            double *x, double *y, CPL_UNUSED double *z,
530
                            int *panSuccess)
531
{
532
    VALIDATE_POINTER1(pTransformArg, "GDALHomographyTransform", 0);
17,543✔
533

534
    HomographyTransformInfo *psInfo =
17,543✔
535
        static_cast<HomographyTransformInfo *>(pTransformArg);
536

537
    double *homography = bDstToSrc ? psInfo->padfReverse : psInfo->padfForward;
17,543✔
538
    for (int i = 0; i < nPointCount; i++)
49,853✔
539
    {
540
        double wx = homography[0] + x[i] * homography[1] + y[i] * homography[2];
32,310✔
541
        double wy = homography[3] + x[i] * homography[4] + y[i] * homography[5];
32,310✔
542
        double w = homography[6] + x[i] * homography[7] + y[i] * homography[8];
32,310✔
543
        x[i] = wx / w;
32,310✔
544
        y[i] = wy / w;
32,310✔
545
        panSuccess[i] = TRUE;
32,310✔
546
    }
547

548
    return TRUE;
17,543✔
549
}
550

551
/************************************************************************/
552
/*                    GDALSerializeHomographyTransformer()                     */
553
/************************************************************************/
554

555
CPLXMLNode *GDALSerializeHomographyTransformer(void *pTransformArg)
8✔
556

557
{
558
    VALIDATE_POINTER1(pTransformArg, "GDALSerializeHomographyTransformer",
8✔
559
                      nullptr);
560

561
    HomographyTransformInfo *psInfo =
8✔
562
        static_cast<HomographyTransformInfo *>(pTransformArg);
563

564
    CPLXMLNode *psTree =
565
        CPLCreateXMLNode(nullptr, CXT_Element, "HomographyTransformer");
8✔
566

567
    /* -------------------------------------------------------------------- */
568
    /*      Attach Homography.                                                */
569
    /* -------------------------------------------------------------------- */
570
    char szWork[300] = {};
8✔
571

572
    CPLsnprintf(
8✔
573
        szWork, sizeof(szWork),
574
        "%.17g,%.17g,%.17g,%.17g,%.17g,%.17g,%.17g,%.17g,%.17g",
575
        psInfo->padfForward[0], psInfo->padfForward[1], psInfo->padfForward[2],
576
        psInfo->padfForward[3], psInfo->padfForward[4], psInfo->padfForward[5],
577
        psInfo->padfForward[6], psInfo->padfForward[7], psInfo->padfForward[8]);
578
    CPLCreateXMLElementAndValue(psTree, "Homography", szWork);
8✔
579

580
    return psTree;
8✔
581
}
582

583
/************************************************************************/
584
/*                   GDALDeserializeHomography()                    */
585
/************************************************************************/
586

587
static void GDALDeserializeHomography(const char *pszH, double adfHomography[9])
7✔
588
{
589
    CPLsscanf(pszH, "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", adfHomography + 0,
7✔
590
              adfHomography + 1, adfHomography + 2, adfHomography + 3,
591
              adfHomography + 4, adfHomography + 5, adfHomography + 6,
592
              adfHomography + 7, adfHomography + 8);
593
}
7✔
594

595
/************************************************************************/
596
/*                   GDALDeserializeHomographyTransformer()                    */
597
/************************************************************************/
598

599
void *GDALDeserializeHomographyTransformer(CPLXMLNode *psTree)
7✔
600

601
{
602
    /* -------------------------------------------------------------------- */
603
    /*      Homography                                                */
604
    /* -------------------------------------------------------------------- */
605
    double padfForward[9];
606
    if (CPLGetXMLNode(psTree, "Homography") != nullptr)
7✔
607
    {
608
        GDALDeserializeHomography(CPLGetXMLValue(psTree, "Homography", ""),
7✔
609
                                  padfForward);
610

611
        /* -------------------------------------------------------------------- */
612
        /*      Generate transformation.                                        */
613
        /* -------------------------------------------------------------------- */
614
        void *pResult = GDALCreateHomographyTransformer(padfForward);
7✔
615

616
        return pResult;
7✔
617
    }
NEW
618
    return nullptr;
×
619
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc