Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_raster_compare.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal "raster compare" subcommand
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_raster_compare.h"
14
15
#include "cpl_conv.h"
16
#include "gdal_alg.h"
17
#include "gdal_priv.h"
18
19
#include <algorithm>
20
#include <cmath>
21
#include <limits>
22
#include <type_traits>
23
24
#if defined(__x86_64__) || defined(_M_X64)
25
#define USE_SSE2
26
#include <emmintrin.h>
27
#elif defined(USE_NEON_OPTIMIZATIONS)
28
#define USE_SSE2
29
#include "include_sse2neon.h"
30
#endif
31
32
//! @cond Doxygen_Suppress
33
34
#ifndef _
35
0
#define _(x) (x)
36
#endif
37
38
/************************************************************************/
39
/*       GDALRasterCompareAlgorithm::GDALRasterCompareAlgorithm()       */
40
/************************************************************************/
41
42
GDALRasterCompareAlgorithm::GDALRasterCompareAlgorithm(bool standaloneStep)
43
0
    : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
44
0
                                      ConstructorOptions()
45
0
                                          .SetStandaloneStep(standaloneStep)
46
0
                                          .SetInputDatasetMaxCount(1)
47
0
                                          .SetAddDefaultArguments(false))
48
0
{
49
0
    if (standaloneStep)
50
0
    {
51
0
        AddProgressArg();
52
0
    }
53
0
    else
54
0
    {
55
0
        AddRasterHiddenInputDatasetArg();
56
0
    }
57
58
0
    auto &referenceDatasetArg = AddArg("reference", 0, _("Reference dataset"),
59
0
                                       &m_referenceDataset, GDAL_OF_RASTER)
60
0
                                    .SetPositional()
61
0
                                    .SetRequired();
62
63
0
    SetAutoCompleteFunctionForFilename(referenceDatasetArg, GDAL_OF_RASTER);
64
65
0
    if (standaloneStep)
66
0
    {
67
0
        AddRasterInputArgs(/* openForMixedRasterVector = */ false,
68
0
                           /* hiddenForCLI = */ false);
69
0
    }
70
71
0
    AddArg("metric", 0, _("Comparison metric(s)"), &m_metrics)
72
0
        .SetChoices(METRIC_ALL, METRIC_NONE, METRIC_DIFF, METRIC_RMSD,
73
0
                    METRIC_PSNR)
74
0
        .SetDefault(METRIC_DEFAULT);
75
76
0
    AddArg("skip-all-optional", 0, _("Skip all optional comparisons"),
77
0
           &m_skipAllOptional);
78
0
    AddArg("skip-binary", 0, _("Skip binary file comparison"), &m_skipBinary);
79
0
    AddArg("skip-crs", 0, _("Skip CRS comparison"), &m_skipCRS);
80
0
    AddArg("skip-geotransform", 0, _("Skip geotransform comparison"),
81
0
           &m_skipGeotransform);
82
0
    AddArg("skip-overview", 0, _("Skip overview comparison"), &m_skipOverview);
83
0
    AddArg("skip-metadata", 0, _("Skip metadata comparison"), &m_skipMetadata);
84
0
    AddArg("skip-rpc", 0, _("Skip RPC metadata comparison"), &m_skipRPC);
85
0
    AddArg("skip-geolocation", 0, _("Skip Geolocation metadata comparison"),
86
0
           &m_skipGeolocation);
87
0
    AddArg("skip-subdataset", 0, _("Skip subdataset comparison"),
88
0
           &m_skipSubdataset);
89
90
0
    AddOutputStringArg(&m_output);
91
92
0
    AddArg("return-code", 0, _("Return code"), &m_retCode)
93
0
        .SetHiddenForCLI()
94
0
        .SetIsInput(false)
95
0
        .SetIsOutput(true);
96
0
}
97
98
/************************************************************************/
99
/*             GDALRasterCompareAlgorithm::CRSComparison()              */
100
/************************************************************************/
101
102
void GDALRasterCompareAlgorithm::CRSComparison(
103
    std::vector<std::string> &aosReport, GDALDataset *poRefDS,
104
    GDALDataset *poInputDS)
105
0
{
106
0
    const auto poRefCRS = poRefDS->GetSpatialRef();
107
0
    const auto poInputCRS = poInputDS->GetSpatialRef();
108
109
0
    if (poRefCRS == nullptr)
110
0
    {
111
0
        if (poInputCRS)
112
0
        {
113
0
            aosReport.push_back(
114
0
                "Reference dataset has no CRS, but input dataset has one.");
115
0
        }
116
0
        return;
117
0
    }
118
119
0
    if (poInputCRS == nullptr)
120
0
    {
121
0
        aosReport.push_back(
122
0
            "Reference dataset has a CRS, but input dataset has none.");
123
0
        return;
124
0
    }
125
126
0
    if (poRefCRS->IsSame(poInputCRS))
127
0
        return;
128
129
0
    const char *apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
130
0
    const auto poRefWKT = poRefCRS->exportToWkt(apszOptions);
131
0
    const auto poInputWKT = poInputCRS->exportToWkt(apszOptions);
132
0
    aosReport.push_back(
133
0
        "Reference and input CRS are not equivalent. Reference one is '" +
134
0
        poRefWKT + "'. Input one is '" + poInputWKT + "'");
135
0
}
136
137
/************************************************************************/
138
/*         GDALRasterCompareAlgorithm::GeotransformComparison()         */
139
/************************************************************************/
140
141
void GDALRasterCompareAlgorithm::GeoTransformComparison(
142
    std::vector<std::string> &aosReport, GDALDataset *poRefDS,
143
    GDALDataset *poInputDS)
144
0
{
145
0
    GDALGeoTransform refGT;
146
0
    CPLErr eErr1 = poRefDS->GetGeoTransform(refGT);
147
0
    GDALGeoTransform inputGT;
148
0
    CPLErr eErr2 = poInputDS->GetGeoTransform(inputGT);
149
0
    if (eErr1 == CE_Failure && eErr2 == CE_Failure)
150
0
        return;
151
152
0
    if (eErr1 == CE_Failure && eErr2 == CE_None)
153
0
    {
154
0
        aosReport.push_back(
155
0
            "Reference dataset has no geotransform, but input one has one.");
156
0
        return;
157
0
    }
158
159
0
    if (eErr1 == CE_None && eErr2 == CE_Failure)
160
0
    {
161
0
        aosReport.push_back(
162
0
            "Reference dataset has a geotransform, but input one has none.");
163
0
        return;
164
0
    }
165
166
0
    for (int i = 0; i < 6; ++i)
167
0
    {
168
0
        if ((refGT[i] != 0 &&
169
0
             std::fabs(refGT[i] - inputGT[i]) > 1e-10 * std::fabs(refGT[i])) ||
170
0
            (refGT[i] == 0 && std::fabs(refGT[i] - inputGT[i]) > 1e-10))
171
0
        {
172
0
            std::string s = "Geotransform of reference and input dataset are "
173
0
                            "not equivalent. Reference geotransform is (";
174
0
            for (int j = 0; j < 6; ++j)
175
0
            {
176
0
                if (j > 0)
177
0
                    s += ',';
178
0
                s += std::to_string(refGT[j]);
179
0
            }
180
0
            s += "). Input geotransform is (";
181
0
            for (int j = 0; j < 6; ++j)
182
0
            {
183
0
                if (j > 0)
184
0
                    s += ',';
185
0
                s += std::to_string(inputGT[j]);
186
0
            }
187
0
            s += ')';
188
0
            aosReport.push_back(std::move(s));
189
0
            return;
190
0
        }
191
0
    }
192
0
}
193
194
#if defined(__GNUC__) && !defined(__clang__)
195
#pragma GCC push_options
196
#pragma GCC optimize("O3")
197
#endif
198
199
/************************************************************************/
200
/*                                Diff()                                */
201
/************************************************************************/
202
203
template <class T> CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static T Diff(T a, T b)
204
0
{
205
0
    return a - b;
206
0
}
Unexecuted instantiation: gdalalg_raster_compare.cpp:unsigned char Diff<unsigned char>(unsigned char, unsigned char)
Unexecuted instantiation: gdalalg_raster_compare.cpp:unsigned short Diff<unsigned short>(unsigned short, unsigned short)
Unexecuted instantiation: gdalalg_raster_compare.cpp:unsigned int Diff<unsigned int>(unsigned int, unsigned int)
Unexecuted instantiation: gdalalg_raster_compare.cpp:unsigned long Diff<unsigned long>(unsigned long, unsigned long)
Unexecuted instantiation: gdalalg_raster_compare.cpp:float Diff<float>(float, float)
Unexecuted instantiation: gdalalg_raster_compare.cpp:double Diff<double>(double, double)
207
208
/************************************************************************/
209
/*                           CompareVectors()                           */
210
/************************************************************************/
211
212
template <class T, class Tdiff, bool bIsComplex>
213
static void CompareVectors(size_t nValCount, const T *refValues,
214
                           const T *inputValues, uint64_t &countDiffPixels,
215
                           Tdiff &maxDiffValue)
216
0
{
217
0
    constexpr bool bIsFloatingPoint = std::is_floating_point_v<T>;
218
    if constexpr (bIsComplex)
219
0
    {
220
0
        for (size_t i = 0; i < nValCount; ++i)
221
0
        {
222
            if constexpr (bIsFloatingPoint)
223
0
            {
224
0
                static_assert(std::is_same_v<T, Tdiff>);
225
0
                if (std::isnan(refValues[2 * i]) &&
226
0
                    std::isnan(inputValues[2 * i]) &&
227
0
                    std::isnan(refValues[2 * i + 1]) &&
228
0
                    std::isnan(inputValues[2 * i + 1]))
229
0
                {
230
0
                    continue;
231
0
                }
232
0
            }
233
234
0
            if (refValues[2 * i] != inputValues[2 * i] ||
235
0
                refValues[2 * i + 1] != inputValues[2 * i + 1])
236
0
            {
237
0
                const Tdiff diff =
238
0
                    std::hypot(static_cast<Tdiff>(refValues[2 * i]) -
239
0
                                   static_cast<Tdiff>(inputValues[2 * i]),
240
0
                               static_cast<Tdiff>(refValues[2 * i + 1]) -
241
0
                                   static_cast<Tdiff>(inputValues[2 * i + 1]));
242
0
                ++countDiffPixels;
243
0
                if (diff > maxDiffValue)
244
0
                    maxDiffValue = diff;
245
0
            }
246
0
        }
247
    }
248
    else
249
0
    {
250
0
        static_assert(sizeof(Tdiff) == sizeof(T));
251
0
        size_t i = 0;
252
0
#ifdef USE_SSE2
253
        if constexpr (std::is_same_v<T, float>)
254
0
        {
255
0
            static_assert(std::is_same_v<T, Tdiff>);
256
257
0
            auto vMaxDiff = _mm_setzero_ps();
258
259
            // Mask for absolute value (clears the sign bit)
260
0
            const auto absMask = _mm_castsi128_ps(
261
0
                _mm_set1_epi32(std::numeric_limits<int32_t>::max()));
262
263
0
            constexpr size_t VALS_PER_REG = sizeof(vMaxDiff) / sizeof(T);
264
0
            while (i + VALS_PER_REG <= nValCount)
265
0
            {
266
0
                auto vCountDiff = _mm_setzero_si128();
267
268
                // We can do a maximum of std::numeric_limits<uint32_t>::max()
269
                // accumulations into vCountDiff
270
0
                const size_t nInnerLimit = [i, nValCount](size_t valsPerReg)
271
0
                {
272
                    if constexpr (sizeof(size_t) > sizeof(uint32_t))
273
0
                    {
274
0
                        return std::min(
275
0
                            nValCount - valsPerReg,
276
0
                            i + std::numeric_limits<uint32_t>::max() *
277
0
                                    valsPerReg);
278
                    }
279
                    else
280
                    {
281
                        return nValCount - valsPerReg;
282
                    }
283
0
                }(VALS_PER_REG);
284
285
0
                for (; i <= nInnerLimit; i += VALS_PER_REG)
286
0
                {
287
0
                    const auto a = _mm_loadu_ps(refValues + i);
288
0
                    const auto b = _mm_loadu_ps(inputValues + i);
289
290
                    // Compute absolute value of difference
291
0
                    const auto absDiff = _mm_and_ps(_mm_sub_ps(a, b), absMask);
292
293
                    // Update vMaxDiff
294
0
                    const auto aIsNan = _mm_cmpunord_ps(a, a);
295
0
                    const auto bIsNan = _mm_cmpunord_ps(b, b);
296
0
                    const auto valNotEqual = _mm_andnot_ps(
297
0
                        _mm_or_ps(aIsNan, bIsNan), _mm_cmpneq_ps(a, b));
298
0
                    vMaxDiff =
299
0
                        _mm_max_ps(vMaxDiff, _mm_and_ps(absDiff, valNotEqual));
300
301
                    // Update vCountDiff
302
0
                    const auto nanMisMatch = _mm_xor_ps(aIsNan, bIsNan);
303
                    // if nanMisMatch OR (both values not NaN and a != b)
304
0
                    const auto maskIsDiff = _mm_or_ps(nanMisMatch, valNotEqual);
305
0
                    const auto shiftedMaskDiff =
306
0
                        _mm_srli_epi32(_mm_castps_si128(maskIsDiff), 31);
307
0
                    vCountDiff = _mm_add_epi32(vCountDiff, shiftedMaskDiff);
308
0
                }
309
310
                // Horizontal add into countDiffPixels
311
0
                uint32_t anCountDiff[VALS_PER_REG];
312
0
                _mm_storeu_si128(reinterpret_cast<__m128i *>(anCountDiff),
313
0
                                 vCountDiff);
314
0
                for (size_t j = 0; j < VALS_PER_REG; ++j)
315
0
                {
316
0
                    countDiffPixels += anCountDiff[j];
317
0
                }
318
0
            }
319
320
            // Horizontal max into maxDiffValue
321
0
            float afMaxDiffValue[VALS_PER_REG];
322
0
            _mm_storeu_ps(afMaxDiffValue, vMaxDiff);
323
0
            for (size_t j = 0; j < VALS_PER_REG; ++j)
324
0
            {
325
0
                CPLAssert(!std::isnan(afMaxDiffValue[j]));
326
0
                maxDiffValue = std::max(maxDiffValue, afMaxDiffValue[j]);
327
0
            }
328
0
        }
329
0
#endif
330
        if constexpr (bIsFloatingPoint)
331
0
        {
332
0
            static_assert(std::is_same_v<T, Tdiff>);
333
0
            for (; i < nValCount; ++i)
334
0
            {
335
0
                if (std::isnan(refValues[i]))
336
0
                {
337
0
                    if (!std::isnan(inputValues[i]))
338
0
                    {
339
0
                        ++countDiffPixels;
340
0
                    }
341
0
                    continue;
342
0
                }
343
0
                else if (std::isnan(inputValues[i]))
344
0
                {
345
0
                    ++countDiffPixels;
346
0
                    continue;
347
0
                }
348
0
                else if (refValues[i] == inputValues[i])
349
0
                {
350
0
                    continue;
351
0
                }
352
353
0
                const Tdiff diff =
354
0
                    refValues[i] >= inputValues[i]
355
0
                        ? Diff(static_cast<Tdiff>(refValues[i]),
356
0
                               static_cast<Tdiff>(inputValues[i]))
357
0
                        : Diff(static_cast<Tdiff>(inputValues[i]),
358
0
                               static_cast<Tdiff>(refValues[i]));
359
0
                if (diff > 0)
360
0
                {
361
0
                    ++countDiffPixels;
362
0
                    if (diff > maxDiffValue)
363
0
                        maxDiffValue = diff;
364
0
                }
365
0
            }
366
        }
367
        else
368
0
        {
369
0
            static_assert(std::is_unsigned_v<Tdiff>);
370
0
            while (i < nValCount)
371
0
            {
372
                // Autovectorizer friendly inner loop (GCC, clang, ICX),
373
                // by making sure it increases countDiffLocal on the same size
374
                // as Tdiff.
375
376
0
                Tdiff countDiffLocal = 0;
377
0
                const size_t innerLimit = [i, nValCount]()
378
0
                {
379
                    if constexpr (sizeof(Tdiff) < sizeof(size_t))
380
0
                    {
381
0
                        return std::min(nValCount,
382
0
                                        i + std::numeric_limits<Tdiff>::max());
383
                    }
384
                    else
385
0
                    {
386
0
                        (void)i;
387
0
                        return nValCount;
388
0
                    }
389
0
                }();
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<unsigned char, unsigned char, false>(unsigned long, unsigned char const*, unsigned char const*, unsigned long&, unsigned char&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<signed char, unsigned char, false>(unsigned long, signed char const*, signed char const*, unsigned long&, unsigned char&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<unsigned short, unsigned short, false>(unsigned long, unsigned short const*, unsigned short const*, unsigned long&, unsigned short&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<short, unsigned short, false>(unsigned long, short const*, short const*, unsigned long&, unsigned short&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<unsigned int, unsigned int, false>(unsigned long, unsigned int const*, unsigned int const*, unsigned long&, unsigned int&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<int, unsigned int, false>(unsigned long, int const*, int const*, unsigned long&, unsigned int&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<unsigned long, unsigned long, false>(unsigned long, unsigned long const*, unsigned long const*, unsigned long&, unsigned long&)::{lambda()#1}::operator()() const
Unexecuted instantiation: gdalalg_raster_compare.cpp:CompareVectors<long, unsigned long, false>(unsigned long, long const*, long const*, unsigned long&, unsigned long&)::{lambda()#1}::operator()() const
390
0
                for (; i < innerLimit; ++i)
391
0
                {
392
0
                    const Tdiff diff =
393
0
                        refValues[i] >= inputValues[i]
394
0
                            ? Diff(static_cast<Tdiff>(refValues[i]),
395
0
                                   static_cast<Tdiff>(inputValues[i]))
396
0
                            : Diff(static_cast<Tdiff>(inputValues[i]),
397
0
                                   static_cast<Tdiff>(refValues[i]));
398
0
                    countDiffLocal += (diff > 0);
399
0
                    maxDiffValue = std::max(maxDiffValue, diff);
400
0
                }
401
0
                countDiffPixels += countDiffLocal;
402
0
            }
403
0
        }
404
0
    }
405
0
}
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<unsigned char, unsigned char, false>(unsigned long, unsigned char const*, unsigned char const*, unsigned long&, unsigned char&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<signed char, unsigned char, false>(unsigned long, signed char const*, signed char const*, unsigned long&, unsigned char&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<unsigned short, unsigned short, false>(unsigned long, unsigned short const*, unsigned short const*, unsigned long&, unsigned short&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<short, unsigned short, false>(unsigned long, short const*, short const*, unsigned long&, unsigned short&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<unsigned int, unsigned int, false>(unsigned long, unsigned int const*, unsigned int const*, unsigned long&, unsigned int&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<int, unsigned int, false>(unsigned long, int const*, int const*, unsigned long&, unsigned int&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<unsigned long, unsigned long, false>(unsigned long, unsigned long const*, unsigned long const*, unsigned long&, unsigned long&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<long, unsigned long, false>(unsigned long, long const*, long const*, unsigned long&, unsigned long&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<float, float, false>(unsigned long, float const*, float const*, unsigned long&, float&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<double, double, false>(unsigned long, double const*, double const*, unsigned long&, double&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<short, float, true>(unsigned long, short const*, short const*, unsigned long&, float&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<int, double, true>(unsigned long, int const*, int const*, unsigned long&, double&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<float, float, true>(unsigned long, float const*, float const*, unsigned long&, float&)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void CompareVectors<double, double, true>(unsigned long, double const*, double const*, unsigned long&, double&)
406
407
/************************************************************************/
408
/*                       DatasetPixelComparison()                       */
409
/************************************************************************/
410
411
template <class T, class Tdiff, bool bIsComplex>
412
static void DatasetPixelComparison(std::vector<std::string> &aosReport,
413
                                   GDALDataset *poRefDS, GDALDataset *poInputDS,
414
                                   GDALDataType eReqDT,
415
                                   GDALProgressFunc pfnProgress,
416
                                   void *pProgressData)
417
0
{
418
0
    std::vector<T> refValues;
419
0
    std::vector<T> inputValues;
420
421
0
    CPLAssert(GDALDataTypeIsComplex(eReqDT) == bIsComplex);
422
423
0
    const uint64_t nTotalPixels =
424
0
        static_cast<uint64_t>(poRefDS->GetRasterXSize()) *
425
0
        poRefDS->GetRasterYSize();
426
0
    uint64_t nIterPixels = 0;
427
428
0
    constexpr int nValPerPixel = bIsComplex ? 2 : 1;
429
0
    const int nBands = poRefDS->GetRasterCount();
430
431
0
    std::vector<Tdiff> maxDiffValue(nBands, 0);
432
0
    std::vector<uint64_t> countDiffPixels(nBands, 0);
433
434
0
    size_t nMaxSize = 0;
435
0
    const GIntBig nUsableRAM = CPLGetUsablePhysicalRAM() / 10;
436
0
    if (nUsableRAM > 0)
437
0
        nMaxSize = static_cast<size_t>(nUsableRAM);
438
439
0
    for (const auto &window : GDALRasterBand::WindowIteratorWrapper(
440
0
             *(poRefDS->GetRasterBand(1)), *(poInputDS->GetRasterBand(1)),
441
0
             nMaxSize))
442
0
    {
443
0
        const size_t nValCount =
444
0
            static_cast<size_t>(window.nXSize) * window.nYSize;
445
0
        const size_t nArraySize = nValCount * nValPerPixel * nBands;
446
0
        try
447
0
        {
448
0
            if (refValues.size() < nArraySize)
449
0
            {
450
0
                refValues.resize(nArraySize);
451
0
                inputValues.resize(nArraySize);
452
0
            }
453
0
        }
454
0
        catch (const std::exception &)
455
0
        {
456
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
457
0
                     "Out of memory allocating temporary arrays");
458
0
            aosReport.push_back("Out of memory allocating temporary arrays");
459
0
            return;
460
0
        }
461
462
0
        if (poRefDS->RasterIO(GF_Read, window.nXOff, window.nYOff,
463
0
                              window.nXSize, window.nYSize, refValues.data(),
464
0
                              window.nXSize, window.nYSize, eReqDT, nBands,
465
0
                              nullptr, 0, 0, 0, nullptr) == CE_None &&
466
0
            poInputDS->RasterIO(
467
0
                GF_Read, window.nXOff, window.nYOff, window.nXSize,
468
0
                window.nYSize, inputValues.data(), window.nXSize, window.nYSize,
469
0
                eReqDT, nBands, nullptr, 0, 0, 0, nullptr) == CE_None)
470
0
        {
471
0
            for (int i = 0; i < nBands; ++i)
472
0
            {
473
0
                CompareVectors<T, Tdiff, bIsComplex>(
474
0
                    nValCount, refValues.data() + i * nValCount * nValPerPixel,
475
0
                    inputValues.data() + i * nValCount * nValPerPixel,
476
0
                    countDiffPixels[i], maxDiffValue[i]);
477
0
            }
478
0
        }
479
0
        else
480
0
        {
481
0
            aosReport.push_back("I/O error when comparing pixel values");
482
0
        }
483
484
0
        if (pfnProgress)
485
0
        {
486
0
            nIterPixels += nValCount;
487
0
            if (!pfnProgress(static_cast<double>(nIterPixels) /
488
0
                                 static_cast<double>(nTotalPixels),
489
0
                             "", pProgressData))
490
0
            {
491
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
492
0
                break;
493
0
            }
494
0
        }
495
0
    }
496
0
    for (int i = 0; i < nBands; ++i)
497
0
    {
498
0
        if (countDiffPixels[i])
499
0
        {
500
0
            aosReport.push_back(
501
0
                "Band " + std::to_string(i + 1) +
502
0
                ": pixels differing: " + std::to_string(countDiffPixels[i]));
503
0
            aosReport.push_back("Band " + std::to_string(i + 1) +
504
0
                                ": maximum pixel value difference: " +
505
0
                                std::to_string(maxDiffValue[i]));
506
0
        }
507
0
    }
508
0
}
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<unsigned char, unsigned char, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<signed char, unsigned char, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<unsigned short, unsigned short, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<short, unsigned short, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<unsigned int, unsigned int, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<int, unsigned int, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<unsigned long, unsigned long, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<long, unsigned long, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<float, float, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<double, double, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<short, float, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<int, double, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<float, float, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void DatasetPixelComparison<double, double, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, GDALDataset*, GDALDataset*, GDALDataType, int (*)(double, char const*, void*), void*)
509
510
/************************************************************************/
511
/*           GDALRasterCompareAlgorithm::DatasetComparison()            */
512
/************************************************************************/
513
514
void GDALRasterCompareAlgorithm::DatasetComparison(
515
    std::vector<std::string> &aosReport, GDALDataset *poRefDS,
516
    GDALDataset *poInputDS, GDALProgressFunc pfnProgress, void *pProgressData)
517
0
{
518
0
    if (!m_skipCRS)
519
0
    {
520
0
        CRSComparison(aosReport, poRefDS, poInputDS);
521
0
    }
522
523
0
    if (!m_skipGeotransform)
524
0
    {
525
0
        GeoTransformComparison(aosReport, poRefDS, poInputDS);
526
0
    }
527
528
0
    bool ret = true;
529
0
    if (poRefDS->GetRasterCount() != poInputDS->GetRasterCount())
530
0
    {
531
0
        aosReport.push_back("Reference dataset has " +
532
0
                            std::to_string(poRefDS->GetRasterCount()) +
533
0
                            " band(s), but input dataset has " +
534
0
                            std::to_string(poInputDS->GetRasterCount()));
535
0
        ret = false;
536
0
    }
537
538
0
    if (poRefDS->GetRasterXSize() != poInputDS->GetRasterXSize())
539
0
    {
540
0
        aosReport.push_back("Reference dataset width is " +
541
0
                            std::to_string(poRefDS->GetRasterXSize()) +
542
0
                            ", but input dataset width is " +
543
0
                            std::to_string(poInputDS->GetRasterXSize()));
544
0
        ret = false;
545
0
    }
546
547
0
    if (poRefDS->GetRasterYSize() != poInputDS->GetRasterYSize())
548
0
    {
549
0
        aosReport.push_back("Reference dataset height is " +
550
0
                            std::to_string(poRefDS->GetRasterYSize()) +
551
0
                            ", but input dataset height is " +
552
0
                            std::to_string(poInputDS->GetRasterYSize()));
553
0
        ret = false;
554
0
    }
555
556
0
    if (!m_skipMetadata)
557
0
    {
558
0
        MetadataComparison(aosReport, "(dataset default metadata domain)",
559
0
                           poRefDS->GetMetadata(), poInputDS->GetMetadata());
560
0
    }
561
562
0
    if (!m_skipRPC)
563
0
    {
564
0
        MetadataComparison(aosReport, GDAL_MDD_RPC,
565
0
                           poRefDS->GetMetadata(GDAL_MDD_RPC),
566
0
                           poInputDS->GetMetadata(GDAL_MDD_RPC));
567
0
    }
568
569
0
    if (!m_skipGeolocation)
570
0
    {
571
0
        MetadataComparison(aosReport, GDAL_MDD_GEOLOCATION,
572
0
                           poRefDS->GetMetadata(GDAL_MDD_GEOLOCATION),
573
0
                           poInputDS->GetMetadata(GDAL_MDD_GEOLOCATION));
574
0
    }
575
576
0
    if (!ret)
577
0
        return;
578
579
0
    const int nBands = poRefDS->GetRasterCount();
580
581
0
    bool doBandBasedPixelComparison = true;
582
    // Do not do band-by-band pixel difference if there are too many interleaved
583
    // bands as this could be extremely slow
584
0
    if (nBands > 10 && !HasMetric(METRIC_NONE))
585
0
    {
586
0
        const char *pszRefInterleave = poRefDS->GetMetadataItem(
587
0
            GDALMD_INTERLEAVE, GDAL_MDD_IMAGE_STRUCTURE);
588
0
        const char *pszInputInterleave = poInputDS->GetMetadataItem(
589
0
            GDALMD_INTERLEAVE, GDAL_MDD_IMAGE_STRUCTURE);
590
0
        if ((pszRefInterleave && EQUAL(pszRefInterleave, "PIXEL")) ||
591
0
            (pszInputInterleave && EQUAL(pszInputInterleave, "PIXEL")))
592
0
        {
593
0
            if (m_metrics != std::vector<std::string>{METRIC_DIFF})
594
0
            {
595
0
                CPLError(CE_Failure, CPLE_AppDefined,
596
0
                         "Given the pixel-interleaved nature of the dataset, "
597
0
                         "only --metrics=diff would be supported");
598
0
            }
599
0
            doBandBasedPixelComparison = false;
600
0
        }
601
0
    }
602
603
0
    for (int i = 0; i < nBands; ++i)
604
0
    {
605
0
        void *pScaledProgress = GDALCreateScaledProgress(
606
0
            static_cast<double>(i) / nBands,
607
0
            static_cast<double>(i + 1) / nBands, pfnProgress, pProgressData);
608
0
        BandComparison(
609
0
            aosReport, std::to_string(i + 1), doBandBasedPixelComparison,
610
0
            poRefDS->GetRasterBand(i + 1), poInputDS->GetRasterBand(i + 1),
611
0
            pScaledProgress ? GDALScaledProgress : nullptr, pScaledProgress);
612
0
        GDALDestroyScaledProgress(pScaledProgress);
613
0
    }
614
615
0
    if (!doBandBasedPixelComparison && HasMetric(METRIC_DIFF))
616
0
    {
617
0
        const auto eReqDT =
618
0
            GDALDataTypeUnion(poRefDS->GetRasterBand(1)->GetRasterDataType(),
619
0
                              poInputDS->GetRasterBand(1)->GetRasterDataType());
620
0
        switch (eReqDT)
621
0
        {
622
0
            case GDT_UInt8:
623
0
                DatasetPixelComparison<uint8_t, uint8_t, false>(
624
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
625
0
                    pProgressData);
626
0
                break;
627
0
            case GDT_Int8:
628
0
                DatasetPixelComparison<int8_t, uint8_t, false>(
629
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
630
0
                    pProgressData);
631
0
                break;
632
0
            case GDT_UInt16:
633
0
                DatasetPixelComparison<uint16_t, uint16_t, false>(
634
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
635
0
                    pProgressData);
636
0
                break;
637
0
            case GDT_Int16:
638
0
                DatasetPixelComparison<int16_t, uint16_t, false>(
639
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
640
0
                    pProgressData);
641
0
                break;
642
0
            case GDT_UInt32:
643
0
                DatasetPixelComparison<uint32_t, uint32_t, false>(
644
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
645
0
                    pProgressData);
646
0
                break;
647
0
            case GDT_Int32:
648
0
                DatasetPixelComparison<int32_t, uint32_t, false>(
649
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
650
0
                    pProgressData);
651
0
                break;
652
0
            case GDT_UInt64:
653
0
                DatasetPixelComparison<uint64_t, uint64_t, false>(
654
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
655
0
                    pProgressData);
656
0
                break;
657
0
            case GDT_Int64:
658
0
                DatasetPixelComparison<int64_t, uint64_t, false>(
659
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
660
0
                    pProgressData);
661
0
                break;
662
0
            case GDT_Float16:
663
0
            case GDT_Float32:
664
0
                DatasetPixelComparison<float, float, false>(
665
0
                    aosReport, poRefDS, poInputDS, GDT_Float32, pfnProgress,
666
0
                    pProgressData);
667
0
                break;
668
0
            case GDT_Float64:
669
0
                DatasetPixelComparison<double, double, false>(
670
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
671
0
                    pProgressData);
672
0
                break;
673
0
            case GDT_CInt16:
674
0
                DatasetPixelComparison<int16_t, float, true>(
675
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
676
0
                    pProgressData);
677
0
                break;
678
0
            case GDT_CInt32:
679
0
                DatasetPixelComparison<int32_t, double, true>(
680
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
681
0
                    pProgressData);
682
0
                break;
683
0
            case GDT_CFloat16:
684
0
            case GDT_CFloat32:
685
0
                DatasetPixelComparison<float, float, true>(
686
0
                    aosReport, poRefDS, poInputDS, GDT_CFloat32, pfnProgress,
687
0
                    pProgressData);
688
0
                break;
689
0
            case GDT_CFloat64:
690
0
                DatasetPixelComparison<double, double, true>(
691
0
                    aosReport, poRefDS, poInputDS, eReqDT, pfnProgress,
692
0
                    pProgressData);
693
0
                break;
694
0
            case GDT_Unknown:
695
0
            case GDT_TypeCount:
696
0
                break;
697
0
        }
698
0
    }
699
0
}
700
701
/************************************************************************/
702
/*                           ComparePixels()                            */
703
/************************************************************************/
704
705
template <class T, class Tdiff, bool bIsComplex>
706
static void ComparePixels(std::vector<std::string> &aosReport,
707
                          const std::string &bandId, GDALRasterBand *poRefBand,
708
                          GDALRasterBand *poInputBand, GDALDataType eReqDT,
709
                          GDALProgressFunc pfnProgress, void *pProgressData)
710
0
{
711
0
    std::vector<T> refValues;
712
0
    std::vector<T> inputValues;
713
0
    Tdiff maxDiffValue = 0;
714
0
    uint64_t countDiffPixels = 0;
715
716
0
    CPLAssert(GDALDataTypeIsComplex(eReqDT) == bIsComplex);
717
0
    const uint64_t nTotalPixels =
718
0
        static_cast<uint64_t>(poRefBand->GetXSize()) * poRefBand->GetYSize();
719
0
    uint64_t nIterPixels = 0;
720
721
0
    constexpr int nValPerPixel = bIsComplex ? 2 : 1;
722
723
0
    size_t nMaxSize = 0;
724
0
    const GIntBig nUsableRAM = CPLGetUsablePhysicalRAM() / 10;
725
0
    if (nUsableRAM > 0)
726
0
        nMaxSize = static_cast<size_t>(nUsableRAM);
727
728
0
    for (const auto &window : GDALRasterBand::WindowIteratorWrapper(
729
0
             *poRefBand, *poInputBand, nMaxSize))
730
0
    {
731
0
        const size_t nValCount =
732
0
            static_cast<size_t>(window.nXSize) * window.nYSize;
733
0
        const size_t nArraySize = nValCount * nValPerPixel;
734
0
        try
735
0
        {
736
0
            if (refValues.size() < nArraySize)
737
0
            {
738
0
                refValues.resize(nArraySize);
739
0
                inputValues.resize(nArraySize);
740
0
            }
741
0
        }
742
0
        catch (const std::exception &)
743
0
        {
744
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
745
0
                     "Out of memory allocating temporary arrays");
746
0
            aosReport.push_back("Out of memory allocating temporary arrays");
747
0
            return;
748
0
        }
749
750
0
        if (poRefBand->RasterIO(GF_Read, window.nXOff, window.nYOff,
751
0
                                window.nXSize, window.nYSize, refValues.data(),
752
0
                                window.nXSize, window.nYSize, eReqDT, 0, 0,
753
0
                                nullptr) == CE_None &&
754
0
            poInputBand->RasterIO(
755
0
                GF_Read, window.nXOff, window.nYOff, window.nXSize,
756
0
                window.nYSize, inputValues.data(), window.nXSize, window.nYSize,
757
0
                eReqDT, 0, 0, nullptr) == CE_None)
758
0
        {
759
0
            CompareVectors<T, Tdiff, bIsComplex>(nValCount, refValues.data(),
760
0
                                                 inputValues.data(),
761
0
                                                 countDiffPixels, maxDiffValue);
762
0
        }
763
0
        else
764
0
        {
765
0
            aosReport.push_back("I/O error when comparing pixel values");
766
0
        }
767
768
0
        if (pfnProgress)
769
0
        {
770
0
            nIterPixels += nValCount;
771
0
            if (!pfnProgress(static_cast<double>(nIterPixels) /
772
0
                                 static_cast<double>(nTotalPixels),
773
0
                             "", pProgressData))
774
0
            {
775
0
                CPLError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
776
0
                break;
777
0
            }
778
0
        }
779
0
    }
780
0
    if (countDiffPixels)
781
0
    {
782
0
        aosReport.push_back("Band " + bandId + ": pixels differing: " +
783
0
                            std::to_string(countDiffPixels));
784
785
0
        std::string reportMessage("Band ");
786
0
        reportMessage += bandId;
787
0
        reportMessage += ": maximum pixel value difference: ";
788
        if constexpr (std::is_floating_point_v<T>)
789
0
        {
790
0
            if (std::isinf(maxDiffValue))
791
0
                reportMessage += "inf";
792
0
            else if (std::isnan(maxDiffValue))
793
0
                reportMessage += "nan";
794
0
            else
795
0
                reportMessage += std::to_string(maxDiffValue);
796
        }
797
        else
798
0
        {
799
0
            reportMessage += std::to_string(maxDiffValue);
800
0
        }
801
0
        aosReport.push_back(std::move(reportMessage));
802
0
    }
803
0
}
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<unsigned char, unsigned char, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<signed char, unsigned char, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<unsigned short, unsigned short, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<short, unsigned short, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<unsigned int, unsigned int, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<int, unsigned int, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<unsigned long, unsigned long, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<long, unsigned long, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<float, float, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<double, double, false>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<short, float, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<int, double, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<float, float, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
Unexecuted instantiation: gdalalg_raster_compare.cpp:void ComparePixels<double, double, true>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, GDALRasterBand*, GDALRasterBand*, GDALDataType, int (*)(double, char const*, void*), void*)
804
805
/************************************************************************/
806
/*                           ComparePixels()                            */
807
/************************************************************************/
808
809
static void ComparePixels(std::vector<std::string> &aosReport,
810
                          const std::string &bandId, GDALRasterBand *poRefBand,
811
                          GDALRasterBand *poInputBand,
812
                          GDALProgressFunc pfnProgress, void *pProgressData)
813
0
{
814
0
    const auto eReqDT = GDALDataTypeUnion(poRefBand->GetRasterDataType(),
815
0
                                          poInputBand->GetRasterDataType());
816
0
    switch (eReqDT)
817
0
    {
818
0
        case GDT_UInt8:
819
0
            ComparePixels<uint8_t, uint8_t, false>(aosReport, bandId, poRefBand,
820
0
                                                   poInputBand, eReqDT,
821
0
                                                   pfnProgress, pProgressData);
822
0
            break;
823
0
        case GDT_Int8:
824
0
            ComparePixels<int8_t, uint8_t, false>(aosReport, bandId, poRefBand,
825
0
                                                  poInputBand, eReqDT,
826
0
                                                  pfnProgress, pProgressData);
827
0
            break;
828
0
        case GDT_UInt16:
829
0
            ComparePixels<uint16_t, uint16_t, false>(
830
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
831
0
                pProgressData);
832
0
            break;
833
0
        case GDT_Int16:
834
0
            ComparePixels<int16_t, uint16_t, false>(
835
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
836
0
                pProgressData);
837
0
            break;
838
0
        case GDT_UInt32:
839
0
            ComparePixels<uint32_t, uint32_t, false>(
840
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
841
0
                pProgressData);
842
0
            break;
843
0
        case GDT_Int32:
844
0
            ComparePixels<int32_t, uint32_t, false>(
845
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
846
0
                pProgressData);
847
0
            break;
848
0
        case GDT_UInt64:
849
0
            ComparePixels<uint64_t, uint64_t, false>(
850
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
851
0
                pProgressData);
852
0
            break;
853
0
        case GDT_Int64:
854
0
            ComparePixels<int64_t, uint64_t, false>(
855
0
                aosReport, bandId, poRefBand, poInputBand, eReqDT, pfnProgress,
856
0
                pProgressData);
857
0
            break;
858
0
        case GDT_Float16:
859
0
        case GDT_Float32:
860
0
            ComparePixels<float, float, false>(aosReport, bandId, poRefBand,
861
0
                                               poInputBand, GDT_Float32,
862
0
                                               pfnProgress, pProgressData);
863
0
            break;
864
0
        case GDT_Float64:
865
0
            ComparePixels<double, double, false>(aosReport, bandId, poRefBand,
866
0
                                                 poInputBand, eReqDT,
867
0
                                                 pfnProgress, pProgressData);
868
0
            break;
869
0
        case GDT_CInt16:
870
0
            ComparePixels<int16_t, float, true>(aosReport, bandId, poRefBand,
871
0
                                                poInputBand, eReqDT,
872
0
                                                pfnProgress, pProgressData);
873
0
            break;
874
0
        case GDT_CInt32:
875
0
            ComparePixels<int32_t, double, true>(aosReport, bandId, poRefBand,
876
0
                                                 poInputBand, eReqDT,
877
0
                                                 pfnProgress, pProgressData);
878
0
            break;
879
0
        case GDT_CFloat16:
880
0
        case GDT_CFloat32:
881
0
            ComparePixels<float, float, true>(aosReport, bandId, poRefBand,
882
0
                                              poInputBand, GDT_CFloat32,
883
0
                                              pfnProgress, pProgressData);
884
0
            break;
885
0
        case GDT_CFloat64:
886
0
            ComparePixels<double, double, true>(aosReport, bandId, poRefBand,
887
0
                                                poInputBand, eReqDT,
888
0
                                                pfnProgress, pProgressData);
889
0
            break;
890
0
        case GDT_Unknown:
891
0
        case GDT_TypeCount:
892
0
            break;
893
0
    }
894
0
}
895
896
#if defined(__GNUC__) && !defined(__clang__)
897
#pragma GCC pop_options
898
#endif
899
900
/************************************************************************/
901
/*             GDALRasterCompareAlgorithm::BandComparison()             */
902
/************************************************************************/
903
904
void GDALRasterCompareAlgorithm::BandComparison(
905
    std::vector<std::string> &aosReport, const std::string &bandId,
906
    bool doBandBasedPixelComparison, GDALRasterBand *poRefBand,
907
    GDALRasterBand *poInputBand, GDALProgressFunc pfnProgress,
908
    void *pProgressData)
909
0
{
910
0
    bool ret = true;
911
912
0
    if (poRefBand->GetXSize() != poInputBand->GetXSize())
913
0
    {
914
0
        aosReport.push_back("Reference band width is " +
915
0
                            std::to_string(poRefBand->GetXSize()) +
916
0
                            ", but input band width is " +
917
0
                            std::to_string(poInputBand->GetXSize()));
918
0
        ret = false;
919
0
    }
920
921
0
    if (poRefBand->GetYSize() != poInputBand->GetYSize())
922
0
    {
923
0
        aosReport.push_back("Reference band height is " +
924
0
                            std::to_string(poRefBand->GetYSize()) +
925
0
                            ", but input band height is " +
926
0
                            std::to_string(poInputBand->GetYSize()));
927
0
        ret = false;
928
0
    }
929
930
0
    if (strcmp(poRefBand->GetDescription(), poInputBand->GetDescription()) != 0)
931
0
    {
932
0
        aosReport.push_back("Reference band " + bandId + " has description " +
933
0
                            std::string(poRefBand->GetDescription()) +
934
0
                            ", but input band has description " +
935
0
                            std::string(poInputBand->GetDescription()));
936
0
    }
937
938
0
    if (poRefBand->GetRasterDataType() != poInputBand->GetRasterDataType())
939
0
    {
940
0
        aosReport.push_back(
941
0
            "Reference band " + bandId + " has data type " +
942
0
            std::string(GDALGetDataTypeName(poRefBand->GetRasterDataType())) +
943
0
            ", but input band has data type " +
944
0
            std::string(GDALGetDataTypeName(poInputBand->GetRasterDataType())));
945
0
    }
946
947
0
    int bRefHasNoData = false;
948
0
    const double dfRefNoData = poRefBand->GetNoDataValue(&bRefHasNoData);
949
0
    int bInputHasNoData = false;
950
0
    const double dfInputNoData = poInputBand->GetNoDataValue(&bInputHasNoData);
951
0
    if (!bRefHasNoData && !bInputHasNoData)
952
0
    {
953
        // ok
954
0
    }
955
0
    else if (bRefHasNoData && !bInputHasNoData)
956
0
    {
957
0
        aosReport.push_back("Reference band " + bandId + " has nodata value " +
958
0
                            std::to_string(dfRefNoData) +
959
0
                            ", but input band has none.");
960
0
    }
961
0
    else if (!bRefHasNoData && bInputHasNoData)
962
0
    {
963
0
        aosReport.push_back("Reference band " + bandId +
964
0
                            " has no nodata value, " +
965
0
                            "but input band has no data value " +
966
0
                            std::to_string(dfInputNoData) + ".");
967
0
    }
968
0
    else if ((std::isnan(dfRefNoData) && std::isnan(dfInputNoData)) ||
969
0
             dfRefNoData == dfInputNoData)
970
0
    {
971
        // ok
972
0
    }
973
0
    else
974
0
    {
975
0
        aosReport.push_back("Reference band " + bandId + " has nodata value " +
976
0
                            std::to_string(dfRefNoData) +
977
0
                            ", but input band has no data value " +
978
0
                            std::to_string(dfInputNoData) + ".");
979
0
    }
980
981
0
    if (poRefBand->GetColorInterpretation() !=
982
0
        poInputBand->GetColorInterpretation())
983
0
    {
984
0
        aosReport.push_back("Reference band " + bandId +
985
0
                            " has color interpretation " +
986
0
                            std::string(GDALGetColorInterpretationName(
987
0
                                poRefBand->GetColorInterpretation())) +
988
0
                            ", but input band has color interpretation " +
989
0
                            std::string(GDALGetColorInterpretationName(
990
0
                                poInputBand->GetColorInterpretation())));
991
0
    }
992
993
0
    if (!ret)
994
0
        return;
995
996
0
    const uint64_t nBasePixels =
997
0
        static_cast<uint64_t>(poRefBand->GetXSize()) * poRefBand->GetYSize();
998
0
    uint64_t nTotalPixels = nBasePixels;
999
0
    const int nOvrCount = poRefBand->GetOverviewCount();
1000
0
    if (!m_skipOverview && nOvrCount == poInputBand->GetOverviewCount())
1001
0
    {
1002
0
        for (int i = 0; i < nOvrCount; ++i)
1003
0
        {
1004
0
            auto poOvrBand = poRefBand->GetOverview(i);
1005
0
            const uint64_t nOvrPixels =
1006
0
                static_cast<uint64_t>(poOvrBand->GetXSize()) *
1007
0
                poOvrBand->GetYSize();
1008
0
            nTotalPixels += nOvrPixels;
1009
0
        }
1010
0
    }
1011
1012
0
    int nCountMetrics = 0;
1013
0
    if (doBandBasedPixelComparison)
1014
0
    {
1015
0
        if (HasMetric(METRIC_DIFF))
1016
0
            ++nCountMetrics;
1017
0
        if (HasMetric(METRIC_RMSD) || HasMetric(METRIC_PSNR))
1018
0
            ++nCountMetrics;
1019
0
    }
1020
1021
0
    double dfLastPct = 0;
1022
0
    if (doBandBasedPixelComparison && HasMetric(METRIC_DIFF))
1023
0
    {
1024
0
        double dfNewLastPct =
1025
0
            dfLastPct + static_cast<double>(nBasePixels) /
1026
0
                            static_cast<double>(nTotalPixels * nCountMetrics);
1027
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1028
0
            pScaledProgress(GDALCreateScaledProgress(dfLastPct, dfNewLastPct,
1029
0
                                                     pfnProgress,
1030
0
                                                     pProgressData),
1031
0
                            GDALDestroyScaledProgress);
1032
0
        dfLastPct = dfNewLastPct;
1033
0
        ComparePixels(aosReport, bandId, poRefBand, poInputBand,
1034
0
                      pScaledProgress ? GDALScaledProgress : nullptr,
1035
0
                      pScaledProgress.get());
1036
0
    }
1037
1038
0
    if (doBandBasedPixelComparison &&
1039
0
        (HasMetric(METRIC_RMSD) || HasMetric(METRIC_PSNR)))
1040
0
    {
1041
        // For PSNR on floating point image, we need to compute min and max of
1042
        // reference band
1043
0
        const bool bIsInteger =
1044
0
            CPL_TO_BOOL(GDALDataTypeIsInteger(poRefBand->GetRasterDataType()));
1045
0
        const double dfScalingProgress =
1046
0
            HasMetric(METRIC_PSNR) && !bIsInteger ? 0.5 : 1;
1047
0
        double dfNewLastPct =
1048
0
            dfLastPct + dfScalingProgress * static_cast<double>(nBasePixels) /
1049
0
                            static_cast<double>(nTotalPixels * nCountMetrics);
1050
0
        std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)>
1051
0
            pScaledProgress(GDALCreateScaledProgress(dfLastPct, dfNewLastPct,
1052
0
                                                     pfnProgress,
1053
0
                                                     pProgressData),
1054
0
                            GDALDestroyScaledProgress);
1055
0
        dfLastPct = dfNewLastPct;
1056
1057
0
        auto diffBand = (*poRefBand) - (*poInputBand);
1058
0
        auto squaredDiffBand = diffBand * diffBand;
1059
0
        double dfMeanSquareError = 0;
1060
0
        if (squaredDiffBand.ComputeStatistics(
1061
0
                /* bApproxOK = */ false,
1062
0
                /* pdfMin = */ nullptr,
1063
0
                /* pdfMax = */ nullptr, &dfMeanSquareError,
1064
0
                /* pdfStdDev = */ nullptr,
1065
0
                pScaledProgress ? GDALScaledProgress : nullptr,
1066
0
                pScaledProgress.get(), nullptr) == CE_None)
1067
0
        {
1068
0
            const double dfRMSD = std::sqrt(dfMeanSquareError);
1069
0
            if (dfRMSD > 0)
1070
0
            {
1071
0
                if (HasMetric(METRIC_RMSD))
1072
0
                {
1073
0
                    aosReport.push_back(CPLSPrintf("Band %s: RMSD: %g",
1074
0
                                                   bandId.c_str(), dfRMSD));
1075
0
                }
1076
1077
0
                if (HasMetric(METRIC_PSNR))
1078
0
                {
1079
0
                    if (bIsInteger)
1080
0
                    {
1081
0
                        double dfMaxAmplitude;
1082
0
                        const char *pszNBITS = poRefBand->GetMetadataItem(
1083
0
                            GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE);
1084
0
                        if (pszNBITS)
1085
0
                            dfMaxAmplitude = std::pow(2.0, atoi(pszNBITS)) - 1;
1086
0
                        else
1087
0
                            dfMaxAmplitude =
1088
0
                                std::pow(2.0,
1089
0
                                         GDALGetDataTypeSizeBits(
1090
0
                                             poRefBand->GetRasterDataType())) -
1091
0
                                1;
1092
1093
0
                        const double dfPSNR_dB =
1094
0
                            20 * std::log10(dfMaxAmplitude / dfRMSD);
1095
0
                        aosReport.push_back(CPLSPrintf("Band %s: PSNR (dB): %g",
1096
0
                                                       bandId.c_str(),
1097
0
                                                       dfPSNR_dB));
1098
0
                    }
1099
0
                    else
1100
0
                    {
1101
0
                        dfNewLastPct =
1102
0
                            dfLastPct + dfScalingProgress *
1103
0
                                            static_cast<double>(nBasePixels) /
1104
0
                                            static_cast<double>(nTotalPixels *
1105
0
                                                                nCountMetrics);
1106
0
                        pScaledProgress.reset(GDALCreateScaledProgress(
1107
0
                            dfLastPct, dfNewLastPct, pfnProgress,
1108
0
                            pProgressData));
1109
0
                        dfLastPct = dfNewLastPct;
1110
0
                        double dfMin = 0;
1111
0
                        double dfMax = 0;
1112
0
                        const char *const apszOptions[] = {
1113
0
                            "SET_STATISTICS=FALSE", nullptr};
1114
0
                        if (poRefBand->ComputeStatistics(
1115
0
                                /* bApproxOK = */ false, &dfMin, &dfMax,
1116
0
                                nullptr, nullptr,
1117
0
                                pScaledProgress ? GDALScaledProgress : nullptr,
1118
0
                                pScaledProgress.get(), apszOptions) == CE_None)
1119
0
                        {
1120
0
                            const double dfPSNR_dB =
1121
0
                                20 * std::log10((dfMax - dfMin) / dfRMSD);
1122
0
                            aosReport.push_back(
1123
0
                                CPLSPrintf("Band %s: PSNR (dB): %g",
1124
0
                                           bandId.c_str(), dfPSNR_dB));
1125
0
                        }
1126
0
                        else
1127
0
                        {
1128
0
                            aosReport.push_back(
1129
0
                                std::string("Error during PSNR computation: ")
1130
0
                                    .append(CPLGetLastErrorMsg()));
1131
0
                        }
1132
0
                    }
1133
0
                }
1134
0
            }
1135
0
        }
1136
0
        else
1137
0
        {
1138
0
            aosReport.push_back(
1139
0
                std::string("Error during RMSD/PSNR computation: ")
1140
0
                    .append(CPLGetLastErrorMsg()));
1141
0
        }
1142
0
    }
1143
1144
0
    CPL_IGNORE_RET_VAL(dfLastPct);
1145
1146
0
    if (!m_skipOverview)
1147
0
    {
1148
0
        if (nOvrCount != poInputBand->GetOverviewCount())
1149
0
        {
1150
0
            aosReport.push_back(
1151
0
                "Reference band " + bandId + " has " +
1152
0
                std::to_string(nOvrCount) +
1153
0
                " overview band(s), but input band has " +
1154
0
                std::to_string(poInputBand->GetOverviewCount()));
1155
0
        }
1156
0
        else
1157
0
        {
1158
0
            uint64_t nIterPixels = nBasePixels;
1159
1160
0
            for (int i = 0; i < nOvrCount; ++i)
1161
0
            {
1162
0
                GDALRasterBand *poOvrBand = poRefBand->GetOverview(i);
1163
0
                const uint64_t nOvrPixels =
1164
0
                    static_cast<uint64_t>(poOvrBand->GetXSize()) *
1165
0
                    poOvrBand->GetYSize();
1166
0
                void *pScaledProgress = GDALCreateScaledProgress(
1167
0
                    static_cast<double>(nIterPixels) /
1168
0
                        static_cast<double>(nTotalPixels),
1169
0
                    static_cast<double>(nIterPixels + nOvrPixels) /
1170
0
                        static_cast<double>(nTotalPixels),
1171
0
                    pfnProgress, pProgressData);
1172
0
                BandComparison(aosReport, "overview of band " + bandId,
1173
0
                               doBandBasedPixelComparison, poOvrBand,
1174
0
                               poInputBand->GetOverview(i),
1175
0
                               pScaledProgress ? GDALScaledProgress : nullptr,
1176
0
                               pScaledProgress);
1177
0
                GDALDestroyScaledProgress(pScaledProgress);
1178
0
                nIterPixels += nOvrPixels;
1179
0
            }
1180
0
        }
1181
0
    }
1182
1183
0
    if (poRefBand->GetMaskFlags() != poInputBand->GetMaskFlags())
1184
0
    {
1185
0
        aosReport.push_back("Reference band " + bandId + " has mask flags = " +
1186
0
                            std::to_string(poRefBand->GetMaskFlags()) +
1187
0
                            " , but input band has mask flags = " +
1188
0
                            std::to_string(poInputBand->GetMaskFlags()));
1189
0
    }
1190
0
    else if (poRefBand->GetMaskFlags() == GMF_PER_DATASET)
1191
0
    {
1192
0
        BandComparison(aosReport, "mask of band " + bandId, true,
1193
0
                       poRefBand->GetMaskBand(), poInputBand->GetMaskBand(),
1194
0
                       nullptr, nullptr);
1195
0
    }
1196
1197
0
    if (!m_skipMetadata)
1198
0
    {
1199
0
        MetadataComparison(aosReport, "(band default metadata domain)",
1200
0
                           poRefBand->GetMetadata(),
1201
0
                           poInputBand->GetMetadata());
1202
0
    }
1203
0
}
1204
1205
/************************************************************************/
1206
/*                GDALRasterCompareAlgorithm::RunStep()                 */
1207
/************************************************************************/
1208
1209
bool GDALRasterCompareAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
1210
0
{
1211
0
    auto poRefDS = m_referenceDataset.GetDatasetRef();
1212
0
    CPLAssert(poRefDS);
1213
1214
0
    CPLAssert(m_inputDataset.size() == 1);
1215
0
    auto poInputDS = m_inputDataset[0].GetDatasetRef();
1216
0
    CPLAssert(poInputDS);
1217
1218
    if constexpr (!HAVE_MUPARSER)
1219
0
    {
1220
0
        for (const char *pszMetric : {METRIC_RMSD, METRIC_PSNR})
1221
0
        {
1222
0
            if (HasMetric(pszMetric))
1223
0
            {
1224
0
                CPLError(CE_Failure, CPLE_NotSupported,
1225
0
                         "%s metric not supported in a GDAL build without "
1226
0
                         "MuParser support",
1227
0
                         pszMetric);
1228
0
                return false;
1229
0
            }
1230
0
        }
1231
0
    }
1232
1233
0
    if (m_skipAllOptional)
1234
0
    {
1235
0
        m_skipBinary = true;
1236
0
        m_skipCRS = true;
1237
0
        m_skipGeotransform = true;
1238
0
        m_skipOverview = true;
1239
0
        m_skipMetadata = true;
1240
0
        m_skipRPC = true;
1241
0
        m_skipGeolocation = true;
1242
0
        m_skipSubdataset = true;
1243
0
    }
1244
1245
0
    std::vector<std::string> aosReport;
1246
1247
0
    if (!m_skipBinary)
1248
0
    {
1249
0
        if (BinaryComparison(this, aosReport, poRefDS, poInputDS))
1250
0
        {
1251
0
            return true;
1252
0
        }
1253
0
    }
1254
1255
0
    CSLConstList papszSubDSRef =
1256
0
        m_skipSubdataset ? nullptr : poRefDS->GetMetadata(GDAL_MDD_SUBDATASETS);
1257
0
    const int nCountRef = CSLCount(papszSubDSRef) / 2;
1258
0
    CSLConstList papszSubDSInput =
1259
0
        m_skipSubdataset ? nullptr
1260
0
                         : poInputDS->GetMetadata(GDAL_MDD_SUBDATASETS);
1261
0
    const int nCountInput = CSLCount(papszSubDSInput) / 2;
1262
1263
0
    if (!m_skipSubdataset)
1264
0
    {
1265
0
        if (nCountRef != nCountInput)
1266
0
        {
1267
0
            aosReport.push_back("Reference dataset has " +
1268
0
                                std::to_string(nCountRef) +
1269
0
                                " subdataset(s) whereas input dataset has " +
1270
0
                                std::to_string(nCountInput) + " one(s).");
1271
0
            m_skipSubdataset = true;
1272
0
        }
1273
0
    }
1274
1275
    // Compute total number of pixels, including in subdatasets
1276
0
    const uint64_t nBasePixels =
1277
0
        static_cast<uint64_t>(poRefDS->GetRasterXSize()) *
1278
0
        poRefDS->GetRasterYSize() * poRefDS->GetRasterCount();
1279
0
    uint64_t nTotalPixels = nBasePixels;
1280
0
    if (ctxt.m_pfnProgress && !m_skipSubdataset)
1281
0
    {
1282
0
        for (int i = 0; i < nCountRef; ++i)
1283
0
        {
1284
0
            const char *pszRef = CSLFetchNameValue(
1285
0
                papszSubDSRef, CPLSPrintf("SUBDATASET_%d_NAME", i + 1));
1286
0
            const char *pszInput = CSLFetchNameValue(
1287
0
                papszSubDSInput, CPLSPrintf("SUBDATASET_%d_NAME", i + 1));
1288
0
            if (pszRef && pszInput)
1289
0
            {
1290
0
                auto poSubRef = std::unique_ptr<GDALDataset>(
1291
0
                    GDALDataset::Open(pszRef, GDAL_OF_RASTER));
1292
0
                auto poSubInput = std::unique_ptr<GDALDataset>(
1293
0
                    GDALDataset::Open(pszInput, GDAL_OF_RASTER));
1294
0
                if (poSubRef && poSubInput)
1295
0
                {
1296
0
                    const uint64_t nSubDSPixels =
1297
0
                        static_cast<uint64_t>(poSubRef->GetRasterXSize()) *
1298
0
                        poSubRef->GetRasterYSize() * poSubRef->GetRasterCount();
1299
0
                    nTotalPixels += nSubDSPixels;
1300
0
                }
1301
0
            }
1302
0
        }
1303
0
    }
1304
1305
0
    {
1306
0
        void *pScaledProgress =
1307
0
            GDALCreateScaledProgress(0.0,
1308
0
                                     static_cast<double>(nBasePixels) /
1309
0
                                         static_cast<double>(nTotalPixels),
1310
0
                                     ctxt.m_pfnProgress, ctxt.m_pProgressData);
1311
0
        DatasetComparison(aosReport, poRefDS, poInputDS,
1312
0
                          pScaledProgress ? GDALScaledProgress : nullptr,
1313
0
                          pScaledProgress);
1314
0
        GDALDestroyScaledProgress(pScaledProgress);
1315
0
    }
1316
1317
0
    if (!m_skipSubdataset)
1318
0
    {
1319
0
        uint64_t nIterPixels = nBasePixels;
1320
0
        for (int i = 0; i < nCountRef; ++i)
1321
0
        {
1322
0
            const char *pszRef = CSLFetchNameValue(
1323
0
                papszSubDSRef, CPLSPrintf("SUBDATASET_%d_NAME", i + 1));
1324
0
            const char *pszInput = CSLFetchNameValue(
1325
0
                papszSubDSInput, CPLSPrintf("SUBDATASET_%d_NAME", i + 1));
1326
0
            if (pszRef && pszInput)
1327
0
            {
1328
0
                auto poSubRef = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1329
0
                    pszRef, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1330
0
                auto poSubInput =
1331
0
                    std::unique_ptr<GDALDataset>(GDALDataset::Open(
1332
0
                        pszInput, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1333
0
                if (poSubRef && poSubInput)
1334
0
                {
1335
0
                    const uint64_t nSubDSPixels =
1336
0
                        static_cast<uint64_t>(poSubRef->GetRasterXSize()) *
1337
0
                        poSubRef->GetRasterYSize() * poSubRef->GetRasterCount();
1338
0
                    void *pScaledProgress = GDALCreateScaledProgress(
1339
0
                        static_cast<double>(nIterPixels) /
1340
0
                            static_cast<double>(nTotalPixels),
1341
0
                        static_cast<double>(nIterPixels + nSubDSPixels) /
1342
0
                            static_cast<double>(nTotalPixels),
1343
0
                        ctxt.m_pfnProgress, ctxt.m_pProgressData);
1344
0
                    DatasetComparison(
1345
0
                        aosReport, poSubRef.get(), poSubInput.get(),
1346
0
                        pScaledProgress ? GDALScaledProgress : nullptr,
1347
0
                        pScaledProgress);
1348
0
                    GDALDestroyScaledProgress(pScaledProgress);
1349
0
                    nIterPixels += nSubDSPixels;
1350
0
                }
1351
0
            }
1352
0
        }
1353
0
    }
1354
1355
0
    for (const auto &s : aosReport)
1356
0
    {
1357
0
        m_output += s;
1358
0
        m_output += '\n';
1359
0
    }
1360
1361
0
    m_retCode = static_cast<int>(aosReport.size());
1362
1363
0
    return true;
1364
0
}
1365
1366
/************************************************************************/
1367
/*               ~GDALRasterCompareAlgorithmStandalone()                */
1368
/************************************************************************/
1369
1370
0
GDALRasterCompareAlgorithmStandalone::~GDALRasterCompareAlgorithmStandalone() =
1371
    default;
1372
1373
//! @endcond