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

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

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

81.97
/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_multiproc.h"
30
#include "cpl_vsi.h"
31
#include "../frmts/mem/memdataset.h"
32
#include "../frmts/vrt/vrtdataset.h"
33
#include "gdal_priv.h"
34
#include "gdal_priv_templates.hpp"
35
// #include "gdalsse_priv.h"
36

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

403
    for (int i = 0; i < psOptions->nInputSpectralBands; i++)
187✔
404
    {
405
        aMSBands.push_back(
264✔
406
            GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]));
132✔
407
    }
408

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

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

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

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

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

508
        GDALGetResampleFunction(pszResampling, &nKernelRadius);
55✔
509
    }
510

511
    return CE_None;
55✔
512
}
513

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

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

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

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

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

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

585
    return panValue / dfPseudoPanchro;
20,117,440✔
586
}
587

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

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

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

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

618
    for (size_t j = 0; j < nValues; j++)
20,106,612✔
619
    {
620
        double dfFactor = 0.0;
20,207,420✔
621
        // if( pPanBuffer[j] == 0 )
622
        //     dfFactor = 1.0;
623
        // else
624
        {
625
            double dfPseudoPanchro = 0.0;
20,207,420✔
626
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
87,304,900✔
627
                dfPseudoPanchro +=
67,097,500✔
628
                    psOptions->padfWeights[i] *
67,097,500✔
629
                    pUpsampledSpectralBuffer[i * nBandValues + j];
67,097,500✔
630
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
20,207,420✔
631
        }
632

633
        for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
79,682,000✔
634
        {
635
            WorkDataType nRawValue =
59,575,500✔
636
                pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
59,575,500✔
637
                                             nBandValues +
59,575,500✔
638
                                         j];
639
            WorkDataType nPansharpenedValue;
640
            GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
59,575,500✔
641
            if constexpr (bHasBitDepth)
642
            {
643
                if (nPansharpenedValue > nMaxValue)
×
644
                    nPansharpenedValue = nMaxValue;
×
645
            }
646
            GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
57,039,200✔
647
        }
648
    }
649
}
650

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

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

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

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

681
    size_t j = 0;  // Used after for.
1,141✔
682
    for (; j + 3 < nValues; j += 4)
1,322,803✔
683
    {
684
        XMMReg4Double pseudoPanchro = zero;
1,322,762✔
685

686
        XMMReg4Double val0 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,326,951✔
687
                                                     0 * nBandValues + j);
769,622✔
688
        XMMReg4Double val1 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,326,693✔
689
                                                     1 * nBandValues + j);
1,326,693✔
690
        XMMReg4Double val2 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
1,325,922✔
691
                                                     2 * nBandValues + j);
1,325,922✔
692
        XMMReg4Double val3;
693
        if constexpr (NINPUT == 4 || NOUTPUT == 4)
694
        {
695
            val3 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
511,034✔
696
                                           3 * nBandValues + j);
500,656✔
697
        }
698

699
        pseudoPanchro += w0 * val0;
1,327,223✔
700
        pseudoPanchro += w1 * val1;
1,325,742✔
701
        pseudoPanchro += w2 * val2;
1,325,499✔
702
        if constexpr (NINPUT == 4)
703
            pseudoPanchro += w3 * val3;
510,662✔
704

705
        /* Little trick to avoid use of ternary operator due to one of the
706
         * branch being zero */
707
        XMMReg4Double factor = XMMReg4Double::And(
1,327,544✔
708
            XMMReg4Double::NotEquals(pseudoPanchro, zero),
709
            XMMReg4Double::Load4Val(pPanBuffer + j) / pseudoPanchro);
767,281✔
710

711
        val0 = XMMReg4Double::Min(val0 * factor, maxValue);
1,313,579✔
712
        val1 = XMMReg4Double::Min(val1 * factor, maxValue);
1,317,715✔
713
        val2 = XMMReg4Double::Min(val2 * factor, maxValue);
1,327,853✔
714
        if constexpr (NOUTPUT == 4)
715
        {
716
            val3 = XMMReg4Double::Min(val3 * factor, maxValue);
259,477✔
717
        }
718
        val0.Store4Val(pDataBuf + 0 * nBandValues + j);
1,328,711✔
719
        val1.Store4Val(pDataBuf + 1 * nBandValues + j);
1,322,557✔
720
        val2.Store4Val(pDataBuf + 2 * nBandValues + j);
1,322,125✔
721
        if constexpr (NOUTPUT == 4)
722
        {
723
            val3.Store4Val(pDataBuf + 3 * nBandValues + j);
250,356✔
724
        }
725
    }
726
    return j;
41✔
727
}
728

729
#else
730

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

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

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

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

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

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

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

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

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

802
    if (nMaxValue == 0)
47✔
803
        nMaxValue = std::numeric_limits<T>::max();
41✔
804
    size_t j;
805
    if (psOptions->nInputSpectralBands == 3 &&
47✔
806
        psOptions->nOutPansharpenedBands == 3 &&
33✔
807
        psOptions->panOutPansharpenedBands[0] == 0 &&
33✔
808
        psOptions->panOutPansharpenedBands[1] == 1 &&
33✔
809
        psOptions->panOutPansharpenedBands[2] == 2)
33✔
810
    {
811
        j = WeightedBroveyPositiveWeightsInternal<T, 3, 3>(
33✔
812
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
813
            nBandValues, nMaxValue);
814
    }
815
    else if (psOptions->nInputSpectralBands == 4 &&
14✔
816
             psOptions->nOutPansharpenedBands == 4 &&
7✔
817
             psOptions->panOutPansharpenedBands[0] == 0 &&
4✔
818
             psOptions->panOutPansharpenedBands[1] == 1 &&
4✔
819
             psOptions->panOutPansharpenedBands[2] == 2 &&
4✔
820
             psOptions->panOutPansharpenedBands[3] == 3)
4✔
821
    {
822
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 4>(
4✔
823
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
824
            nBandValues, nMaxValue);
825
    }
826
    else if (psOptions->nInputSpectralBands == 4 &&
10✔
827
             psOptions->nOutPansharpenedBands == 3 &&
4✔
828
             psOptions->panOutPansharpenedBands[0] == 0 &&
4✔
829
             psOptions->panOutPansharpenedBands[1] == 1 &&
3✔
830
             psOptions->panOutPansharpenedBands[2] == 2)
4✔
831
    {
832
        j = WeightedBroveyPositiveWeightsInternal<T, 4, 3>(
4✔
833
            pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
834
            nBandValues, nMaxValue);
835
    }
836
    else
837
    {
838
        for (j = 0; j + 1 < nValues; j += 2)
54✔
839
        {
840
            double dfFactor = 0.0;
48✔
841
            double dfFactor2 = 0.0;
48✔
842
            double dfPseudoPanchro = 0.0;
48✔
843
            double dfPseudoPanchro2 = 0.0;
48✔
844
            for (int i = 0; i < psOptions->nInputSpectralBands; i++)
96✔
845
            {
846
                dfPseudoPanchro +=
48✔
847
                    psOptions->padfWeights[i] *
48✔
848
                    pUpsampledSpectralBuffer[i * nBandValues + j];
48✔
849
                dfPseudoPanchro2 +=
48✔
850
                    psOptions->padfWeights[i] *
48✔
851
                    pUpsampledSpectralBuffer[i * nBandValues + j + 1];
48✔
852
            }
853

854
            dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
48✔
855
            dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
48✔
856

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1012
        case GDT_Float32:
1013
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1014
                           static_cast<float *>(pDataBuf), nValues, nBandValues,
1015
                           nMaxValue);
1016
            break;
1017
#endif
1018

1019
        case GDT_Float64:
124✔
1020
            WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
124✔
1021
                           static_cast<double *>(pDataBuf), nValues,
1022
                           nBandValues, nMaxValue);
1023
            break;
124✔
1024

1025
        default:
×
1026
            CPLError(CE_Failure, CPLE_NotSupported,
×
1027
                     "eBufDataType not supported");
1028
            return CE_Failure;
×
1029
            break;
1030
    }
1031

1032
    return CE_None;
173✔
1033
}
1034

1035
template <class WorkDataType>
1036
CPLErr GDALPansharpenOperation::WeightedBrovey(
6✔
1037
    const WorkDataType *pPanBuffer,
1038
    const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
1039
    GDALDataType eBufDataType, size_t nValues, size_t nBandValues) const
1040
{
1041
    switch (eBufDataType)
6✔
1042
    {
1043
        case GDT_Byte:
6✔
1044
            WeightedBrovey3<WorkDataType, GByte, FALSE>(
6✔
1045
                pPanBuffer, pUpsampledSpectralBuffer,
1046
                static_cast<GByte *>(pDataBuf), nValues, nBandValues, 0);
1047
            break;
6✔
1048

1049
        case GDT_UInt16:
×
1050
            WeightedBrovey3<WorkDataType, GUInt16, FALSE>(
×
1051
                pPanBuffer, pUpsampledSpectralBuffer,
1052
                static_cast<GUInt16 *>(pDataBuf), nValues, nBandValues, 0);
1053
            break;
×
1054

1055
#ifndef LIMIT_TYPES
1056
        case GDT_Int8:
1057
            WeightedBrovey3<WorkDataType, GInt8, FALSE>(
1058
                pPanBuffer, pUpsampledSpectralBuffer,
1059
                static_cast<GInt8 *>(pDataBuf), nValues, nBandValues, 0);
1060
            break;
1061

1062
        case GDT_Int16:
1063
            WeightedBrovey3<WorkDataType, GInt16, FALSE>(
1064
                pPanBuffer, pUpsampledSpectralBuffer,
1065
                static_cast<GInt16 *>(pDataBuf), nValues, nBandValues, 0);
1066
            break;
1067

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

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

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

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

1093
        case GDT_Float32:
1094
            WeightedBrovey3<WorkDataType, float, FALSE>(
1095
                pPanBuffer, pUpsampledSpectralBuffer,
1096
                static_cast<float *>(pDataBuf), nValues, nBandValues, 0);
1097
            break;
1098
#endif
1099

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

1106
        default:
×
1107
            CPLError(CE_Failure, CPLE_NotSupported,
×
1108
                     "eBufDataType not supported");
1109
            return CE_Failure;
×
1110
            break;
1111
    }
1112

1113
    return CE_None;
6✔
1114
}
1115

1116
/************************************************************************/
1117
/*                           ClampValues()                              */
1118
/************************************************************************/
1119

1120
template <class T>
1121
static void ClampValues(T *panBuffer, size_t nValues, T nMaxVal)
2✔
1122
{
1123
    for (size_t i = 0; i < nValues; i++)
34✔
1124
    {
1125
        if (panBuffer[i] > nMaxVal)
32✔
1126
            panBuffer[i] = nMaxVal;
8✔
1127
    }
1128
}
2✔
1129

1130
/************************************************************************/
1131
/*                         ProcessRegion()                              */
1132
/************************************************************************/
1133

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

1165
    // TODO: Avoid allocating buffers each time.
1166
    GDALRasterBand *poPanchroBand =
1167
        GDALRasterBand::FromHandle(psOptions->hPanchroBand);
80✔
1168
    GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
80✔
1169
#ifdef LIMIT_TYPES
1170
    if (eWorkDataType != GDT_Byte && eWorkDataType != GDT_UInt16)
80✔
1171
        eWorkDataType = GDT_Float64;
6✔
1172
#endif
1173
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eWorkDataType);
80✔
1174
    GByte *pUpsampledSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
80✔
1175
        nXSize, nYSize,
1176
        cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1177
    GByte *pPanBuffer = static_cast<GByte *>(
1178
        VSI_MALLOC3_VERBOSE(nXSize, nYSize, nDataTypeSize));
80✔
1179
    if (pUpsampledSpectralBuffer == nullptr || pPanBuffer == nullptr)
80✔
1180
    {
1181
        VSIFree(pUpsampledSpectralBuffer);
×
1182
        VSIFree(pPanBuffer);
×
1183
        return CE_Failure;
×
1184
    }
1185

1186
    CPLErr eErr = poPanchroBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
80✔
1187
                                          pPanBuffer, nXSize, nYSize,
1188
                                          eWorkDataType, 0, 0, nullptr);
1189
    if (eErr != CE_None)
80✔
1190
    {
1191
        VSIFree(pUpsampledSpectralBuffer);
×
1192
        VSIFree(pPanBuffer);
×
1193
        return CE_Failure;
×
1194
    }
1195

1196
    int nTasks = 0;
80✔
1197
    if (poThreadPool)
80✔
1198
    {
1199
        nTasks = poThreadPool->GetThreadCount();
33✔
1200
        if (nTasks > nYSize)
33✔
1201
            nTasks = nYSize;
×
1202
    }
1203

1204
    GDALRasterIOExtraArg sExtraArg;
1205
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
80✔
1206
    const GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
80✔
1207
    // cppcheck-suppress redundantAssignment
1208
    sExtraArg.eResampleAlg = eResampleAlg;
80✔
1209
    sExtraArg.bFloatingPointWindowValidity = TRUE;
80✔
1210
    sExtraArg.dfXOff = m_adfPanToMSGT[0] + nXOff * m_adfPanToMSGT[1];
80✔
1211
    sExtraArg.dfYOff = m_adfPanToMSGT[3] + nYOff * m_adfPanToMSGT[5];
80✔
1212
    sExtraArg.dfXSize = nXSize * m_adfPanToMSGT[1];
80✔
1213
    sExtraArg.dfYSize = nYSize * m_adfPanToMSGT[5];
80✔
1214
    if (sExtraArg.dfXOff + sExtraArg.dfXSize > aMSBands[0]->GetXSize())
80✔
1215
        sExtraArg.dfXOff = aMSBands[0]->GetXSize() - sExtraArg.dfXSize;
×
1216
    if (sExtraArg.dfYOff + sExtraArg.dfYSize > aMSBands[0]->GetYSize())
80✔
1217
        sExtraArg.dfYOff = aMSBands[0]->GetYSize() - sExtraArg.dfYSize;
×
1218
    int nSpectralXOff = static_cast<int>(sExtraArg.dfXOff);
80✔
1219
    int nSpectralYOff = static_cast<int>(sExtraArg.dfYOff);
80✔
1220
    int nSpectralXSize = static_cast<int>(0.49999 + sExtraArg.dfXSize);
80✔
1221
    int nSpectralYSize = static_cast<int>(0.49999 + sExtraArg.dfYSize);
80✔
1222
    if (nSpectralXSize == 0)
80✔
1223
        nSpectralXSize = 1;
10✔
1224
    if (nSpectralYSize == 0)
80✔
1225
        nSpectralYSize = 1;
10✔
1226

1227
    // When upsampling, extract the multispectral data at
1228
    // full resolution in a temp buffer, and then do the upsampling.
1229
    if (nSpectralXSize < nXSize && nSpectralYSize < nYSize &&
80✔
1230
        eResampleAlg != GRIORA_NearestNeighbour && nYSize > 1)
70✔
1231
    {
1232
        // Take some margin to take into account the radius of the
1233
        // resampling kernel.
1234
        int nXOffExtract = nSpectralXOff - nKernelRadius;
70✔
1235
        int nYOffExtract = nSpectralYOff - nKernelRadius;
70✔
1236
        int nXSizeExtract = nSpectralXSize + 1 + 2 * nKernelRadius;
70✔
1237
        int nYSizeExtract = nSpectralYSize + 1 + 2 * nKernelRadius;
70✔
1238
        if (nXOffExtract < 0)
70✔
1239
        {
1240
            nXSizeExtract += nXOffExtract;
65✔
1241
            nXOffExtract = 0;
65✔
1242
        }
1243
        if (nYOffExtract < 0)
70✔
1244
        {
1245
            nYSizeExtract += nYOffExtract;
58✔
1246
            nYOffExtract = 0;
58✔
1247
        }
1248
        if (nXOffExtract + nXSizeExtract > aMSBands[0]->GetXSize())
70✔
1249
            nXSizeExtract = aMSBands[0]->GetXSize() - nXOffExtract;
65✔
1250
        if (nYOffExtract + nYSizeExtract > aMSBands[0]->GetYSize())
70✔
1251
            nYSizeExtract = aMSBands[0]->GetYSize() - nYOffExtract;
58✔
1252

1253
        GByte *pSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
70✔
1254
            nXSizeExtract, nYSizeExtract,
1255
            cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1256
        if (pSpectralBuffer == nullptr)
70✔
1257
        {
1258
            VSIFree(pUpsampledSpectralBuffer);
×
1259
            VSIFree(pPanBuffer);
×
1260
            return CE_Failure;
×
1261
        }
1262

1263
        if (!anInputBands.empty())
70✔
1264
        {
1265
            // Use dataset RasterIO when possible.
1266
            eErr = aMSBands[0]->GetDataset()->RasterIO(
124✔
1267
                GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1268
                nYSizeExtract, pSpectralBuffer, nXSizeExtract, nYSizeExtract,
1269
                eWorkDataType, static_cast<int>(anInputBands.size()),
62✔
1270
                &anInputBands[0], 0, 0, 0, nullptr);
62✔
1271
        }
1272
        else
1273
        {
1274
            for (int i = 0;
8✔
1275
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
20✔
1276
            {
1277
                eErr = aMSBands[i]->RasterIO(
24✔
1278
                    GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1279
                    nYSizeExtract,
1280
                    pSpectralBuffer + static_cast<size_t>(i) * nXSizeExtract *
12✔
1281
                                          nYSizeExtract * nDataTypeSize,
12✔
1282
                    nXSizeExtract, nYSizeExtract, eWorkDataType, 0, 0, nullptr);
1283
            }
1284
        }
1285
        if (eErr != CE_None)
70✔
1286
        {
1287
            VSIFree(pSpectralBuffer);
×
1288
            VSIFree(pUpsampledSpectralBuffer);
×
1289
            VSIFree(pPanBuffer);
×
1290
            return CE_Failure;
×
1291
        }
1292

1293
        // Create a MEM dataset that wraps the input buffer.
1294
        auto poMEMDS = MEMDataset::Create("", nXSizeExtract, nYSizeExtract, 0,
70✔
1295
                                          eWorkDataType, nullptr);
1296

1297
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
282✔
1298
        {
1299
            GByte *pabyBuffer =
212✔
1300
                pSpectralBuffer + static_cast<size_t>(i) * nDataTypeSize *
212✔
1301
                                      nXSizeExtract * nYSizeExtract;
212✔
1302
            GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
212✔
1303
                poMEMDS, i + 1, pabyBuffer, eWorkDataType, 0, 0, false);
1304
            poMEMDS->AddMEMBand(hMEMBand);
212✔
1305

1306
            const char *pszNBITS =
1307
                aMSBands[i]->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
212✔
1308
            if (pszNBITS)
212✔
1309
                poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
4✔
1310
                    "NBITS", pszNBITS, "IMAGE_STRUCTURE");
4✔
1311

1312
            if (psOptions->bHasNoData)
212✔
1313
                poMEMDS->GetRasterBand(i + 1)->SetNoDataValue(
10✔
1314
                    psOptions->dfNoData);
10✔
1315
        }
1316

1317
        if (nTasks <= 1)
70✔
1318
        {
1319
            nSpectralXOff -= nXOffExtract;
37✔
1320
            nSpectralYOff -= nYOffExtract;
37✔
1321
            sExtraArg.dfXOff -= nXOffExtract;
37✔
1322
            sExtraArg.dfYOff -= nYOffExtract;
37✔
1323
            CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
37✔
1324
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1325
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1326
                eWorkDataType, psOptions->nInputSpectralBands, nullptr, 0, 0, 0,
37✔
1327
                &sExtraArg));
1328
        }
1329
        else
1330
        {
1331
            // We are abusing the contract of the GDAL API by using the
1332
            // MEMDataset from several threads. In this case, this is safe. In
1333
            // case, that would no longer be the case we could create as many
1334
            // MEMDataset as threads pointing to the same buffer.
1335

1336
            // To avoid races in threads, we query now the mask flags,
1337
            // so that implicit mask bands are created now.
1338
            for (int i = 0; i < poMEMDS->GetRasterCount(); i++)
150✔
1339
            {
1340
                poMEMDS->GetRasterBand(i + 1)->GetMaskFlags();
117✔
1341
            }
1342

1343
            std::vector<GDALPansharpenResampleJob> asJobs;
66✔
1344
            asJobs.resize(nTasks);
33✔
1345
            GDALPansharpenResampleJob *pasJobs = &(asJobs[0]);
33✔
1346
            {
1347
                std::vector<void *> ahJobData;
66✔
1348
                ahJobData.resize(nTasks);
33✔
1349

1350
#ifdef DEBUG_TIMING
1351
                struct timeval tv;
1352
#endif
1353
                for (int i = 0; i < nTasks; i++)
165✔
1354
                {
1355
                    const size_t iStartLine =
132✔
1356
                        (static_cast<size_t>(i) * nYSize) / nTasks;
132✔
1357
                    const size_t iNextStartLine =
132✔
1358
                        (static_cast<size_t>(i + 1) * nYSize) / nTasks;
132✔
1359
                    pasJobs[i].poMEMDS = poMEMDS;
132✔
1360
                    pasJobs[i].eResampleAlg = eResampleAlg;
132✔
1361
                    pasJobs[i].dfXOff = sExtraArg.dfXOff - nXOffExtract;
132✔
1362
                    pasJobs[i].dfYOff =
132✔
1363
                        m_adfPanToMSGT[3] +
132✔
1364
                        (nYOff + iStartLine) * m_adfPanToMSGT[5] - nYOffExtract;
132✔
1365
                    pasJobs[i].dfXSize = sExtraArg.dfXSize;
132✔
1366
                    pasJobs[i].dfYSize =
132✔
1367
                        (iNextStartLine - iStartLine) * m_adfPanToMSGT[5];
132✔
1368
                    if (pasJobs[i].dfXOff + pasJobs[i].dfXSize >
264✔
1369
                        aMSBands[0]->GetXSize())
132✔
1370
                    {
1371
                        pasJobs[i].dfXOff =
×
1372
                            aMSBands[0]->GetXSize() - pasJobs[i].dfXSize;
×
1373
                    }
1374
                    if (pasJobs[i].dfYOff + pasJobs[i].dfYSize >
264✔
1375
                        aMSBands[0]->GetYSize())
132✔
1376
                    {
1377
                        pasJobs[i].dfYOff =
×
1378
                            aMSBands[0]->GetYSize() - pasJobs[i].dfYSize;
×
1379
                    }
1380
                    pasJobs[i].nXOff = static_cast<int>(pasJobs[i].dfXOff);
132✔
1381
                    pasJobs[i].nYOff = static_cast<int>(pasJobs[i].dfYOff);
132✔
1382
                    pasJobs[i].nXSize =
132✔
1383
                        static_cast<int>(0.4999 + pasJobs[i].dfXSize);
132✔
1384
                    pasJobs[i].nYSize =
132✔
1385
                        static_cast<int>(0.4999 + pasJobs[i].dfYSize);
132✔
1386
                    if (pasJobs[i].nXSize == 0)
132✔
1387
                        pasJobs[i].nXSize = 1;
×
1388
                    if (pasJobs[i].nYSize == 0)
132✔
1389
                        pasJobs[i].nYSize = 1;
×
1390
                    pasJobs[i].pBuffer = pUpsampledSpectralBuffer +
132✔
1391
                                         static_cast<size_t>(iStartLine) *
132✔
1392
                                             nXSize * nDataTypeSize;
132✔
1393
                    pasJobs[i].eDT = eWorkDataType;
132✔
1394
                    pasJobs[i].nBufXSize = nXSize;
132✔
1395
                    pasJobs[i].nBufYSize =
132✔
1396
                        static_cast<int>(iNextStartLine - iStartLine);
132✔
1397
                    pasJobs[i].nBandCount = psOptions->nInputSpectralBands;
132✔
1398
                    pasJobs[i].nBandSpace =
132✔
1399
                        static_cast<GSpacing>(nXSize) * nYSize * nDataTypeSize;
132✔
1400
#ifdef DEBUG_TIMING
1401
                    pasJobs[i].ptv = &tv;
1402
#endif
1403
                    ahJobData[i] = &(pasJobs[i]);
132✔
1404
                }
1405
#ifdef DEBUG_TIMING
1406
                gettimeofday(&tv, nullptr);
1407
#endif
1408
                poThreadPool->SubmitJobs(PansharpenResampleJobThreadFunc,
33✔
1409
                                         ahJobData);
1410
                poThreadPool->WaitCompletion();
33✔
1411
            }
1412
        }
1413

1414
        GDALClose(poMEMDS);
70✔
1415

1416
        VSIFree(pSpectralBuffer);
70✔
1417
    }
1418
    else
1419
    {
1420
        if (!anInputBands.empty())
10✔
1421
        {
1422
            // Use dataset RasterIO when possible.
1423
            eErr = aMSBands[0]->GetDataset()->RasterIO(
20✔
1424
                GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1425
                nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1426
                eWorkDataType, static_cast<int>(anInputBands.size()),
10✔
1427
                &anInputBands[0], 0, 0, 0, &sExtraArg);
10✔
1428
        }
1429
        else
1430
        {
1431
            for (int i = 0;
×
1432
                 eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
×
1433
            {
1434
                eErr = aMSBands[i]->RasterIO(
×
1435
                    GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1436
                    nSpectralYSize,
1437
                    pUpsampledSpectralBuffer + static_cast<size_t>(i) * nXSize *
×
1438
                                                   nYSize * nDataTypeSize,
×
1439
                    nXSize, nYSize, eWorkDataType, 0, 0, &sExtraArg);
1440
            }
1441
        }
1442
        if (eErr != CE_None)
10✔
1443
        {
1444
            VSIFree(pUpsampledSpectralBuffer);
×
1445
            VSIFree(pPanBuffer);
×
1446
            return CE_Failure;
×
1447
        }
1448
    }
1449

1450
    // In case NBITS was not set on the spectral bands, clamp the values
1451
    // if overshoot might have occurred.
1452
    int nBitDepth = psOptions->nBitDepth;
80✔
1453
    if (nBitDepth &&
80✔
1454
        (eResampleAlg == GRIORA_Cubic || eResampleAlg == GRIORA_CubicSpline ||
×
1455
         eResampleAlg == GRIORA_Lanczos))
1456
    {
1457
        for (int i = 0; i < psOptions->nInputSpectralBands; i++)
12✔
1458
        {
1459
            GDALRasterBand *poBand = aMSBands[i];
6✔
1460
            int nBandBitDepth = 0;
6✔
1461
            const char *pszNBITS =
1462
                poBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
6✔
1463
            if (pszNBITS)
6✔
1464
                nBandBitDepth = atoi(pszNBITS);
4✔
1465
            if (nBandBitDepth < nBitDepth)
6✔
1466
            {
1467
                if (eWorkDataType == GDT_Byte && nBitDepth >= 0 &&
2✔
1468
                    nBitDepth <= 8)
1469
                {
1470
                    ClampValues(
1✔
1471
                        reinterpret_cast<GByte *>(pUpsampledSpectralBuffer) +
1472
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1473
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1474
                        static_cast<GByte>((1 << nBitDepth) - 1));
1✔
1475
                }
1476
                else if (eWorkDataType == GDT_UInt16 && nBitDepth >= 0 &&
1✔
1477
                         nBitDepth <= 16)
1478
                {
1479
                    ClampValues(
1✔
1480
                        reinterpret_cast<GUInt16 *>(pUpsampledSpectralBuffer) +
1✔
1481
                            static_cast<size_t>(i) * nXSize * nYSize,
1✔
1482
                        static_cast<size_t>(nXSize) * nYSize,
1✔
1483
                        static_cast<GUInt16>((1 << nBitDepth) - 1));
1✔
1484
                }
1485
#ifndef LIMIT_TYPES
1486
                else if (eWorkDataType == GDT_UInt32)
1487
                {
1488
                    ClampValues(reinterpret_cast<GUInt32*>(pUpsampledSpectralBuffer) +
1489
                                static_cast<size_t>(i) * nXSize * nYSize,
1490
                                static_cast<size_t>(nXSize)*nYSize,
1491
                                (static_cast<GUInt32>((1 << nBitDepth)-1));
1492
                }
1493
#endif
1494
            }
1495
        }
1496
    }
1497

1498
    const GUInt32 nMaxValue = (nBitDepth >= 0 && nBitDepth <= 31)
80✔
1499
                                  ? (1U << nBitDepth) - 1
160✔
1500
                                  : UINT32_MAX;
1501

1502
    double *padfTempBuffer = nullptr;
80✔
1503
    GDALDataType eBufDataTypeOri = eBufDataType;
80✔
1504
    void *pDataBufOri = pDataBuf;
80✔
1505
    // CFloat64 is the query type used by gdallocationinfo...
1506
#ifdef LIMIT_TYPES
1507
    if (eBufDataType != GDT_Byte && eBufDataType != GDT_UInt16)
80✔
1508
#else
1509
    if (eBufDataType == GDT_CFloat64)
1510
#endif
1511
    {
1512
        padfTempBuffer = static_cast<double *>(VSI_MALLOC3_VERBOSE(
43✔
1513
            nXSize, nYSize, psOptions->nOutPansharpenedBands * sizeof(double)));
1514
        if (padfTempBuffer == nullptr)
43✔
1515
        {
1516
            VSIFree(pUpsampledSpectralBuffer);
×
1517
            VSIFree(pPanBuffer);
×
1518
            return CE_Failure;
×
1519
        }
1520
        pDataBuf = padfTempBuffer;
43✔
1521
        eBufDataType = GDT_Float64;
43✔
1522
    }
1523

1524
    if (nTasks > 1)
80✔
1525
    {
1526
        std::vector<GDALPansharpenJob> asJobs;
66✔
1527
        asJobs.resize(nTasks);
33✔
1528
        GDALPansharpenJob *pasJobs = &(asJobs[0]);
33✔
1529
        {
1530
            std::vector<void *> ahJobData;
66✔
1531
            ahJobData.resize(nTasks);
33✔
1532
#ifdef DEBUG_TIMING
1533
            struct timeval tv;
1534
#endif
1535
            for (int i = 0; i < nTasks; i++)
165✔
1536
            {
1537
                const size_t iStartLine =
132✔
1538
                    (static_cast<size_t>(i) * nYSize) / nTasks;
132✔
1539
                const size_t iNextStartLine =
132✔
1540
                    (static_cast<size_t>(i + 1) * nYSize) / nTasks;
132✔
1541
                pasJobs[i].poPansharpenOperation = this;
132✔
1542
                pasJobs[i].eWorkDataType = eWorkDataType;
132✔
1543
                pasJobs[i].eBufDataType = eBufDataType;
132✔
1544
                pasJobs[i].pPanBuffer =
132✔
1545
                    pPanBuffer + iStartLine * nXSize * nDataTypeSize;
132✔
1546
                pasJobs[i].pUpsampledSpectralBuffer =
132✔
1547
                    pUpsampledSpectralBuffer +
132✔
1548
                    iStartLine * nXSize * nDataTypeSize;
132✔
1549
                pasJobs[i].pDataBuf =
132✔
1550
                    static_cast<GByte *>(pDataBuf) +
132✔
1551
                    iStartLine * nXSize *
264✔
1552
                        GDALGetDataTypeSizeBytes(eBufDataType);
132✔
1553
                pasJobs[i].nValues = (iNextStartLine - iStartLine) * nXSize;
132✔
1554
                pasJobs[i].nBandValues = static_cast<size_t>(nXSize) * nYSize;
132✔
1555
                pasJobs[i].nMaxValue = nMaxValue;
132✔
1556
#ifdef DEBUG_TIMING
1557
                pasJobs[i].ptv = &tv;
1558
#endif
1559
                ahJobData[i] = &(pasJobs[i]);
132✔
1560
            }
1561
#ifdef DEBUG_TIMING
1562
            gettimeofday(&tv, nullptr);
1563
#endif
1564
            poThreadPool->SubmitJobs(PansharpenJobThreadFunc, ahJobData);
33✔
1565
            poThreadPool->WaitCompletion();
33✔
1566
        }
1567

1568
        eErr = CE_None;
33✔
1569
        for (int i = 0; i < nTasks; i++)
165✔
1570
        {
1571
            if (pasJobs[i].eErr != CE_None)
132✔
1572
                eErr = CE_Failure;
×
1573
        }
1574
    }
1575
    else
1576
    {
1577
        eErr = PansharpenChunk(eWorkDataType, eBufDataType, pPanBuffer,
47✔
1578
                               pUpsampledSpectralBuffer, pDataBuf,
1579
                               static_cast<size_t>(nXSize) * nYSize,
47✔
1580
                               static_cast<size_t>(nXSize) * nYSize, nMaxValue);
47✔
1581
    }
1582

1583
    if (padfTempBuffer)
80✔
1584
    {
1585
        GDALCopyWords64(padfTempBuffer, GDT_Float64, sizeof(double),
43✔
1586
                        pDataBufOri, eBufDataTypeOri,
1587
                        GDALGetDataTypeSizeBytes(eBufDataTypeOri),
1588
                        static_cast<size_t>(nXSize) * nYSize *
43✔
1589
                            psOptions->nOutPansharpenedBands);
43✔
1590
        VSIFree(padfTempBuffer);
43✔
1591
    }
1592

1593
    VSIFree(pUpsampledSpectralBuffer);
80✔
1594
    VSIFree(pPanBuffer);
80✔
1595

1596
    return eErr;
80✔
1597
}
1598

1599
/************************************************************************/
1600
/*                   PansharpenResampleJobThreadFunc()                  */
1601
/************************************************************************/
1602

1603
// static int acc=0;
1604

1605
void GDALPansharpenOperation::PansharpenResampleJobThreadFunc(void *pUserData)
131✔
1606
{
1607
    GDALPansharpenResampleJob *psJob =
131✔
1608
        static_cast<GDALPansharpenResampleJob *>(pUserData);
1609

1610
#ifdef DEBUG_TIMING
1611
    struct timeval tv;
1612
    gettimeofday(&tv, nullptr);
1613
    const GIntBig launch_time =
1614
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1615
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1616
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1617
                              static_cast<GIntBig>(tv.tv_usec);
1618
#endif
1619

1620
#if 0
1621
    for(int i=0;i<1000000;i++)
1622
        acc += i * i;
1623
#else
1624
    GDALRasterIOExtraArg sExtraArg;
1625
    INIT_RASTERIO_EXTRA_ARG(sExtraArg);
131✔
1626
    // cppcheck-suppress redundantAssignment
1627
    sExtraArg.eResampleAlg = psJob->eResampleAlg;
131✔
1628
    sExtraArg.bFloatingPointWindowValidity = TRUE;
131✔
1629
    sExtraArg.dfXOff = psJob->dfXOff;
131✔
1630
    sExtraArg.dfYOff = psJob->dfYOff;
131✔
1631
    sExtraArg.dfXSize = psJob->dfXSize;
131✔
1632
    sExtraArg.dfYSize = psJob->dfYSize;
131✔
1633

1634
    std::vector<int> anBands;
131✔
1635
    for (int i = 0; i < psJob->nBandCount; ++i)
599✔
1636
        anBands.push_back(i + 1);
467✔
1637
    // This call to RasterIO() in a thread to poMEMDS shared between several
1638
    // threads is really risky, but works given the implementation details...
1639
    // Do not do this at home!
1640
    CPL_IGNORE_RET_VAL(psJob->poMEMDS->RasterIO(
130✔
1641
        GF_Read, psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
1642
        psJob->pBuffer, psJob->nBufXSize, psJob->nBufYSize, psJob->eDT,
1643
        psJob->nBandCount, anBands.data(), 0, 0, psJob->nBandSpace,
132✔
1644
        &sExtraArg));
1645
#endif
1646

1647
#ifdef DEBUG_TIMING
1648
    struct timeval tv_end;
1649
    gettimeofday(&tv_end, nullptr);
1650
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1651
                        static_cast<GIntBig>(tv_end.tv_usec);
1652
    if (start_job - launch_time > 500)
1653
        /*ok*/ printf("Resample: Delay before start=" CPL_FRMT_GIB
1654
                      ", completion time=" CPL_FRMT_GIB "\n",
1655
                      start_job - launch_time, end - start_job);
1656
#endif
1657
}
132✔
1658

1659
/************************************************************************/
1660
/*                      PansharpenJobThreadFunc()                       */
1661
/************************************************************************/
1662

1663
void GDALPansharpenOperation::PansharpenJobThreadFunc(void *pUserData)
130✔
1664
{
1665
    GDALPansharpenJob *psJob = static_cast<GDALPansharpenJob *>(pUserData);
130✔
1666

1667
#ifdef DEBUG_TIMING
1668
    struct timeval tv;
1669
    gettimeofday(&tv, nullptr);
1670
    const GIntBig launch_time =
1671
        static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1672
        static_cast<GIntBig>(psJob->ptv->tv_usec);
1673
    const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1674
                              static_cast<GIntBig>(tv.tv_usec);
1675
#endif
1676

1677
#if 0
1678
    for( int i = 0; i < 1000000; i++ )
1679
        acc += i * i;
1680
    psJob->eErr = CE_None;
1681
#else
1682
    psJob->eErr = psJob->poPansharpenOperation->PansharpenChunk(
130✔
1683
        psJob->eWorkDataType, psJob->eBufDataType, psJob->pPanBuffer,
1684
        psJob->pUpsampledSpectralBuffer, psJob->pDataBuf, psJob->nValues,
1685
        psJob->nBandValues, psJob->nMaxValue);
1686
#endif
1687

1688
#ifdef DEBUG_TIMING
1689
    struct timeval tv_end;
1690
    gettimeofday(&tv_end, nullptr);
1691
    const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1692
                        static_cast<GIntBig>(tv_end.tv_usec);
1693
    if (start_job - launch_time > 500)
1694
        /*ok*/ printf("Pansharpen: Delay before start=" CPL_FRMT_GIB
1695
                      ", completion time=" CPL_FRMT_GIB "\n",
1696
                      start_job - launch_time, end - start_job);
1697
#endif
1698
}
132✔
1699

1700
/************************************************************************/
1701
/*                           PansharpenChunk()                          */
1702
/************************************************************************/
1703

1704
CPLErr GDALPansharpenOperation::PansharpenChunk(
177✔
1705
    GDALDataType eWorkDataType, GDALDataType eBufDataType,
1706
    const void *pPanBuffer, const void *pUpsampledSpectralBuffer,
1707
    void *pDataBuf, size_t nValues, size_t nBandValues, GUInt32 nMaxValue) const
1708
{
1709
    CPLErr eErr = CE_None;
177✔
1710

1711
    switch (eWorkDataType)
177✔
1712
    {
1713
        case GDT_Byte:
65✔
1714
            eErr = WeightedBrovey(
65✔
1715
                static_cast<const GByte *>(pPanBuffer),
1716
                static_cast<const GByte *>(pUpsampledSpectralBuffer), pDataBuf,
1717
                eBufDataType, nValues, nBandValues,
1718
                static_cast<GByte>(nMaxValue));
1719
            break;
65✔
1720

1721
        case GDT_UInt16:
106✔
1722
            eErr = WeightedBrovey(
106✔
1723
                static_cast<const GUInt16 *>(pPanBuffer),
1724
                static_cast<const GUInt16 *>(pUpsampledSpectralBuffer),
1725
                pDataBuf, eBufDataType, nValues, nBandValues,
1726
                static_cast<GUInt16>(nMaxValue));
1727
            break;
108✔
1728

1729
#ifndef LIMIT_TYPES
1730
        case GDT_Int8:
1731
            eErr = WeightedBrovey(
1732
                static_cast<const GInt8 *>(pPanBuffer),
1733
                static_cast<const GInt8 *>(pUpsampledSpectralBuffer), pDataBuf,
1734
                eBufDataType, nValues, nBandValues);
1735
            break;
1736

1737
        case GDT_Int16:
1738
            eErr = WeightedBrovey(
1739
                static_cast<const GInt16 *>(pPanBuffer),
1740
                static_cast<const GInt16 *>(pUpsampledSpectralBuffer), pDataBuf,
1741
                eBufDataType, nValues, nBandValues);
1742
            break;
1743

1744
        case GDT_UInt32:
1745
            eErr = WeightedBrovey(
1746
                static_cast<const GUInt32 *>(pPanBuffer),
1747
                static_cast<const GUInt32 *>(pUpsampledSpectralBuffer),
1748
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1749
            break;
1750

1751
        case GDT_Int32:
1752
            eErr = WeightedBrovey(
1753
                static_cast<const GInt32 *>(pPanBuffer),
1754
                static_cast<const GInt32 *>(pUpsampledSpectralBuffer), pDataBuf,
1755
                eBufDataType, nValues, nBandValues);
1756
            break;
1757

1758
        case GDT_UInt64:
1759
            eErr = WeightedBrovey(
1760
                static_cast<const std::uint64_t *>(pPanBuffer),
1761
                static_cast<const std::uint64_t *>(pUpsampledSpectralBuffer),
1762
                pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1763
            break;
1764

1765
        case GDT_Int64:
1766
            eErr = WeightedBrovey(
1767
                static_cast<const std::int64_t *>(pPanBuffer),
1768
                static_cast<const std::int64_t *>(pUpsampledSpectralBuffer),
1769
                pDataBuf, eBufDataType, nValues, nBandValues);
1770
            break;
1771

1772
        case GDT_Float32:
1773
            eErr = WeightedBrovey(
1774
                static_cast<const float *>(pPanBuffer),
1775
                static_cast<const float *>(pUpsampledSpectralBuffer), pDataBuf,
1776
                eBufDataType, nValues, nBandValues);
1777
            break;
1778
#endif
1779
        case GDT_Float64:
6✔
1780
            eErr = WeightedBrovey(
6✔
1781
                static_cast<const double *>(pPanBuffer),
1782
                static_cast<const double *>(pUpsampledSpectralBuffer), pDataBuf,
1783
                eBufDataType, nValues, nBandValues);
1784
            break;
6✔
1785

1786
        default:
×
1787
            CPLError(CE_Failure, CPLE_NotSupported,
×
1788
                     "eWorkDataType not supported");
1789
            eErr = CE_Failure;
×
1790
            break;
×
1791
    }
1792

1793
    return eErr;
179✔
1794
}
1795

1796
/************************************************************************/
1797
/*                             GetOptions()                             */
1798
/************************************************************************/
1799

1800
/** Return options.
1801
 * @return options.
1802
 */
1803
GDALPansharpenOptions *GDALPansharpenOperation::GetOptions()
110✔
1804
{
1805
    return psOptions;
110✔
1806
}
1807

1808
/************************************************************************/
1809
/*                     GDALCreatePansharpenOperation()                  */
1810
/************************************************************************/
1811

1812
/** Instantiate a pansharpening operation.
1813
 *
1814
 * The passed options are validated.
1815
 *
1816
 * @param psOptions a pansharpening option structure allocated with
1817
 * GDALCreatePansharpenOptions(). It is duplicated by this function.
1818
 * @return a valid pansharpening operation handle, or NULL in case of failure.
1819
 *
1820
 * @since GDAL 2.1
1821
 */
1822

1823
GDALPansharpenOperationH
1824
GDALCreatePansharpenOperation(const GDALPansharpenOptions *psOptions)
×
1825
{
1826
    GDALPansharpenOperation *psOperation = new GDALPansharpenOperation();
×
1827
    if (psOperation->Initialize(psOptions) == CE_None)
×
1828
        return reinterpret_cast<GDALPansharpenOperationH>(psOperation);
×
1829
    delete psOperation;
×
1830
    return nullptr;
×
1831
}
1832

1833
/************************************************************************/
1834
/*                     GDALDestroyPansharpenOperation()                 */
1835
/************************************************************************/
1836

1837
/** Destroy a pansharpening operation.
1838
 *
1839
 * @param hOperation a valid pansharpening operation.
1840
 *
1841
 * @since GDAL 2.1
1842
 */
1843

1844
void GDALDestroyPansharpenOperation(GDALPansharpenOperationH hOperation)
×
1845
{
1846
    delete reinterpret_cast<GDALPansharpenOperation *>(hOperation);
×
1847
}
×
1848

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

1853
/** Executes a pansharpening operation on a rectangular region of the
1854
 * resulting dataset.
1855
 *
1856
 * The window is expressed with respect to the dimensions of the panchromatic
1857
 * band.
1858
 *
1859
 * Spectral bands are upsampled and merged with the panchromatic band according
1860
 * to the select algorithm and options.
1861
 *
1862
 * @param hOperation a valid pansharpening operation.
1863
 * @param nXOff pixel offset.
1864
 * @param nYOff pixel offset.
1865
 * @param nXSize width of the pansharpened region to compute.
1866
 * @param nYSize height of the pansharpened region to compute.
1867
 * @param pDataBuf output buffer. Must be nXSize * nYSize *
1868
 *                 GDALGetDataTypeSizeBytes(eBufDataType) *
1869
 *                 psOptions->nOutPansharpenedBands large.
1870
 *                 It begins with all values of the first output band, followed
1871
 *                 by values of the second output band, etc...
1872
 * @param eBufDataType data type of the output buffer
1873
 *
1874
 * @return CE_None in case of success, CE_Failure in case of failure.
1875
 *
1876
 * @since GDAL 2.1
1877
 */
1878
CPLErr GDALPansharpenProcessRegion(GDALPansharpenOperationH hOperation,
×
1879
                                   int nXOff, int nYOff, int nXSize, int nYSize,
1880
                                   void *pDataBuf, GDALDataType eBufDataType)
1881
{
1882
    return reinterpret_cast<GDALPansharpenOperation *>(hOperation)
1883
        ->ProcessRegion(nXOff, nYOff, nXSize, nYSize, pDataBuf, eBufDataType);
×
1884
}
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