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

OSGeo / gdal / 13836648005

13 Mar 2025 02:09PM UTC coverage: 70.436% (-0.01%) from 70.446%
13836648005

push

github

web-flow
New Transform type: Homography (#11949)

Add new transform type, Homography.
Add functions to compute homography from a list of GCPs.
Add functions to serialize and deserialize a homography
Automatically select homography transfrom when there are 4 or 5 GCPs present.

Fixes #11940

231 of 274 new or added lines in 2 files covered. (84.31%)

16257 existing lines in 42 files now uncovered.

553736 of 786159 relevant lines covered (70.44%)

221595.72 hits per line

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

81.2
/alg/gdalpansharpen.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Pansharpening module
4
 * Purpose:  Implementation of pansharpening.
5
 * Author:   Even Rouault <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
9
 * Copyright (c) 2015, Airbus DS Geo SA (weighted Brovey algorithm)
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "cpl_worker_thread_pool.h"
16
#include "gdalpansharpen.h"
17

18
#include <algorithm>
19
#include <array>
20
#include <cstddef>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <limits>
25
#include <new>
26

27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_float.h"
30
#include "cpl_multiproc.h"
31
#include "cpl_vsi.h"
32
#include "../frmts/mem/memdataset.h"
33
#include "../frmts/vrt/vrtdataset.h"
34
#include "gdal_priv.h"
35
#include "gdal_priv_templates.hpp"
36
// #include "gdalsse_priv.h"
37

38
// Limit types to practical use cases.
39
#define LIMIT_TYPES 1
40

41
/************************************************************************/
42
/*                     GDALCreatePansharpenOptions()                    */
43
/************************************************************************/
44

45
/** Create pansharpening options.
46
 *
47
 * @return a newly allocated pansharpening option structure that must be freed
48
 * with GDALDestroyPansharpenOptions().
49
 *
50
 * @since GDAL 2.1
51
 */
52

53
GDALPansharpenOptions *GDALCreatePansharpenOptions()
113✔
54
{
55
    GDALPansharpenOptions *psOptions = static_cast<GDALPansharpenOptions *>(
56
        CPLCalloc(1, sizeof(GDALPansharpenOptions)));
113✔
57
    psOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
113✔
58
    psOptions->eResampleAlg = GRIORA_Cubic;
113✔
59
    return psOptions;
113✔
60
}
61

62
/************************************************************************/
63
/*                     GDALDestroyPansharpenOptions()                   */
64
/************************************************************************/
65

66
/** Destroy pansharpening options.
67
 *
68
 * @param psOptions a pansharpening option structure allocated with
69
 * GDALCreatePansharpenOptions()
70
 *
71
 * @since GDAL 2.1
72
 */
73

74
void GDALDestroyPansharpenOptions(GDALPansharpenOptions *psOptions)
114✔
75
{
76
    if (psOptions == nullptr)
114✔
77
        return;
1✔
78
    CPLFree(psOptions->padfWeights);
113✔
79
    CPLFree(psOptions->pahInputSpectralBands);
113✔
80
    CPLFree(psOptions->panOutPansharpenedBands);
113✔
81
    CPLFree(psOptions);
113✔
82
}
83

84
/************************************************************************/
85
/*                      GDALClonePansharpenOptions()                    */
86
/************************************************************************/
87

88
/** Clone pansharpening options.
89
 *
90
 * @param psOptions a pansharpening option structure allocated with
91
 * GDALCreatePansharpenOptions()
92
 * @return a newly allocated pansharpening option structure that must be freed
93
 * with GDALDestroyPansharpenOptions().
94
 *
95
 * @since GDAL 2.1
96
 */
97

98
GDALPansharpenOptions *
99
GDALClonePansharpenOptions(const GDALPansharpenOptions *psOptions)
59✔
100
{
101
    GDALPansharpenOptions *psNewOptions = GDALCreatePansharpenOptions();
59✔
102
    psNewOptions->ePansharpenAlg = psOptions->ePansharpenAlg;
59✔
103
    psNewOptions->eResampleAlg = psOptions->eResampleAlg;
59✔
104
    psNewOptions->nBitDepth = psOptions->nBitDepth;
59✔
105
    psNewOptions->nWeightCount = psOptions->nWeightCount;
59✔
106
    if (psOptions->padfWeights)
59✔
107
    {
108
        psNewOptions->padfWeights = static_cast<double *>(
59✔
109
            CPLMalloc(sizeof(double) * psOptions->nWeightCount));
59✔
110
        memcpy(psNewOptions->padfWeights, psOptions->padfWeights,
59✔
111
               sizeof(double) * psOptions->nWeightCount);
59✔
112
    }
113
    psNewOptions->hPanchroBand = psOptions->hPanchroBand;
59✔
114
    psNewOptions->nInputSpectralBands = psOptions->nInputSpectralBands;
59✔
115
    if (psOptions->pahInputSpectralBands)
59✔
116
    {
117
        const size_t nSize =
59✔
118
            sizeof(GDALRasterBandH) * psOptions->nInputSpectralBands;
59✔
119
        psNewOptions->pahInputSpectralBands =
59✔
120
            static_cast<GDALRasterBandH *>(CPLMalloc(nSize));
59✔
121
        memcpy(psNewOptions->pahInputSpectralBands,
59✔
122
               psOptions->pahInputSpectralBands, nSize);
59✔
123
    }
124
    psNewOptions->nOutPansharpenedBands = psOptions->nOutPansharpenedBands;
59✔
125
    if (psOptions->panOutPansharpenedBands)
59✔
126
    {
127
        psNewOptions->panOutPansharpenedBands = static_cast<int *>(
58✔
128
            CPLMalloc(sizeof(int) * psOptions->nOutPansharpenedBands));
58✔
129
        memcpy(psNewOptions->panOutPansharpenedBands,
58✔
130
               psOptions->panOutPansharpenedBands,
58✔
131
               sizeof(int) * psOptions->nOutPansharpenedBands);
58✔
132
    }
133
    psNewOptions->bHasNoData = psOptions->bHasNoData;
59✔
134
    psNewOptions->dfNoData = psOptions->dfNoData;
59✔
135
    psNewOptions->nThreads = psOptions->nThreads;
59✔
136
    return psNewOptions;
59✔
137
}
138

139
/************************************************************************/
140
/*                        GDALPansharpenOperation()                     */
141
/************************************************************************/
142

143
/** Pansharpening operation constructor.
144
 *
145
 * The object is ready to be used after Initialize() has been called.
146
 */
147
GDALPansharpenOperation::GDALPansharpenOperation() = default;
148

149
/************************************************************************/
150
/*                       ~GDALPansharpenOperation()                     */
151
/************************************************************************/
152

153
/** Pansharpening operation destructor.
154
 */
155

156
GDALPansharpenOperation::~GDALPansharpenOperation()
57✔
157
{
158
    GDALDestroyPansharpenOptions(psOptions);
57✔
159
    for (size_t i = 0; i < aVDS.size(); i++)
64✔
160
        delete aVDS[i];
7✔
161
    delete poThreadPool;
57✔
162
}
57✔
163

164
/************************************************************************/
165
/*                              Initialize()                            */
166
/************************************************************************/
167

168
/** Initialize the pansharpening operation.
169
 *
170
 * @param psOptionsIn pansharpening options. Must not be NULL.
171
 *
172
 * @return CE_None in case of success, CE_Failure in case of failure.
173
 */
174
CPLErr
175
GDALPansharpenOperation::Initialize(const GDALPansharpenOptions *psOptionsIn)
57✔
176
{
177
    if (psOptionsIn->hPanchroBand == nullptr)
57✔
178
    {
179
        CPLError(CE_Failure, CPLE_AppDefined, "hPanchroBand not set");
×
180
        return CE_Failure;
×
181
    }
182
    if (psOptionsIn->nInputSpectralBands <= 0)
57✔
183
    {
184
        CPLError(CE_Failure, CPLE_AppDefined,
×
185
                 "No input spectral bands defined");
186
        return CE_Failure;
×
187
    }
188
    if (psOptionsIn->padfWeights == nullptr ||
57✔
189
        psOptionsIn->nWeightCount != psOptionsIn->nInputSpectralBands)
57✔
190
    {
191
        CPLError(CE_Failure, CPLE_AppDefined,
×
192
                 "No weights defined, or not the same number as input "
193
                 "spectral bands");
194
        return CE_Failure;
×
195
    }
196

197
    auto poPanchroBand = GDALRasterBand::FromHandle(psOptionsIn->hPanchroBand);
57✔
198
    auto poPanchroDS = poPanchroBand->GetDataset();
57✔
199
    if (poPanchroDS == nullptr)
57✔
200
    {
201
        CPLError(CE_Failure, CPLE_AppDefined,
×
202
                 "Cannot retrieve dataset associated with hPanchroBand");
203
        return CE_Failure;
×
204
    }
205
    // Make sure that the band is really a first level child of the owning dataset
206
    if (poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != poPanchroBand)
57✔
207
    {
208
        CPLError(CE_Failure, CPLE_AppDefined,
×
209
                 "poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != "
210
                 "poPanchroBand");
211
        return CE_Failure;
×
212
    }
213
    std::array<double, 6> adfPanchroGT;
214
    if (poPanchroDS->GetGeoTransform(adfPanchroGT.data()) != CE_None)
57✔
215
    {
216
        CPLError(CE_Failure, CPLE_AppDefined,
×
217
                 "Panchromatic band has no associated geotransform");
218
        return CE_Failure;
×
219
    }
220

221
    GDALRasterBandH hRefBand = psOptionsIn->pahInputSpectralBands[0];
57✔
222
    auto poRefBand = GDALRasterBand::FromHandle(hRefBand);
57✔
223
    auto poRefBandDS = poRefBand->GetDataset();
57✔
224
    if (poRefBandDS == nullptr)
57✔
225
    {
226
        CPLError(
×
227
            CE_Failure, CPLE_AppDefined,
228
            "Cannot retrieve dataset associated with ahInputSpectralBands[0]");
229
        return CE_Failure;
×
230
    }
231
    // Make sure that the band is really a first level child of the owning dataset
232
    if (poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand)
57✔
233
    {
234
        CPLError(
×
235
            CE_Failure, CPLE_AppDefined,
236
            "poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand");
237
        return CE_Failure;
×
238
    }
239

240
    std::array<double, 6> adfRefMSGT;
241
    if (poRefBandDS->GetGeoTransform(adfRefMSGT.data()) != CE_None)
57✔
242
    {
243
        CPLError(CE_Failure, CPLE_AppDefined,
×
244
                 "ahInputSpectralBands[0] band has no associated geotransform");
245
        return CE_Failure;
×
246
    }
247

248
    std::array<double, 6> adfInvMSGT;
249
    if (!GDALInvGeoTransform(adfRefMSGT.data(), adfInvMSGT.data()))
57✔
250
    {
251
        CPLError(CE_Failure, CPLE_AppDefined,
×
252
                 "ahInputSpectralBands[0] geotransform is not invertible");
253
        return CE_Failure;
×
254
    }
255

256
    // Do InvMSGT * PanchroGT multiplication
257
    m_adfPanToMSGT[1] =
114✔
258
        adfInvMSGT[1] * adfPanchroGT[1] + adfInvMSGT[2] * adfPanchroGT[4];
57✔
259
    m_adfPanToMSGT[2] =
114✔
260
        adfInvMSGT[1] * adfPanchroGT[2] + adfInvMSGT[2] * adfPanchroGT[5];
57✔
261
    m_adfPanToMSGT[0] = adfInvMSGT[1] * adfPanchroGT[0] +
57✔
262
                        adfInvMSGT[2] * adfPanchroGT[3] + adfInvMSGT[0];
57✔
263
    m_adfPanToMSGT[4] =
114✔
264
        adfInvMSGT[4] * adfPanchroGT[1] + adfInvMSGT[5] * adfPanchroGT[4];
57✔
265
    m_adfPanToMSGT[5] =
114✔
266
        adfInvMSGT[4] * adfPanchroGT[2] + adfInvMSGT[5] * adfPanchroGT[5];
57✔
267
    m_adfPanToMSGT[3] = adfInvMSGT[4] * adfPanchroGT[0] +
57✔
268
                        adfInvMSGT[5] * adfPanchroGT[3] + adfInvMSGT[3];
57✔
269
#if 0
270
    CPLDebug("GDAL", "m_adfPanToMSGT[] = %g %g %g %g %g %g",
271
           m_adfPanToMSGT[0], m_adfPanToMSGT[1], m_adfPanToMSGT[2],
272
           m_adfPanToMSGT[3], m_adfPanToMSGT[4], m_adfPanToMSGT[5]);
273
#endif
274
    if (std::fabs(m_adfPanToMSGT[2]) > 1e-10 ||
114✔
275
        std::fabs(m_adfPanToMSGT[4]) > 1e-10)
57✔
276
    {
277
        CPLError(CE_Failure, CPLE_NotSupported,
×
278
                 "Composition of panchromatic and multispectral geotransform "
279
                 "has rotational terms");
280
        return CE_Failure;
×
281
    }
282

283
    bool bSameDataset = psOptionsIn->nInputSpectralBands > 1;
57✔
284
    if (bSameDataset)
57✔
285
        anInputBands.push_back(GDALGetBandNumber(hRefBand));
42✔
286
    for (int i = 1; i < psOptionsIn->nInputSpectralBands; i++)
135✔
287
    {
288
        GDALRasterBandH hBand = psOptionsIn->pahInputSpectralBands[i];
79✔
289
        if (GDALGetRasterBandXSize(hBand) != GDALGetRasterBandXSize(hRefBand) ||
157✔
290
            GDALGetRasterBandYSize(hBand) != GDALGetRasterBandYSize(hRefBand))
78✔
291
        {
292
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
293
                     "Dimensions of input spectral band %d different from "
294
                     "first spectral band",
295
                     i + 1);
296
            return CE_Failure;
1✔
297
        }
298

299
        auto poBand = GDALRasterBand::FromHandle(hBand);
78✔
300
        auto poBandDS = poBand->GetDataset();
78✔
301
        if (poBandDS == nullptr)
78✔
302
        {
303
            CPLError(CE_Failure, CPLE_AppDefined,
×
304
                     "Cannot retrieve dataset associated with "
305
                     "input spectral band %d",
306
                     i + 1);
307
            return CE_Failure;
×
308
        }
309
        // Make sure that the band is really a first level child of the owning dataset
310
        if (poBandDS->GetRasterBand(poBand->GetBand()) != poBand)
78✔
311
        {
312
            CPLError(CE_Failure, CPLE_AppDefined,
×
313
                     "poBandDS->GetRasterBand(poBand->GetBand()) != poBand");
314
            return CE_Failure;
×
315
        }
316

317
        std::array<double, 6> adfMSGT;
318
        if (poBandDS->GetGeoTransform(adfMSGT.data()) != CE_None)
78✔
319
        {
320
            CPLError(
×
321
                CE_Failure, CPLE_AppDefined,
322
                "input spectral band %d band has no associated geotransform",
323
                i + 1);
324
            return CE_Failure;
×
325
        }
326
        if (adfMSGT != adfRefMSGT)
78✔
327
        {
328
            CPLError(CE_Failure, CPLE_AppDefined,
×
329
                     "input spectral band %d has a different "
330
                     "geotransform than the first spectral band",
331
                     i + 1);
332
            return CE_Failure;
×
333
        }
334

335
        if (bSameDataset)
78✔
336
        {
337
            if (GDALGetBandDataset(hBand) != GDALGetBandDataset(hRefBand))
76✔
338
            {
339
                anInputBands.resize(0);
4✔
340
                bSameDataset = false;
4✔
341
            }
342
            else
343
            {
344
                anInputBands.push_back(GDALGetBandNumber(hBand));
72✔
345
            }
346
        }
347
    }
348
    if (psOptionsIn->nOutPansharpenedBands == 0)
56✔
349
    {
350
        CPLError(CE_Warning, CPLE_AppDefined,
1✔
351
                 "No output pansharpened band defined");
352
    }
353
    for (int i = 0; i < psOptionsIn->nOutPansharpenedBands; i++)
184✔
354
    {
355
        if (psOptionsIn->panOutPansharpenedBands[i] < 0 ||
128✔
356
            psOptionsIn->panOutPansharpenedBands[i] >=
128✔
357
                psOptionsIn->nInputSpectralBands)
128✔
358
        {
359
            CPLError(CE_Failure, CPLE_AppDefined,
×
360
                     "Invalid value panOutPansharpenedBands[%d] = %d", i,
361
                     psOptionsIn->panOutPansharpenedBands[i]);
×
362
            return CE_Failure;
×
363
        }
364
    }
365

366
    GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
56✔
367
    if (psOptionsIn->nBitDepth)
56✔
368
    {
369
        if (psOptionsIn->nBitDepth < 0 || psOptionsIn->nBitDepth > 31 ||
12✔
370
            (eWorkDataType == GDT_Byte && psOptionsIn->nBitDepth > 8) ||
12✔
371
            (eWorkDataType == GDT_UInt16 && psOptionsIn->nBitDepth > 16))
3✔
372
        {
373
            CPLError(CE_Failure, CPLE_AppDefined,
×
374
                     "Invalid value nBitDepth = %d for type %s",
375
                     psOptionsIn->nBitDepth,
×
376
                     GDALGetDataTypeName(eWorkDataType));
377
            return CE_Failure;
×
378
        }
379
    }
380

381
    psOptions = GDALClonePansharpenOptions(psOptionsIn);
56✔
382
    if (psOptions->nBitDepth == GDALGetDataTypeSize(eWorkDataType))
56✔
383
        psOptions->nBitDepth = 0;
6✔
384
    if (psOptions->nBitDepth &&
56✔
385
        !(eWorkDataType == GDT_Byte || eWorkDataType == GDT_UInt16 ||
3✔
386
          eWorkDataType == GDT_UInt32 || eWorkDataType == GDT_UInt64))
387
    {
388
        CPLError(CE_Warning, CPLE_AppDefined,
×
389
                 "Ignoring nBitDepth = %d for type %s", psOptions->nBitDepth,
×
390
                 GDALGetDataTypeName(eWorkDataType));
391
        psOptions->nBitDepth = 0;
×
392
    }
393

394
    // Detect negative weights.
395
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
190✔
396
    {
397
        if (psOptions->padfWeights[i] < 0.0)
134✔
398
        {
399
            bPositiveWeights = FALSE;
×
400
            break;
×
401
        }
402
    }
403

404
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
190✔
405
    {
406
        aMSBands.push_back(
268✔
407
            GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]));
134✔
408
    }
409

410
    if (psOptions->bHasNoData)
56✔
411
    {
412
        bool bNeedToWrapInVRT = false;
7✔
413
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
21✔
414
        {
415
            GDALRasterBand *poBand =
416
                GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
14✔
417
            int bHasNoData = FALSE;
14✔
418
            double dfNoData = poBand->GetNoDataValue(&bHasNoData);
14✔
419
            if (!bHasNoData || dfNoData != psOptions->dfNoData)
14✔
420
                bNeedToWrapInVRT = true;
11✔
421
        }
422

423
        if (bNeedToWrapInVRT)
7✔
424
        {
425
            // Wrap spectral bands in a VRT if they don't have the nodata value.
426
            VRTDataset *poVDS = nullptr;
6✔
427
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
17✔
428
            {
429
                GDALRasterBand *poSrcBand = aMSBands[i];
11✔
430
                int iVRTBand = 1;
11✔
431
                if (anInputBands.empty() || i == 0)
11✔
432
                {
433
                    poVDS = new VRTDataset(poSrcBand->GetXSize(),
7✔
434
                                           poSrcBand->GetYSize());
7✔
435
                    aVDS.push_back(poVDS);
7✔
436
                }
437
                if (!anInputBands.empty())
11✔
438
                {
439
                    anInputBands[i] = i + 1;
7✔
440
                    iVRTBand = i + 1;
7✔
441
                }
442
                poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
11✔
443
                VRTSourcedRasterBand *poVRTBand =
444
                    dynamic_cast<VRTSourcedRasterBand *>(
×
445
                        poVDS->GetRasterBand(iVRTBand));
11✔
446
                if (poVRTBand == nullptr)
11✔
447
                    return CE_Failure;
×
448
                aMSBands[i] = poVRTBand;
11✔
449
                poVRTBand->SetNoDataValue(psOptions->dfNoData);
11✔
450
                const char *pszNBITS =
451
                    poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
11✔
452
                if (pszNBITS)
11✔
453
                    poVRTBand->SetMetadataItem("NBITS", pszNBITS,
×
454
                                               "IMAGE_STRUCTURE");
×
455

456
                VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
11✔
457
                poVRTBand->ConfigureSource(
44✔
458
                    poSimpleSource, poSrcBand, FALSE, 0, 0,
459
                    poSrcBand->GetXSize(), poSrcBand->GetYSize(), 0, 0,
11✔
460
                    poSrcBand->GetXSize(), poSrcBand->GetYSize());
11✔
461
                poVRTBand->AddSource(poSimpleSource);
11✔
462
            }
463
        }
464
    }
465

466
    // Setup thread pool.
467
    int nThreads = psOptions->nThreads;
56✔
468
    if (nThreads == -1)
56✔
469
        nThreads = CPLGetNumCPUs();
10✔
470
    else if (nThreads == 0)
46✔
471
    {
472
        const char *pszNumThreads =
473
            CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
46✔
474
        if (pszNumThreads)
46✔
475
        {
476
            if (EQUAL(pszNumThreads, "ALL_CPUS"))
×
477
                nThreads = CPLGetNumCPUs();
×
478
            else
479
                nThreads = std::max(0, std::min(128, atoi(pszNumThreads)));
×
480
        }
481
    }
482
    if (nThreads > 1)
56✔
483
    {
484
        CPLDebug("PANSHARPEN", "Using %d threads", nThreads);
10✔
485
        poThreadPool = new (std::nothrow) CPLWorkerThreadPool();
20✔
486
        // coverity[tainted_data]
487
        if (poThreadPool == nullptr ||
20✔
488
            !poThreadPool->Setup(nThreads, nullptr, nullptr))
10✔
489
        {
490
            delete poThreadPool;
×
491
            poThreadPool = nullptr;
×
492
        }
493
    }
494

495
    GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
56✔
496
    if (eResampleAlg != GRIORA_NearestNeighbour)
56✔
497
    {
498
        const char *pszResampling =
56✔
499
            (eResampleAlg == GRIORA_Bilinear)      ? "BILINEAR"
112✔
500
            : (eResampleAlg == GRIORA_Cubic)       ? "CUBIC"
56✔
501
            : (eResampleAlg == GRIORA_CubicSpline) ? "CUBICSPLINE"
×
502
            : (eResampleAlg == GRIORA_Lanczos)     ? "LANCZOS"
×
503
            : (eResampleAlg == GRIORA_Average)     ? "AVERAGE"
×
504
            : (eResampleAlg == GRIORA_RMS)         ? "RMS"
×
505
            : (eResampleAlg == GRIORA_Mode)        ? "MODE"
×
506
            : (eResampleAlg == GRIORA_Gauss)       ? "GAUSS"
×
507
                                                   : "UNKNOWN";
508

509
        GDALGetResampleFunction(pszResampling, &nKernelRadius);
56✔
510
    }
511

512
    return CE_None;
56✔
513
}
514

515
/************************************************************************/
516
/*                    WeightedBroveyWithNoData()                        */
517
/************************************************************************/
518

519
template <class WorkDataType, class OutDataType>
520
void GDALPansharpenOperation::WeightedBroveyWithNoData(
4✔
521
    const WorkDataType *pPanBuffer,
522
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
523
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
524
{
525
    WorkDataType noData, validValue;
526
    GDALCopyWord(psOptions->dfNoData, noData);
4✔
527

528
    if constexpr (!(std::numeric_limits<WorkDataType>::is_integer))
529
        validValue = static_cast<WorkDataType>(noData + 1e-5);
×
530
    else if (noData == std::numeric_limits<WorkDataType>::min())
4✔
531
        validValue = std::numeric_limits<WorkDataType>::min() + 1;
4✔
532
    else
533
        validValue = noData - 1;
×
534

535
    for (size_t j = 0; j < nValues; j++)
1,280,000✔
536
    {
537
        double dfPseudoPanchro = 0.0;
1,280,000✔
538
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
4,480,000✔
539
        {
540
            WorkDataType nSpectralVal =
3,200,000✔
541
                pUpsampledSpectralBuffer[i * nBandValues + j];
3,200,000✔
542
            if (nSpectralVal == noData)
3,200,000✔
543
            {
544
                dfPseudoPanchro = 0.0;
×
545
                break;
×
546
            }
547
            dfPseudoPanchro += psOptions->padfWeights[i] * nSpectralVal;
3,200,000✔
548
        }
549
        if (dfPseudoPanchro != 0.0 && pPanBuffer[j] != noData)
1,280,000✔
550
        {
551
            const double dfFactor = pPanBuffer[j] / dfPseudoPanchro;
1,277,710✔
552
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
4,471,990✔
553
            {
554
                WorkDataType nRawValue = pUpsampledSpectralBuffer
3,194,280✔
555
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
3,194,280✔
556
                WorkDataType nPansharpenedValue;
557
                GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
3,194,280✔
558
                if (nMaxValue != 0 && nPansharpenedValue > nMaxValue)
3,194,280✔
559
                    nPansharpenedValue = nMaxValue;
×
560
                // We don't want a valid value to be mapped to NoData.
561
                if (nPansharpenedValue == noData)
3,194,280✔
562
                    nPansharpenedValue = validValue;
3,238✔
563
                GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
3,194,280✔
564
            }
1,277,710✔
565
        }
566
        else
567
        {
568
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
8,008✔
569
            {
570
                GDALCopyWord(noData, pDataBuf[i * nBandValues + j]);
5,720✔
571
            }
572
        }
573
    }
574
}
4✔
575

576
/************************************************************************/
577
/*                           ComputeFactor()                            */
578
/************************************************************************/
579

580
template <class T>
581
static inline double ComputeFactor(T panValue, double dfPseudoPanchro)
20,685,440✔
582
{
583
    if (dfPseudoPanchro == 0.0)
20,685,440✔
584
        return 0.0;
2,778✔
585

586
    return panValue / dfPseudoPanchro;
20,682,660✔
587
}
588

589
/************************************************************************/
590
/*                           ClampAndRound()                            */
591
/************************************************************************/
592

593
template <class T> static inline T ClampAndRound(double dfVal, T nMaxValue)
1,025,098✔
594
{
595
    if (dfVal > nMaxValue)
1,025,098✔
596
        return nMaxValue;
50✔
597
    else
598
        return static_cast<T>(dfVal + 0.5);
1,025,044✔
599
}
600

601
/************************************************************************/
602
/*                         WeightedBrovey()                             */
603
/************************************************************************/
604

605
template <class WorkDataType, class OutDataType, int bHasBitDepth>
606
void GDALPansharpenOperation::WeightedBrovey3(
131✔
607
    const WorkDataType *pPanBuffer,
608
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
609
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
610
{
611
    if (psOptions->bHasNoData)
131✔
612
    {
613
        WeightedBroveyWithNoData<WorkDataType, OutDataType>(
4✔
614
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
615
            nBandValues, nMaxValue);
616
        return;
4✔
617
    }
618

619
    for (size_t j = 0; j < nValues; j++)
19,866,092✔
620
    {
621
        double dfFactor = 0.0;
19,953,580✔
622
        // if( pPanBuffer[j] == 0 )
623
        //     dfFactor = 1.0;
624
        // else
625
        {
626
            double dfPseudoPanchro = 0.0;
19,953,580✔
627
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
86,014,800✔
628
                dfPseudoPanchro +=
66,061,200✔
629
                    psOptions->padfWeights[i] *
66,061,200✔
630
                    pUpsampledSpectralBuffer[i * nBandValues + j];
66,061,200✔
631
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
19,953,580✔
632
        }
633

634
        for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
77,767,300✔
635
        {
636
            WorkDataType nRawValue =
57,901,200✔
637
                pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
57,901,200✔
638
                                             nBandValues +
57,901,200✔
639
                                         j];
640
            WorkDataType nPansharpenedValue;
641
            GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
57,901,200✔
642
            if constexpr (bHasBitDepth)
643
            {
644
                if (nPansharpenedValue > nMaxValue)
×
645
                    nPansharpenedValue = nMaxValue;
×
646
            }
647
            GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
55,163,100✔
648
        }
649
    }
650
}
651

652
/* We restrict to 64bit processors because they are guaranteed to have SSE2 */
653
/* Could possibly be used too on 32bit, but we would need to check at runtime */
654
#if defined(__x86_64) || defined(_M_X64) || defined(USE_NEON_OPTIMIZATIONS)
655

656
#define USE_SSE2
657
#include "gdalsse_priv.h"
658

659
template <class T, int NINPUT, int NOUTPUT>
660
size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
40✔
661
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
662
    size_t nValues, size_t nBandValues, T nMaxValue) const
663
{
664
    static_assert(NINPUT == 3 || NINPUT == 4);
665
    static_assert(NOUTPUT == 3 || NOUTPUT == 4);
666
    const XMMReg4Double w0 =
40✔
667
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 0);
40✔
668
    const XMMReg4Double w1 =
41✔
669
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 1);
41✔
670
    const XMMReg4Double w2 =
41✔
671
        XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 2);
41✔
672
    [[maybe_unused]] const XMMReg4Double w3 =
40✔
673
        (NINPUT == 3)
674
            ? XMMReg4Double::Zero()
675
            : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 3);
7✔
676

677
    const XMMReg4Double zero = XMMReg4Double::Zero();
39✔
678
    double dfMaxValue = nMaxValue;
39✔
679
    const XMMReg4Double maxValue =
39✔
680
        XMMReg4Double::Load1ValHighAndLow(&dfMaxValue);
681

682
    size_t j = 0;  // Used after for.
114✔
683
    for (; j + 3 < nValues; j += 4)
1,307,971✔
684
    {
685
        XMMReg4Double pseudoPanchro = zero;
1,307,930✔
686

687
        XMMReg4Double val0 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,300,934✔
688
                                                     0 * nBandValues + j);
752,518✔
689
        XMMReg4Double val1 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,315,051✔
690
                                                     1 * nBandValues + j);
1,315,051✔
691
        XMMReg4Double val2 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,304,231✔
692
                                                     2 * nBandValues + j);
1,304,231✔
693
        XMMReg4Double val3;
694
        if constexpr (NINPUT == 4 || NOUTPUT == 4)
695
        {
696
            val3 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
511,200✔
697
                                           3 * nBandValues + j);
508,057✔
698
        }
699

700
        pseudoPanchro += w0 * val0;
1,312,040✔
701
        pseudoPanchro += w1 * val1;
1,316,602✔
702
        pseudoPanchro += w2 * val2;
1,317,608✔
703
        if constexpr (NINPUT == 4)
704
            pseudoPanchro += w3 * val3;
510,860✔
705

706
        /* Little trick to avoid use of ternary operator due to one of the
707
         * branch being zero */
708
        XMMReg4Double factor = XMMReg4Double::And(
1,314,667✔
709
            XMMReg4Double::NotEquals(pseudoPanchro, zero),
710
            XMMReg4Double::Load4Val(pPanBuffer + j) / pseudoPanchro);
761,168✔
711

712
        val0 = XMMReg4Double::Min(val0 * factor, maxValue);
1,310,464✔
713
        val1 = XMMReg4Double::Min(val1 * factor, maxValue);
1,306,919✔
714
        val2 = XMMReg4Double::Min(val2 * factor, maxValue);
1,308,627✔
715
        if constexpr (NOUTPUT == 4)
716
        {
717
            val3 = XMMReg4Double::Min(val3 * factor, maxValue);
253,262✔
718
        }
719
        val0.Store4Val(pDataBuf + 0 * nBandValues + j);
1,310,036✔
720
        val1.Store4Val(pDataBuf + 1 * nBandValues + j);
1,316,536✔
721
        val2.Store4Val(pDataBuf + 2 * nBandValues + j);
1,317,377✔
722
        if constexpr (NOUTPUT == 4)
723
        {
724
            val3.Store4Val(pDataBuf + 3 * nBandValues + j);
254,068✔
725
        }
726
    }
727
    return j;
41✔
728
}
729

730
#else
731

732
template <class T, int NINPUT, int NOUTPUT>
733
size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
734
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
735
    size_t nValues, size_t nBandValues, T nMaxValue) const
736
{
737
    static_assert(NINPUT == 3 || NINPUT == 4);
738
    const double dfw0 = psOptions->padfWeights[0];
739
    const double dfw1 = psOptions->padfWeights[1];
740
    const double dfw2 = psOptions->padfWeights[2];
741
    // cppcheck-suppress knownConditionTrueFalse
742
    [[maybe_unused]] const double dfw3 =
743
        (NINPUT == 3) ? 0 : psOptions->padfWeights[3];
744
    size_t j = 0;  // Used after for.
745
    for (; j + 1 < nValues; j += 2)
746
    {
747
        double dfFactor = 0.0;
748
        double dfFactor2 = 0.0;
749
        double dfPseudoPanchro = 0.0;
750
        double dfPseudoPanchro2 = 0.0;
751

752
        dfPseudoPanchro += dfw0 * pUpsampledSpectralBuffer[j];
753
        dfPseudoPanchro2 += dfw0 * pUpsampledSpectralBuffer[j + 1];
754

755
        dfPseudoPanchro += dfw1 * pUpsampledSpectralBuffer[nBandValues + j];
756
        dfPseudoPanchro2 +=
757
            dfw1 * pUpsampledSpectralBuffer[nBandValues + j + 1];
758

759
        dfPseudoPanchro += dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j];
760
        dfPseudoPanchro2 +=
761
            dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j + 1];
762

763
        if constexpr (NINPUT == 4)
764
        {
765
            dfPseudoPanchro +=
766
                dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j];
767
            dfPseudoPanchro2 +=
768
                dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j + 1];
769
        }
770

771
        dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
772
        dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
773

774
        for (int i = 0; i < NOUTPUT; i++)
775
        {
776
            T nRawValue = pUpsampledSpectralBuffer[i * nBandValues + j];
777
            double dfTmp = nRawValue * dfFactor;
778
            pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
779

780
            T nRawValue2 = pUpsampledSpectralBuffer[i * nBandValues + j + 1];
781
            double dfTmp2 = nRawValue2 * dfFactor2;
782
            pDataBuf[i * nBandValues + j + 1] =
783
                ClampAndRound(dfTmp2, nMaxValue);
784
        }
785
    }
786
    return j;
787
}
788
#endif
789

790
template <class T>
791
void GDALPansharpenOperation::WeightedBroveyPositiveWeights(
62✔
792
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
793
    size_t nValues, size_t nBandValues, T nMaxValue) const
794
{
795
    if (psOptions->bHasNoData)
62✔
796
    {
797
        WeightedBroveyWithNoData<T, T>(pPanBuffer, pUpsampledSpectralBuffer,
×
798
                                       pDataBuf, nValues, nBandValues,
799
                                       nMaxValue);
800
        return;
×
801
    }
802

803
    if (nMaxValue == 0)
62✔
804
        nMaxValue = cpl::NumericLimits<T>::max();
56✔
805
    size_t j;
806
    if (psOptions->nInputSpectralBands == 3 &&
62✔
807
        psOptions->nOutPansharpenedBands == 3 &&
33✔
808
        psOptions->panOutPansharpenedBands[0] == 0 &&
33✔
809
        psOptions->panOutPansharpenedBands[1] == 1 &&
33✔
810
        psOptions->panOutPansharpenedBands[2] == 2)
33✔
811
    {
812
        j = WeightedBroveyPositiveWeightsInternal<T, 3, 3>(
33✔
813
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
814
            nBandValues, nMaxValue);
815
    }
816
    else if (psOptions->nInputSpectralBands == 4 &&
29✔
817
             psOptions->nOutPansharpenedBands == 4 &&
8✔
818
             psOptions->panOutPansharpenedBands[0] == 0 &&
4✔
819
             psOptions->panOutPansharpenedBands[1] == 1 &&
3✔
820
             psOptions->panOutPansharpenedBands[2] == 2 &&
4✔
821
             psOptions->panOutPansharpenedBands[3] == 3)
4✔
822
    {
823
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 4>(
4✔
824
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
825
            nBandValues, nMaxValue);
826
    }
827
    else if (psOptions->nInputSpectralBands == 4 &&
25✔
828
             psOptions->nOutPansharpenedBands == 3 &&
4✔
829
             psOptions->panOutPansharpenedBands[0] == 0 &&
4✔
830
             psOptions->panOutPansharpenedBands[1] == 1 &&
4✔
831
             psOptions->panOutPansharpenedBands[2] == 2)
4✔
832
    {
833
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 3>(
4✔
834
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
835
            nBandValues, nMaxValue);
836
    }
837
    else
838
    {
839
        for (j = 0; j + 1 < nValues; j += 2)
374,669✔
840
        {
841
            double dfFactor = 0.0;
390,724✔
842
            double dfFactor2 = 0.0;
390,724✔
843
            double dfPseudoPanchro = 0.0;
390,724✔
844
            double dfPseudoPanchro2 = 0.0;
390,724✔
845
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1,113,078✔
846
            {
847
                dfPseudoPanchro +=
722,355✔
848
                    psOptions->padfWeights[i] *
722,355✔
849
                    pUpsampledSpectralBuffer[i * nBandValues + j];
722,355✔
850
                dfPseudoPanchro2 +=
722,355✔
851
                    psOptions->padfWeights[i] *
722,355✔
852
                    pUpsampledSpectralBuffer[i * nBandValues + j + 1];
722,355✔
853
            }
854

855
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
390,724✔
856
            dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
397,341✔
857

858
            for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
990,392✔
859
            {
860
                const T nRawValue = pUpsampledSpectralBuffer
615,744✔
861
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
615,744✔
862
                const double dfTmp = nRawValue * dfFactor;
615,744✔
863
                pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
615,744✔
864

865
                const T nRawValue2 = pUpsampledSpectralBuffer
617,637✔
866
                    [psOptions->panOutPansharpenedBands[i] * nBandValues + j +
617,637✔
867
                     1];
868
                const double dfTmp2 = nRawValue2 * dfFactor2;
617,637✔
869
                pDataBuf[i * nBandValues + j + 1] =
596,894✔
870
                    ClampAndRound(dfTmp2, nMaxValue);
617,637✔
871
            }
872
        }
873
    }
874
    for (; j < nValues; j++)
18✔
875
    {
876
        double dfFactor = 0.0;
13✔
877
        double dfPseudoPanchro = 0.0;
13✔
878
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
54✔
879
            dfPseudoPanchro += psOptions->padfWeights[i] *
41✔
880
                               pUpsampledSpectralBuffer[i * nBandValues + j];
41✔
881
        dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
13✔
882

883
        for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
53✔
884
        {
885
            T nRawValue =
40✔
886
                pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
40✔
887
                                             nBandValues +
40✔
888
                                         j];
889
            double dfTmp = nRawValue * dfFactor;
40✔
890
            pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
40✔
891
        }
892
    }
893
}
894

895
template <class WorkDataType, class OutDataType>
896
void GDALPansharpenOperation::WeightedBrovey(
126✔
897
    const WorkDataType *pPanBuffer,
898
    const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
899
    size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
900
{
901
    if (nMaxValue == 0)
126✔
902
        WeightedBrovey3<WorkDataType, OutDataType, FALSE>(
126✔
903
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
904
            nBandValues, 0);
905
    else
906
    {
907
        WeightedBrovey3<WorkDataType, OutDataType, TRUE>(
×
908
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
909
            nBandValues, nMaxValue);
910
    }
911
}
126✔
912

913
template <class T>
914
void GDALPansharpenOperation::WeightedBroveyGByteOrUInt16(
62✔
915
    const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
916
    size_t nValues, size_t nBandValues, T nMaxValue) const
917
{
918
    if (bPositiveWeights)
62✔
919
    {
920
        WeightedBroveyPositiveWeights(pPanBuffer, pUpsampledSpectralBuffer,
62✔
921
                                      pDataBuf, nValues, nBandValues,
922
                                      nMaxValue);
923
    }
924
    else if (nMaxValue == 0)
×
925
    {
926
        WeightedBrovey3<T, T, FALSE>(pPanBuffer, pUpsampledSpectralBuffer,
×
927
                                     pDataBuf, nValues, nBandValues, 0);
928
    }
929
    else
930
    {
931
        WeightedBrovey3<T, T, TRUE>(pPanBuffer, pUpsampledSpectralBuffer,
×
932
                                    pDataBuf, nValues, nBandValues, nMaxValue);
933
    }
934
}
63✔
935

936
template <>
937
void GDALPansharpenOperation::WeightedBrovey<GByte, GByte>(
48✔
938
    const GByte *pPanBuffer, const GByte *pUpsampledSpectralBuffer,
939
    GByte *pDataBuf, size_t nValues, size_t nBandValues, GByte nMaxValue) const
940
{
941
    WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
48✔
942
                                nValues, nBandValues, nMaxValue);
943
}
48✔
944

945
template <>
946
void GDALPansharpenOperation::WeightedBrovey<GUInt16, GUInt16>(
15✔
947
    const GUInt16 *pPanBuffer, const GUInt16 *pUpsampledSpectralBuffer,
948
    GUInt16 *pDataBuf, size_t nValues, size_t nBandValues,
949
    GUInt16 nMaxValue) const
950
{
951
    WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
15✔
952
                                nValues, nBandValues, nMaxValue);
953
}
15✔
954

955
template <class WorkDataType>
956
CPLErr GDALPansharpenOperation::WeightedBrovey(
186✔
957
    const WorkDataType *pPanBuffer,
958
    const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
959
    GDALDataType eBufDataType, size_t nValues, size_t nBandValues,
960
    WorkDataType nMaxValue) const
961
{
962
    switch (eBufDataType)
186✔
963
    {
964
        case GDT_Byte:
49✔
965
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
49✔
966
                           static_cast<GByte *>(pDataBuf), nValues, nBandValues,
967
                           nMaxValue);
968
            break;
49✔
969

970
        case GDT_UInt16:
16✔
971
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
16✔
972
                           static_cast<GUInt16 *>(pDataBuf), nValues,
973
                           nBandValues, nMaxValue);
974
            break;
16✔
975

976
#ifndef LIMIT_TYPES
977
        case GDT_Int8:
978
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
979
                           static_cast<GInt8 *>(pDataBuf), nValues, nBandValues,
980
                           nMaxValue);
981
            break;
982

983
        case GDT_Int16:
984
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
985
                           static_cast<GInt16 *>(pDataBuf), nValues,
986
                           nBandValues, nMaxValue);
987
            break;
988

989
        case GDT_UInt32:
990
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
991
                           static_cast<GUInt32 *>(pDataBuf), nValues,
992
                           nBandValues, nMaxValue);
993
            break;
994

995
        case GDT_Int32:
996
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
997
                           static_cast<GInt32 *>(pDataBuf), nValues,
998
                           nBandValues, nMaxValue);
999
            break;
1000

1001
        case GDT_UInt64:
1002
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1003
                           static_cast<std::uint64_t *>(pDataBuf), nValues,
1004
                           nBandValues, nMaxValue);
1005
            break;
1006

1007
        case GDT_Int64:
1008
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1009
                           static_cast<std::int64_t *>(pDataBuf), nValues,
1010
                           nBandValues, nMaxValue);
1011
            break;
1012

1013
        case GDT_Float16:
1014
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1015
                           static_cast<GFloat16 *>(pDataBuf), nValues,
1016
                           nBandValues, nMaxValue);
1017
            break;
1018

1019
        case GDT_Float32:
1020
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1021
                           static_cast<float *>(pDataBuf), nValues, nBandValues,
1022
                           nMaxValue);
1023
            break;
1024
#endif
1025

1026
        case GDT_Float64:
123✔
1027
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
123✔
1028
                           static_cast<double *>(pDataBuf), nValues,
1029
                           nBandValues, nMaxValue);
1030
            break;
124✔
1031

UNCOV
1032
        default:
×
UNCOV
1033
            CPLError(CE_Failure, CPLE_NotSupported,
×
1034
                     "eBufDataType not supported");
1035
            return CE_Failure;
×
1036
            break;
1037
    }
1038

1039
    return CE_None;
189✔
1040
}
1041

1042
template <class WorkDataType>
1043
CPLErr GDALPansharpenOperation::WeightedBrovey(
6✔
1044
    const WorkDataType *pPanBuffer,
1045
    const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
1046
    GDALDataType eBufDataType, size_t nValues, size_t nBandValues) const
1047
{
1048
    switch (eBufDataType)
6✔
1049
    {
1050
        case GDT_Byte:
6✔
1051
            WeightedBrovey3<WorkDataType, GByte, FALSE>(
6✔
1052
                pPanBuffer, pUpsampledSpectralBuffer,
1053
                static_cast<GByte *>(pDataBuf), nValues, nBandValues, 0);
1054
            break;
6✔
1055

1056
        case GDT_UInt16:
×
1057
            WeightedBrovey3<WorkDataType, GUInt16, FALSE>(
×
1058
                pPanBuffer, pUpsampledSpectralBuffer,
1059
                static_cast<GUInt16 *>(pDataBuf), nValues, nBandValues, 0);
1060
            break;
×
1061

1062
#ifndef LIMIT_TYPES
1063
        case GDT_Int8:
1064
            WeightedBrovey3<WorkDataType, GInt8, FALSE>(
1065
                pPanBuffer, pUpsampledSpectralBuffer,
1066
                static_cast<GInt8 *>(pDataBuf), nValues, nBandValues, 0);
1067
            break;
1068

1069
        case GDT_Int16:
1070
            WeightedBrovey3<WorkDataType, GInt16, FALSE>(
1071
                pPanBuffer, pUpsampledSpectralBuffer,
1072
                static_cast<GInt16 *>(pDataBuf), nValues, nBandValues, 0);
1073
            break;
1074

1075
        case GDT_UInt32:
1076
            WeightedBrovey3<WorkDataType, GUInt32, FALSE>(
1077
                pPanBuffer, pUpsampledSpectralBuffer,
1078
                static_cast<GUInt32 *>(pDataBuf), nValues, nBandValues, 0);
1079
            break;
1080

1081
        case GDT_Int32:
1082
            WeightedBrovey3<WorkDataType, GInt32, FALSE>(
1083
                pPanBuffer, pUpsampledSpectralBuffer,
1084
                static_cast<GInt32 *>(pDataBuf), nValues, nBandValues, 0);
1085
            break;
1086

1087
        case GDT_UInt64:
1088
            WeightedBrovey3<WorkDataType, std::uint64_t, FALSE>(
1089
                pPanBuffer, pUpsampledSpectralBuffer,
1090
                static_cast<std::uint64_t *>(pDataBuf), nValues, nBandValues,
1091
                0);
1092
            break;
1093

1094
        case GDT_Int64:
1095
            WeightedBrovey3<WorkDataType, std::int64_t, FALSE>(
1096
                pPanBuffer, pUpsampledSpectralBuffer,
1097
                static_cast<std::int64_t *>(pDataBuf), nValues, nBandValues, 0);
1098
            break;
1099

1100
        case GDT_Float16:
1101
            WeightedBrovey3<WorkDataType, GFloat16, FALSE>(
1102
                pPanBuffer, pUpsampledSpectralBuffer,
1103
                static_cast<GFloat16 *>(pDataBuf), nValues, nBandValues, 0);
1104
            break;
1105

1106
        case GDT_Float32:
1107
            WeightedBrovey3<WorkDataType, float, FALSE>(
1108
                pPanBuffer, pUpsampledSpectralBuffer,
1109
                static_cast<float *>(pDataBuf), nValues, nBandValues, 0);
1110
            break;
1111
#endif
1112

1113
        case GDT_Float64:
×
1114
            WeightedBrovey3<WorkDataType, double, FALSE>(
×
1115
                pPanBuffer, pUpsampledSpectralBuffer,
1116
                static_cast<double *>(pDataBuf), nValues, nBandValues, 0);
1117
            break;
×
1118

1119
        default:
×
1120
            CPLError(CE_Failure, CPLE_NotSupported,
×
1121
                     "eBufDataType not supported");
1122
            return CE_Failure;
×
1123
            break;
1124
    }
1125

1126
    return CE_None;
6✔
1127
}
1128

1129
/************************************************************************/
1130
/*                           ClampValues()                              */
1131
/************************************************************************/
1132

1133
template <class T>
1134
static void ClampValues(T *panBuffer, size_t nValues, T nMaxVal)
2✔
1135
{
1136
    for (size_t i = 0; i < nValues; i++)
34✔
1137
    {
1138
        if (panBuffer[i] > nMaxVal)
32✔
1139
            panBuffer[i] = nMaxVal;
8✔
1140
    }
1141
}
2✔
1142

1143
/************************************************************************/
1144
/*                         ProcessRegion()                              */
1145
/************************************************************************/
1146

1147
/** Executes a pansharpening operation on a rectangular region of the
1148
 * resulting dataset.
1149
 *
1150
 * The window is expressed with respect to the dimensions of the panchromatic
1151
 * band.
1152
 *
1153
 * Spectral bands are upsampled and merged with the panchromatic band according
1154
 * to the select algorithm and options.
1155
 *
1156
 * @param nXOff pixel offset.
1157
 * @param nYOff pixel offset.
1158
 * @param nXSize width of the pansharpened region to compute.
1159
 * @param nYSize height of the pansharpened region to compute.
1160
 * @param pDataBuf output buffer. Must be nXSize * nYSize *
1161
 *                 GDALGetDataTypeSizeBytes(eBufDataType) *
1162
 *                 psOptions->nOutPansharpenedBands large.
1163
 *                 It begins with all values of the first output band, followed
1164
 *                 by values of the second output band, etc...
1165
 * @param eBufDataType data type of the output buffer
1166
 *
1167
 * @return CE_None in case of success, CE_Failure in case of failure.
1168
 *
1169
 * @since GDAL 2.1
1170
 */
1171
CPLErr GDALPansharpenOperation::ProcessRegion(int nXOff, int nYOff, int nXSize,
84✔
1172
                                              int nYSize, void *pDataBuf,
1173
                                              GDALDataType eBufDataType)
1174
{
1175
    if (psOptions == nullptr)
84✔
1176
        return CE_Failure;
×
1177

1178
    // TODO: Avoid allocating buffers each time.
1179
    GDALRasterBand *poPanchroBand =
1180
        GDALRasterBand::FromHandle(psOptions->hPanchroBand);
84✔
1181
    GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
84✔
1182
#ifdef LIMIT_TYPES
1183
    if (eWorkDataType != GDT_Byte && eWorkDataType != GDT_UInt16)
84✔
1184
        eWorkDataType = GDT_Float64;
6✔
1185
#endif
1186
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eWorkDataType);
84✔
1187
    GByte *pUpsampledSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
84✔
1188
        nXSize, nYSize,
1189
        cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1190
    GByte *pPanBuffer = static_cast<GByte *>(
1191
        VSI_MALLOC3_VERBOSE(nXSize, nYSize, nDataTypeSize));
84✔
1192
    if (pUpsampledSpectralBuffer == nullptr || pPanBuffer == nullptr)
84✔
1193
    {
1194
        VSIFree(pUpsampledSpectralBuffer);
×
1195
        VSIFree(pPanBuffer);
×
1196
        return CE_Failure;
×
1197
    }
1198

1199
    CPLErr eErr = poPanchroBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
84✔
1200
                                          pPanBuffer, nXSize, nYSize,
1201
                                          eWorkDataType, 0, 0, nullptr);
1202
    if (eErr != CE_None)
84✔
1203
    {
1204
        VSIFree(pUpsampledSpectralBuffer);
×
1205
        VSIFree(pPanBuffer);
×
1206
        return CE_Failure;
×
1207
    }
1208

1209
    int nTasks = 0;
84✔
1210
    if (poThreadPool)
84✔
1211
    {
1212
        nTasks = poThreadPool->GetThreadCount();
37✔
1213
        if (nTasks > nYSize)
37✔
1214
            nTasks = nYSize;
×
1215
    }
1216

1217
    GDALRasterIOExtraArg sExtraArg;
1218
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
84✔
1219
    const GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
84✔
1220
    // cppcheck-suppress redundantAssignment
1221
    sExtraArg.eResampleAlg = eResampleAlg;
84✔
1222
    sExtraArg.bFloatingPointWindowValidity = TRUE;
84✔
1223
    sExtraArg.dfXOff = m_adfPanToMSGT[0] + nXOff * m_adfPanToMSGT[1];
84✔
1224
    sExtraArg.dfYOff = m_adfPanToMSGT[3] + nYOff * m_adfPanToMSGT[5];
84✔
1225
    sExtraArg.dfXSize = nXSize * m_adfPanToMSGT[1];
84✔
1226
    sExtraArg.dfYSize = nYSize * m_adfPanToMSGT[5];
84✔
1227
    if (sExtraArg.dfXOff + sExtraArg.dfXSize > aMSBands[0]->GetXSize())
84✔
1228
        sExtraArg.dfXSize = aMSBands[0]->GetXSize() - sExtraArg.dfXOff;
2✔
1229
    if (sExtraArg.dfYOff + sExtraArg.dfYSize > aMSBands[0]->GetYSize())
84✔
1230
        sExtraArg.dfYSize = aMSBands[0]->GetYSize() - sExtraArg.dfYOff;
2✔
1231
    int nSpectralXOff = static_cast<int>(sExtraArg.dfXOff);
84✔
1232
    int nSpectralYOff = static_cast<int>(sExtraArg.dfYOff);
84✔
1233
    int nSpectralXSize = static_cast<int>(0.49999 + sExtraArg.dfXSize);
84✔
1234
    int nSpectralYSize = static_cast<int>(0.49999 + sExtraArg.dfYSize);
84✔
1235
    if (nSpectralXOff + nSpectralXSize > aMSBands[0]->GetXSize())
84✔
1236
        nSpectralXSize = aMSBands[0]->GetXSize() - nSpectralXOff;
×
1237
    if (nSpectralYOff + nSpectralYSize > aMSBands[0]->GetYSize())
84✔
1238
        nSpectralYSize = aMSBands[0]->GetYSize() - nSpectralYOff;
×
1239
    if (nSpectralXSize == 0)
84✔
1240
        nSpectralXSize = 1;
10✔
1241
    if (nSpectralYSize == 0)
84✔
1242
        nSpectralYSize = 1;
10✔
1243

1244
    // When upsampling, extract the multispectral data at
1245
    // full resolution in a temp buffer, and then do the upsampling.
1246
    if (nSpectralXSize < nXSize && nSpectralYSize < nYSize &&
84✔
1247
        eResampleAlg != GRIORA_NearestNeighbour && nYSize > 1)
74✔
1248
    {
1249
        // Take some margin to take into account the radius of the
1250
        // resampling kernel.
1251
        int nXOffExtract = nSpectralXOff - nKernelRadius;
74✔
1252
        int nYOffExtract = nSpectralYOff - nKernelRadius;
74✔
1253
        int nXSizeExtract = nSpectralXSize + 1 + 2 * nKernelRadius;
74✔
1254
        int nYSizeExtract = nSpectralYSize + 1 + 2 * nKernelRadius;
74✔
1255
        if (nXOffExtract < 0)
74✔
1256
        {
1257
            nXSizeExtract += nXOffExtract;
67✔
1258
            nXOffExtract = 0;
67✔
1259
        }
1260
        if (nYOffExtract < 0)
74✔
1261
        {
1262
            nYSizeExtract += nYOffExtract;
60✔
1263
            nYOffExtract = 0;
60✔
1264
        }
1265
        if (nXOffExtract + nXSizeExtract > aMSBands[0]->GetXSize())
74✔
1266
            nXSizeExtract = aMSBands[0]->GetXSize() - nXOffExtract;
67✔
1267
        if (nYOffExtract + nYSizeExtract > aMSBands[0]->GetYSize())
74✔
1268
            nYSizeExtract = aMSBands[0]->GetYSize() - nYOffExtract;
60✔
1269

1270
        GByte *pSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
74✔
1271
            nXSizeExtract, nYSizeExtract,
1272
            cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1273
        if (pSpectralBuffer == nullptr)
74✔
1274
        {
1275
            VSIFree(pUpsampledSpectralBuffer);
×
1276
            VSIFree(pPanBuffer);
×
1277
            return CE_Failure;
×
1278
        }
1279

1280
        if (!anInputBands.empty())
74✔
1281
        {
1282
            // Use dataset RasterIO when possible.
1283
            eErr = aMSBands[0]->GetDataset()->RasterIO(
132✔
1284
                GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1285
                nYSizeExtract, pSpectralBuffer, nXSizeExtract, nYSizeExtract,
1286
                eWorkDataType, static_cast<int>(anInputBands.size()),
66✔
1287
                &anInputBands[0], 0, 0, 0, nullptr);
66✔
1288
        }
1289
        else
1290
        {
1291
            for (int i = 0;
8✔
1292
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
20✔
1293
            {
1294
                eErr = aMSBands[i]->RasterIO(
24✔
1295
                    GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1296
                    nYSizeExtract,
1297
                    pSpectralBuffer + static_cast<size_t>(i) * nXSizeExtract *
12✔
1298
                                          nYSizeExtract * nDataTypeSize,
12✔
1299
                    nXSizeExtract, nYSizeExtract, eWorkDataType, 0, 0, nullptr);
1300
            }
1301
        }
1302
        if (eErr != CE_None)
74✔
1303
        {
1304
            VSIFree(pSpectralBuffer);
×
1305
            VSIFree(pUpsampledSpectralBuffer);
×
1306
            VSIFree(pPanBuffer);
×
1307
            return CE_Failure;
×
1308
        }
1309

1310
        // Create a MEM dataset that wraps the input buffer.
1311
        auto poMEMDS = MEMDataset::Create("", nXSizeExtract, nYSizeExtract, 0,
74✔
1312
                                          eWorkDataType, nullptr);
1313

1314
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
294✔
1315
        {
1316
            GByte *pabyBuffer =
220✔
1317
                pSpectralBuffer + static_cast<size_t>(i) * nDataTypeSize *
220✔
1318
                                      nXSizeExtract * nYSizeExtract;
220✔
1319
            GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
220✔
1320
                poMEMDS, i + 1, pabyBuffer, eWorkDataType, 0, 0, false);
1321
            poMEMDS->AddMEMBand(hMEMBand);
220✔
1322

1323
            const char *pszNBITS =
1324
                aMSBands[i]->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
220✔
1325
            if (pszNBITS)
220✔
1326
                poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
4✔
1327
                    "NBITS", pszNBITS, "IMAGE_STRUCTURE");
4✔
1328

1329
            if (psOptions->bHasNoData)
220✔
1330
                poMEMDS->GetRasterBand(i + 1)->SetNoDataValue(
10✔
1331
                    psOptions->dfNoData);
10✔
1332
        }
1333

1334
        if (nTasks <= 1)
74✔
1335
        {
1336
            nSpectralXOff -= nXOffExtract;
37✔
1337
            nSpectralYOff -= nYOffExtract;
37✔
1338
            sExtraArg.dfXOff -= nXOffExtract;
37✔
1339
            sExtraArg.dfYOff -= nYOffExtract;
37✔
1340
            CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
37✔
1341
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1342
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1343
                eWorkDataType, psOptions->nInputSpectralBands, nullptr, 0, 0, 0,
37✔
1344
                &sExtraArg));
1345
        }
1346
        else
1347
        {
1348
            // We are abusing the contract of the GDAL API by using the
1349
            // MEMDataset from several threads. In this case, this is safe. In
1350
            // case, that would no longer be the case we could create as many
1351
            // MEMDataset as threads pointing to the same buffer.
1352

1353
            // To avoid races in threads, we query now the mask flags,
1354
            // so that implicit mask bands are created now.
1355
            for (int i = 0; i < poMEMDS->GetRasterCount(); i++)
162✔
1356
            {
1357
                poMEMDS->GetRasterBand(i + 1)->GetMaskFlags();
125✔
1358
            }
1359

1360
            std::vector<GDALPansharpenResampleJob> asJobs;
37✔
1361
            asJobs.resize(nTasks);
37✔
1362
            GDALPansharpenResampleJob *pasJobs = &(asJobs[0]);
37✔
1363
            {
1364
                std::vector<void *> ahJobData;
37✔
1365
                ahJobData.resize(nTasks);
37✔
1366

1367
#ifdef DEBUG_TIMING
1368
                struct timeval tv;
1369
#endif
1370
                for (int i = 0; i < nTasks; i++)
185✔
1371
                {
1372
                    const size_t iStartLine =
148✔
1373
                        (static_cast<size_t>(i) * nYSize) / nTasks;
148✔
1374
                    const size_t iNextStartLine =
148✔
1375
                        (static_cast<size_t>(i + 1) * nYSize) / nTasks;
148✔
1376
                    pasJobs[i].poMEMDS = poMEMDS;
148✔
1377
                    pasJobs[i].eResampleAlg = eResampleAlg;
148✔
1378
                    pasJobs[i].dfXOff = sExtraArg.dfXOff - nXOffExtract;
148✔
1379
                    pasJobs[i].dfYOff =
148✔
1380
                        m_adfPanToMSGT[3] +
148✔
1381
                        (nYOff + iStartLine) * m_adfPanToMSGT[5] - nYOffExtract;
148✔
1382
                    pasJobs[i].dfXSize = sExtraArg.dfXSize;
148✔
1383
                    pasJobs[i].dfYSize =
148✔
1384
                        (iNextStartLine - iStartLine) * m_adfPanToMSGT[5];
148✔
1385
                    if (pasJobs[i].dfXOff + pasJobs[i].dfXSize > nXSizeExtract)
148✔
1386
                    {
1387
                        pasJobs[i].dfXSize = nYSizeExtract - pasJobs[i].dfXOff;
×
1388
                    }
1389
                    if (pasJobs[i].dfYOff + pasJobs[i].dfYSize >
296✔
1390
                        aMSBands[0]->GetYSize())
148✔
1391
                    {
1392
                        pasJobs[i].dfYSize =
×
1393
                            aMSBands[0]->GetYSize() - pasJobs[i].dfYOff;
×
1394
                    }
1395
                    pasJobs[i].nXOff = static_cast<int>(pasJobs[i].dfXOff);
148✔
1396
                    pasJobs[i].nYOff = static_cast<int>(pasJobs[i].dfYOff);
148✔
1397
                    pasJobs[i].nXSize =
148✔
1398
                        static_cast<int>(0.4999 + pasJobs[i].dfXSize);
148✔
1399
                    pasJobs[i].nYSize =
148✔
1400
                        static_cast<int>(0.4999 + pasJobs[i].dfYSize);
148✔
1401
                    if (pasJobs[i].nXOff + pasJobs[i].nXSize > nXSizeExtract)
148✔
1402
                    {
1403
                        pasJobs[i].nXSize = nXSizeExtract - pasJobs[i].nXOff;
×
1404
                    }
1405
                    if (pasJobs[i].nYOff + pasJobs[i].nYSize > nYSizeExtract)
148✔
1406
                    {
1407
                        pasJobs[i].nYSize = nYSizeExtract - pasJobs[i].nYOff;
×
1408
                    }
1409
                    if (pasJobs[i].nXSize == 0)
148✔
1410
                        pasJobs[i].nXSize = 1;
×
1411
                    if (pasJobs[i].nYSize == 0)
148✔
1412
                        pasJobs[i].nYSize = 1;
×
1413
                    pasJobs[i].pBuffer = pUpsampledSpectralBuffer +
148✔
1414
                                         static_cast<size_t>(iStartLine) *
148✔
1415
                                             nXSize * nDataTypeSize;
148✔
1416
                    pasJobs[i].eDT = eWorkDataType;
148✔
1417
                    pasJobs[i].nBufXSize = nXSize;
148✔
1418
                    pasJobs[i].nBufYSize =
148✔
1419
                        static_cast<int>(iNextStartLine - iStartLine);
148✔
1420
                    pasJobs[i].nBandCount = psOptions->nInputSpectralBands;
148✔
1421
                    pasJobs[i].nBandSpace =
148✔
1422
                        static_cast<GSpacing>(nXSize) * nYSize * nDataTypeSize;
148✔
1423
#ifdef DEBUG_TIMING
1424
                    pasJobs[i].ptv = &tv;
1425
#endif
1426
                    pasJobs[i].eErr = CE_Failure;
148✔
1427

1428
                    ahJobData[i] = &(pasJobs[i]);
148✔
1429
                }
1430
#ifdef DEBUG_TIMING
1431
                gettimeofday(&tv, nullptr);
1432
#endif
1433
                poThreadPool->SubmitJobs(PansharpenResampleJobThreadFunc,
37✔
1434
                                         ahJobData);
1435
                poThreadPool->WaitCompletion();
37✔
1436

1437
                for (int i = 0; i < nTasks; i++)
185✔
1438
                {
1439
                    if (pasJobs[i].eErr == CE_Failure)
148✔
1440
                    {
1441
                        CPLError(CE_Failure, CPLE_AppDefined, "%s",
×
1442
                                 pasJobs[i].osLastErrorMsg.c_str());
×
1443
                        GDALClose(poMEMDS);
×
1444
                        VSIFree(pSpectralBuffer);
×
1445
                        VSIFree(pUpsampledSpectralBuffer);
×
1446
                        VSIFree(pPanBuffer);
×
1447

1448
                        return CE_Failure;
×
1449
                    }
1450
                }
1451
            }
1452
        }
1453

1454
        GDALClose(poMEMDS);
74✔
1455

1456
        VSIFree(pSpectralBuffer);
74✔
1457
    }
1458
    else
1459
    {
1460
        if (!anInputBands.empty())
10✔
1461
        {
1462
            // Use dataset RasterIO when possible.
1463
            eErr = aMSBands[0]->GetDataset()->RasterIO(
20✔
1464
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1465
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1466
                eWorkDataType, static_cast<int>(anInputBands.size()),
10✔
1467
                &anInputBands[0], 0, 0, 0, &sExtraArg);
10✔
1468
        }
1469
        else
1470
        {
1471
            for (int i = 0;
×
1472
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
×
1473
            {
1474
                eErr = aMSBands[i]->RasterIO(
×
1475
                    GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1476
                    nSpectralYSize,
1477
                    pUpsampledSpectralBuffer + static_cast<size_t>(i) * nXSize *
×
1478
                                                   nYSize * nDataTypeSize,
×
1479
                    nXSize, nYSize, eWorkDataType, 0, 0, &sExtraArg);
1480
            }
1481
        }
1482
        if (eErr != CE_None)
10✔
1483
        {
1484
            VSIFree(pUpsampledSpectralBuffer);
×
1485
            VSIFree(pPanBuffer);
×
1486
            return CE_Failure;
×
1487
        }
1488
    }
1489

1490
    // In case NBITS was not set on the spectral bands, clamp the values
1491
    // if overshoot might have occurred.
1492
    int nBitDepth = psOptions->nBitDepth;
84✔
1493
    if (nBitDepth &&
84✔
1494
        (eResampleAlg == GRIORA_Cubic || eResampleAlg == GRIORA_CubicSpline ||
×
1495
         eResampleAlg == GRIORA_Lanczos))
1496
    {
1497
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
12✔
1498
        {
1499
            GDALRasterBand *poBand = aMSBands[i];
6✔
1500
            int nBandBitDepth = 0;
6✔
1501
            const char *pszNBITS =
1502
                poBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
6✔
1503
            if (pszNBITS)
6✔
1504
                nBandBitDepth = atoi(pszNBITS);
4✔
1505
            if (nBandBitDepth < nBitDepth)
6✔
1506
            {
1507
                if (eWorkDataType == GDT_Byte && nBitDepth >= 0 &&
2✔
1508
                    nBitDepth <= 8)
1509
                {
1510
                    ClampValues(
1✔
1511
                        reinterpret_cast<GByte *>(pUpsampledSpectralBuffer) +
1512
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1513
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1514
                        static_cast<GByte>((1 << nBitDepth) - 1));
1✔
1515
                }
1516
                else if (eWorkDataType == GDT_UInt16 && nBitDepth >= 0 &&
1✔
1517
                         nBitDepth <= 16)
1518
                {
1519
                    ClampValues(
1✔
1520
                        reinterpret_cast<GUInt16 *>(pUpsampledSpectralBuffer) +
1✔
1521
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1522
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1523
                        static_cast<GUInt16>((1 << nBitDepth) - 1));
1✔
1524
                }
1525
#ifndef LIMIT_TYPES
1526
                else if (eWorkDataType == GDT_UInt32)
1527
                {
1528
                    ClampValues(reinterpret_cast<GUInt32*>(pUpsampledSpectralBuffer) +
1529
                                static_cast<size_t>(i) * nXSize * nYSize,
1530
                                static_cast<size_t>(nXSize)*nYSize,
1531
                                (static_cast<GUInt32>((1 << nBitDepth)-1));
1532
                }
1533
#endif
1534
            }
1535
        }
1536
    }
1537

1538
    const GUInt32 nMaxValue = (nBitDepth >= 0 && nBitDepth <= 31)
84✔
1539
                                  ? (1U << nBitDepth) - 1
168✔
1540
                                  : UINT32_MAX;
1541

1542
    double *padfTempBuffer = nullptr;
84✔
1543
    GDALDataType eBufDataTypeOri = eBufDataType;
84✔
1544
    void *pDataBufOri = pDataBuf;
84✔
1545
    // CFloat64 is the query type used by gdallocationinfo...
1546
#ifdef LIMIT_TYPES
1547
    if (eBufDataType != GDT_Byte && eBufDataType != GDT_UInt16)
84✔
1548
#else
1549
    if (eBufDataType == GDT_CFloat64)
1550
#endif
1551
    {
1552
        padfTempBuffer = static_cast<double *>(VSI_MALLOC3_VERBOSE(
43✔
1553
            nXSize, nYSize, psOptions->nOutPansharpenedBands * sizeof(double)));
1554
        if (padfTempBuffer == nullptr)
43✔
1555
        {
1556
            VSIFree(pUpsampledSpectralBuffer);
×
1557
            VSIFree(pPanBuffer);
×
1558
            return CE_Failure;
×
1559
        }
1560
        pDataBuf = padfTempBuffer;
43✔
1561
        eBufDataType = GDT_Float64;
43✔
1562
    }
1563

1564
    if (nTasks > 1)
84✔
1565
    {
1566
        std::vector<GDALPansharpenJob> asJobs;
74✔
1567
        asJobs.resize(nTasks);
37✔
1568
        GDALPansharpenJob *pasJobs = &(asJobs[0]);
37✔
1569
        {
1570
            std::vector<void *> ahJobData;
74✔
1571
            ahJobData.resize(nTasks);
37✔
1572
#ifdef DEBUG_TIMING
1573
            struct timeval tv;
1574
#endif
1575
            for (int i = 0; i < nTasks; i++)
185✔
1576
            {
1577
                const size_t iStartLine =
148✔
1578
                    (static_cast<size_t>(i) * nYSize) / nTasks;
148✔
1579
                const size_t iNextStartLine =
148✔
1580
                    (static_cast<size_t>(i + 1) * nYSize) / nTasks;
148✔
1581
                pasJobs[i].poPansharpenOperation = this;
148✔
1582
                pasJobs[i].eWorkDataType = eWorkDataType;
148✔
1583
                pasJobs[i].eBufDataType = eBufDataType;
148✔
1584
                pasJobs[i].pPanBuffer =
148✔
1585
                    pPanBuffer + iStartLine * nXSize * nDataTypeSize;
148✔
1586
                pasJobs[i].pUpsampledSpectralBuffer =
148✔
1587
                    pUpsampledSpectralBuffer +
148✔
1588
                    iStartLine * nXSize * nDataTypeSize;
148✔
1589
                pasJobs[i].pDataBuf =
148✔
1590
                    static_cast<GByte *>(pDataBuf) +
148✔
1591
                    iStartLine * nXSize *
296✔
1592
                        GDALGetDataTypeSizeBytes(eBufDataType);
148✔
1593
                pasJobs[i].nValues = (iNextStartLine - iStartLine) * nXSize;
148✔
1594
                pasJobs[i].nBandValues = static_cast<size_t>(nXSize) * nYSize;
148✔
1595
                pasJobs[i].nMaxValue = nMaxValue;
148✔
1596
#ifdef DEBUG_TIMING
1597
                pasJobs[i].ptv = &tv;
1598
#endif
1599
                ahJobData[i] = &(pasJobs[i]);
148✔
1600
            }
1601
#ifdef DEBUG_TIMING
1602
            gettimeofday(&tv, nullptr);
1603
#endif
1604
            poThreadPool->SubmitJobs(PansharpenJobThreadFunc, ahJobData);
37✔
1605
            poThreadPool->WaitCompletion();
37✔
1606
        }
1607

1608
        eErr = CE_None;
37✔
1609
        for (int i = 0; i < nTasks; i++)
185✔
1610
        {
1611
            if (pasJobs[i].eErr != CE_None)
148✔
1612
                eErr = CE_Failure;
×
1613
        }
1614
    }
1615
    else
1616
    {
1617
        eErr = PansharpenChunk(eWorkDataType, eBufDataType, pPanBuffer,
47✔
1618
                               pUpsampledSpectralBuffer, pDataBuf,
1619
                               static_cast<size_t>(nXSize) * nYSize,
47✔
1620
                               static_cast<size_t>(nXSize) * nYSize, nMaxValue);
47✔
1621
    }
1622

1623
    if (padfTempBuffer)
84✔
1624
    {
1625
        GDALCopyWords64(padfTempBuffer, GDT_Float64, sizeof(double),
43✔
1626
                        pDataBufOri, eBufDataTypeOri,
1627
                        GDALGetDataTypeSizeBytes(eBufDataTypeOri),
1628
                        static_cast<size_t>(nXSize) * nYSize *
43✔
1629
                            psOptions->nOutPansharpenedBands);
43✔
1630
        VSIFree(padfTempBuffer);
43✔
1631
    }
1632

1633
    VSIFree(pUpsampledSpectralBuffer);
84✔
1634
    VSIFree(pPanBuffer);
84✔
1635

1636
    return eErr;
84✔
1637
}
1638

1639
/************************************************************************/
1640
/*                   PansharpenResampleJobThreadFunc()                  */
1641
/************************************************************************/
1642

1643
void GDALPansharpenOperation::PansharpenResampleJobThreadFunc(void *pUserData)
148✔
1644
{
1645
    GDALPansharpenResampleJob *psJob =
148✔
1646
        static_cast<GDALPansharpenResampleJob *>(pUserData);
1647

1648
#ifdef DEBUG_TIMING
1649
    struct timeval tv;
1650
    gettimeofday(&tv, nullptr);
1651
    const GIntBig launch_time =
1652
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1653
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1654
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1655
                              static_cast<GIntBig>(tv.tv_usec);
1656
#endif
1657

1658
    GDALRasterIOExtraArg sExtraArg;
1659
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
148✔
1660
    // cppcheck-suppress redundantAssignment
1661
    sExtraArg.eResampleAlg = psJob->eResampleAlg;
148✔
1662
    sExtraArg.bFloatingPointWindowValidity = TRUE;
148✔
1663
    sExtraArg.dfXOff = psJob->dfXOff;
148✔
1664
    sExtraArg.dfYOff = psJob->dfYOff;
148✔
1665
    sExtraArg.dfXSize = psJob->dfXSize;
148✔
1666
    sExtraArg.dfYSize = psJob->dfYSize;
148✔
1667

1668
    std::vector<int> anBands;
296✔
1669
    for (int i = 0; i < psJob->nBandCount; ++i)
642✔
1670
        anBands.push_back(i + 1);
495✔
1671
    // This call to RasterIO() in a thread to poMEMDS shared between several
1672
    // threads is really risky, but works given the implementation details...
1673
    // Do not do this at home!
1674
    psJob->eErr = psJob->poMEMDS->RasterIO(
145✔
1675
        GF_Read, psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
1676
        psJob->pBuffer, psJob->nBufXSize, psJob->nBufYSize, psJob->eDT,
1677
        psJob->nBandCount, anBands.data(), 0, 0, psJob->nBandSpace, &sExtraArg);
147✔
1678
    if (CPLGetLastErrorType() == CE_Failure)
148✔
1679
        psJob->osLastErrorMsg = CPLGetLastErrorMsg();
×
1680

1681
#ifdef DEBUG_TIMING
1682
    struct timeval tv_end;
1683
    gettimeofday(&tv_end, nullptr);
1684
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1685
                        static_cast<GIntBig>(tv_end.tv_usec);
1686
    if (start_job - launch_time > 500)
1687
        /*ok*/ printf("Resample: Delay before start=" CPL_FRMT_GIB
1688
                      ", completion time=" CPL_FRMT_GIB "\n",
1689
                      start_job - launch_time, end - start_job);
1690
#endif
1691
}
148✔
1692

1693
/************************************************************************/
1694
/*                      PansharpenJobThreadFunc()                       */
1695
/************************************************************************/
1696

1697
void GDALPansharpenOperation::PansharpenJobThreadFunc(void *pUserData)
146✔
1698
{
1699
    GDALPansharpenJob *psJob = static_cast<GDALPansharpenJob *>(pUserData);
146✔
1700

1701
#ifdef DEBUG_TIMING
1702
    struct timeval tv;
1703
    gettimeofday(&tv, nullptr);
1704
    const GIntBig launch_time =
1705
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1706
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1707
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1708
                              static_cast<GIntBig>(tv.tv_usec);
1709
#endif
1710

1711
#if 0
1712
    for( int i = 0; i < 1000000; i++ )
1713
        acc += i * i;
1714
    psJob->eErr = CE_None;
1715
#else
1716
    psJob->eErr = psJob->poPansharpenOperation->PansharpenChunk(
146✔
1717
        psJob->eWorkDataType, psJob->eBufDataType, psJob->pPanBuffer,
1718
        psJob->pUpsampledSpectralBuffer, psJob->pDataBuf, psJob->nValues,
1719
        psJob->nBandValues, psJob->nMaxValue);
1720
#endif
1721

1722
#ifdef DEBUG_TIMING
1723
    struct timeval tv_end;
1724
    gettimeofday(&tv_end, nullptr);
1725
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1726
                        static_cast<GIntBig>(tv_end.tv_usec);
1727
    if (start_job - launch_time > 500)
1728
        /*ok*/ printf("Pansharpen: Delay before start=" CPL_FRMT_GIB
1729
                      ", completion time=" CPL_FRMT_GIB "\n",
1730
                      start_job - launch_time, end - start_job);
1731
#endif
1732
}
148✔
1733

1734
/************************************************************************/
1735
/*                           PansharpenChunk()                          */
1736
/************************************************************************/
1737

1738
CPLErr GDALPansharpenOperation::PansharpenChunk(
192✔
1739
    GDALDataType eWorkDataType, GDALDataType eBufDataType,
1740
    const void *pPanBuffer, const void *pUpsampledSpectralBuffer,
1741
    void *pDataBuf, size_t nValues, size_t nBandValues, GUInt32 nMaxValue) const
1742
{
1743
    CPLErr eErr = CE_None;
192✔
1744

1745
    switch (eWorkDataType)
192✔
1746
    {
1747
        case GDT_Byte:
80✔
1748
            eErr = WeightedBrovey(
80✔
1749
                static_cast<const GByte *>(pPanBuffer),
1750
                static_cast<const GByte *>(pUpsampledSpectralBuffer), pDataBuf,
1751
                eBufDataType, nValues, nBandValues,
1752
                static_cast<GByte>(nMaxValue));
1753
            break;
81✔
1754

1755
        case GDT_UInt16:
106✔
1756
            eErr = WeightedBrovey(
106✔
1757
                static_cast<const GUInt16 *>(pPanBuffer),
1758
                static_cast<const GUInt16 *>(pUpsampledSpectralBuffer),
1759
                pDataBuf, eBufDataType, nValues, nBandValues,
1760
                static_cast<GUInt16>(nMaxValue));
1761
            break;
108✔
1762

1763
#ifndef LIMIT_TYPES
1764
        case GDT_Int8:
1765
            eErr = WeightedBrovey(
1766
                static_cast<const GInt8 *>(pPanBuffer),
1767
                static_cast<const GInt8 *>(pUpsampledSpectralBuffer), pDataBuf,
1768
                eBufDataType, nValues, nBandValues);
1769
            break;
1770

1771
        case GDT_Int16:
1772
            eErr = WeightedBrovey(
1773
                static_cast<const GInt16 *>(pPanBuffer),
1774
                static_cast<const GInt16 *>(pUpsampledSpectralBuffer), pDataBuf,
1775
                eBufDataType, nValues, nBandValues);
1776
            break;
1777

1778
        case GDT_UInt32:
1779
            eErr = WeightedBrovey(
1780
                static_cast<const GUInt32 *>(pPanBuffer),
1781
                static_cast<const GUInt32 *>(pUpsampledSpectralBuffer),
1782
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1783
            break;
1784

1785
        case GDT_Int32:
1786
            eErr = WeightedBrovey(
1787
                static_cast<const GInt32 *>(pPanBuffer),
1788
                static_cast<const GInt32 *>(pUpsampledSpectralBuffer), pDataBuf,
1789
                eBufDataType, nValues, nBandValues);
1790
            break;
1791

1792
        case GDT_UInt64:
1793
            eErr = WeightedBrovey(
1794
                static_cast<const std::uint64_t *>(pPanBuffer),
1795
                static_cast<const std::uint64_t *>(pUpsampledSpectralBuffer),
1796
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1797
            break;
1798

1799
        case GDT_Int64:
1800
            eErr = WeightedBrovey(
1801
                static_cast<const std::int64_t *>(pPanBuffer),
1802
                static_cast<const std::int64_t *>(pUpsampledSpectralBuffer),
1803
                pDataBuf, eBufDataType, nValues, nBandValues);
1804
            break;
1805

1806
        case GDT_Float16:
1807
            eErr = WeightedBrovey(
1808
                static_cast<const GFloat16 *>(pPanBuffer),
1809
                static_cast<const GFloat16 *>(pUpsampledSpectralBuffer),
1810
                pDataBuf, eBufDataType, nValues, nBandValues);
1811
            break;
1812

1813
        case GDT_Float32:
1814
            eErr = WeightedBrovey(
1815
                static_cast<const float *>(pPanBuffer),
1816
                static_cast<const float *>(pUpsampledSpectralBuffer), pDataBuf,
1817
                eBufDataType, nValues, nBandValues);
1818
            break;
1819
#endif
1820
        case GDT_Float64:
6✔
1821
            eErr = WeightedBrovey(
6✔
1822
                static_cast<const double *>(pPanBuffer),
1823
                static_cast<const double *>(pUpsampledSpectralBuffer), pDataBuf,
1824
                eBufDataType, nValues, nBandValues);
1825
            break;
6✔
1826

1827
        default:
×
1828
            CPLError(CE_Failure, CPLE_NotSupported,
×
1829
                     "eWorkDataType not supported");
1830
            eErr = CE_Failure;
×
1831
            break;
×
1832
    }
1833

1834
    return eErr;
195✔
1835
}
1836

1837
/************************************************************************/
1838
/*                             GetOptions()                             */
1839
/************************************************************************/
1840

1841
/** Return options.
1842
 * @return options.
1843
 */
1844
GDALPansharpenOptions *GDALPansharpenOperation::GetOptions()
118✔
1845
{
1846
    return psOptions;
118✔
1847
}
1848

1849
/************************************************************************/
1850
/*                     GDALCreatePansharpenOperation()                  */
1851
/************************************************************************/
1852

1853
/** Instantiate a pansharpening operation.
1854
 *
1855
 * The passed options are validated.
1856
 *
1857
 * @param psOptions a pansharpening option structure allocated with
1858
 * GDALCreatePansharpenOptions(). It is duplicated by this function.
1859
 * @return a valid pansharpening operation handle, or NULL in case of failure.
1860
 *
1861
 * @since GDAL 2.1
1862
 */
1863

1864
GDALPansharpenOperationH
1865
GDALCreatePansharpenOperation(const GDALPansharpenOptions *psOptions)
×
1866
{
1867
    GDALPansharpenOperation *psOperation = new GDALPansharpenOperation();
×
1868
    if (psOperation->Initialize(psOptions) == CE_None)
×
1869
        return reinterpret_cast<GDALPansharpenOperationH>(psOperation);
×
1870
    delete psOperation;
×
1871
    return nullptr;
×
1872
}
1873

1874
/************************************************************************/
1875
/*                     GDALDestroyPansharpenOperation()                 */
1876
/************************************************************************/
1877

1878
/** Destroy a pansharpening operation.
1879
 *
1880
 * @param hOperation a valid pansharpening operation.
1881
 *
1882
 * @since GDAL 2.1
1883
 */
1884

1885
void GDALDestroyPansharpenOperation(GDALPansharpenOperationH hOperation)
×
1886
{
1887
    delete reinterpret_cast<GDALPansharpenOperation *>(hOperation);
×
1888
}
×
1889

1890
/************************************************************************/
1891
/*                       GDALPansharpenProcessRegion()                  */
1892
/************************************************************************/
1893

1894
/** Executes a pansharpening operation on a rectangular region of the
1895
 * resulting dataset.
1896
 *
1897
 * The window is expressed with respect to the dimensions of the panchromatic
1898
 * band.
1899
 *
1900
 * Spectral bands are upsampled and merged with the panchromatic band according
1901
 * to the select algorithm and options.
1902
 *
1903
 * @param hOperation a valid pansharpening operation.
1904
 * @param nXOff pixel offset.
1905
 * @param nYOff pixel offset.
1906
 * @param nXSize width of the pansharpened region to compute.
1907
 * @param nYSize height of the pansharpened region to compute.
1908
 * @param pDataBuf output buffer. Must be nXSize * nYSize *
1909
 *                 GDALGetDataTypeSizeBytes(eBufDataType) *
1910
 *                 psOptions->nOutPansharpenedBands large.
1911
 *                 It begins with all values of the first output band, followed
1912
 *                 by values of the second output band, etc...
1913
 * @param eBufDataType data type of the output buffer
1914
 *
1915
 * @return CE_None in case of success, CE_Failure in case of failure.
1916
 *
1917
 * @since GDAL 2.1
1918
 */
1919
CPLErr GDALPansharpenProcessRegion(GDALPansharpenOperationH hOperation,
×
1920
                                   int nXOff, int nYOff, int nXSize, int nYSize,
1921
                                   void *pDataBuf, GDALDataType eBufDataType)
1922
{
1923
    return reinterpret_cast<GDALPansharpenOperation *>(hOperation)
1924
        ->ProcessRegion(nXOff, nYOff, nXSize, nYSize, pDataBuf, eBufDataType);
×
1925
}
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