Line data Source code
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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "cpl_worker_thread_pool.h"
32 : #include "gdalpansharpen.h"
33 :
34 : #include <algorithm>
35 : #include <array>
36 : #include <cstddef>
37 : #include <cstdio>
38 : #include <cstdlib>
39 : #include <cstring>
40 : #include <limits>
41 : #include <new>
42 :
43 : #include "cpl_conv.h"
44 : #include "cpl_error.h"
45 : #include "cpl_multiproc.h"
46 : #include "cpl_vsi.h"
47 : #include "../frmts/mem/memdataset.h"
48 : #include "../frmts/vrt/vrtdataset.h"
49 : #include "gdal_priv.h"
50 : #include "gdal_priv_templates.hpp"
51 : // #include "gdalsse_priv.h"
52 :
53 : // Limit types to practical use cases.
54 : #define LIMIT_TYPES 1
55 :
56 : /************************************************************************/
57 : /* GDALCreatePansharpenOptions() */
58 : /************************************************************************/
59 :
60 : /** Create pansharpening options.
61 : *
62 : * @return a newly allocated pansharpening option structure that must be freed
63 : * with GDALDestroyPansharpenOptions().
64 : *
65 : * @since GDAL 2.1
66 : */
67 :
68 111 : GDALPansharpenOptions *GDALCreatePansharpenOptions()
69 : {
70 : GDALPansharpenOptions *psOptions = static_cast<GDALPansharpenOptions *>(
71 111 : CPLCalloc(1, sizeof(GDALPansharpenOptions)));
72 111 : psOptions->ePansharpenAlg = GDAL_PSH_WEIGHTED_BROVEY;
73 111 : psOptions->eResampleAlg = GRIORA_Cubic;
74 111 : return psOptions;
75 : }
76 :
77 : /************************************************************************/
78 : /* GDALDestroyPansharpenOptions() */
79 : /************************************************************************/
80 :
81 : /** Destroy pansharpening options.
82 : *
83 : * @param psOptions a pansharpening option structure allocated with
84 : * GDALCreatePansharpenOptions()
85 : *
86 : * @since GDAL 2.1
87 : */
88 :
89 112 : void GDALDestroyPansharpenOptions(GDALPansharpenOptions *psOptions)
90 : {
91 112 : if (psOptions == nullptr)
92 1 : return;
93 111 : CPLFree(psOptions->padfWeights);
94 111 : CPLFree(psOptions->pahInputSpectralBands);
95 111 : CPLFree(psOptions->panOutPansharpenedBands);
96 111 : CPLFree(psOptions);
97 : }
98 :
99 : /************************************************************************/
100 : /* GDALClonePansharpenOptions() */
101 : /************************************************************************/
102 :
103 : /** Clone pansharpening options.
104 : *
105 : * @param psOptions a pansharpening option structure allocated with
106 : * GDALCreatePansharpenOptions()
107 : * @return a newly allocated pansharpening option structure that must be freed
108 : * with GDALDestroyPansharpenOptions().
109 : *
110 : * @since GDAL 2.1
111 : */
112 :
113 : GDALPansharpenOptions *
114 58 : GDALClonePansharpenOptions(const GDALPansharpenOptions *psOptions)
115 : {
116 58 : GDALPansharpenOptions *psNewOptions = GDALCreatePansharpenOptions();
117 58 : psNewOptions->ePansharpenAlg = psOptions->ePansharpenAlg;
118 58 : psNewOptions->eResampleAlg = psOptions->eResampleAlg;
119 58 : psNewOptions->nBitDepth = psOptions->nBitDepth;
120 58 : psNewOptions->nWeightCount = psOptions->nWeightCount;
121 58 : if (psOptions->padfWeights)
122 : {
123 58 : psNewOptions->padfWeights = static_cast<double *>(
124 58 : CPLMalloc(sizeof(double) * psOptions->nWeightCount));
125 58 : memcpy(psNewOptions->padfWeights, psOptions->padfWeights,
126 58 : sizeof(double) * psOptions->nWeightCount);
127 : }
128 58 : psNewOptions->hPanchroBand = psOptions->hPanchroBand;
129 58 : psNewOptions->nInputSpectralBands = psOptions->nInputSpectralBands;
130 58 : if (psOptions->pahInputSpectralBands)
131 : {
132 58 : const size_t nSize =
133 58 : sizeof(GDALRasterBandH) * psOptions->nInputSpectralBands;
134 58 : psNewOptions->pahInputSpectralBands =
135 58 : static_cast<GDALRasterBandH *>(CPLMalloc(nSize));
136 58 : memcpy(psNewOptions->pahInputSpectralBands,
137 58 : psOptions->pahInputSpectralBands, nSize);
138 : }
139 58 : psNewOptions->nOutPansharpenedBands = psOptions->nOutPansharpenedBands;
140 58 : if (psOptions->panOutPansharpenedBands)
141 : {
142 57 : psNewOptions->panOutPansharpenedBands = static_cast<int *>(
143 57 : CPLMalloc(sizeof(int) * psOptions->nOutPansharpenedBands));
144 57 : memcpy(psNewOptions->panOutPansharpenedBands,
145 57 : psOptions->panOutPansharpenedBands,
146 57 : sizeof(int) * psOptions->nOutPansharpenedBands);
147 : }
148 58 : psNewOptions->bHasNoData = psOptions->bHasNoData;
149 58 : psNewOptions->dfNoData = psOptions->dfNoData;
150 58 : psNewOptions->nThreads = psOptions->nThreads;
151 58 : return psNewOptions;
152 : }
153 :
154 : /************************************************************************/
155 : /* GDALPansharpenOperation() */
156 : /************************************************************************/
157 :
158 : /** Pansharpening operation constructor.
159 : *
160 : * The object is ready to be used after Initialize() has been called.
161 : */
162 : GDALPansharpenOperation::GDALPansharpenOperation() = default;
163 :
164 : /************************************************************************/
165 : /* ~GDALPansharpenOperation() */
166 : /************************************************************************/
167 :
168 : /** Pansharpening operation destructor.
169 : */
170 :
171 56 : GDALPansharpenOperation::~GDALPansharpenOperation()
172 : {
173 56 : GDALDestroyPansharpenOptions(psOptions);
174 63 : for (size_t i = 0; i < aVDS.size(); i++)
175 7 : delete aVDS[i];
176 56 : delete poThreadPool;
177 56 : }
178 :
179 : /************************************************************************/
180 : /* Initialize() */
181 : /************************************************************************/
182 :
183 : /** Initialize the pansharpening operation.
184 : *
185 : * @param psOptionsIn pansharpening options. Must not be NULL.
186 : *
187 : * @return CE_None in case of success, CE_Failure in case of failure.
188 : */
189 : CPLErr
190 56 : GDALPansharpenOperation::Initialize(const GDALPansharpenOptions *psOptionsIn)
191 : {
192 56 : if (psOptionsIn->hPanchroBand == nullptr)
193 : {
194 0 : CPLError(CE_Failure, CPLE_AppDefined, "hPanchroBand not set");
195 0 : return CE_Failure;
196 : }
197 56 : if (psOptionsIn->nInputSpectralBands <= 0)
198 : {
199 0 : CPLError(CE_Failure, CPLE_AppDefined,
200 : "No input spectral bands defined");
201 0 : return CE_Failure;
202 : }
203 56 : if (psOptionsIn->padfWeights == nullptr ||
204 56 : psOptionsIn->nWeightCount != psOptionsIn->nInputSpectralBands)
205 : {
206 0 : CPLError(CE_Failure, CPLE_AppDefined,
207 : "No weights defined, or not the same number as input "
208 : "spectral bands");
209 0 : return CE_Failure;
210 : }
211 :
212 56 : auto poPanchroBand = GDALRasterBand::FromHandle(psOptionsIn->hPanchroBand);
213 56 : auto poPanchroDS = poPanchroBand->GetDataset();
214 56 : if (poPanchroDS == nullptr)
215 : {
216 0 : CPLError(CE_Failure, CPLE_AppDefined,
217 : "Cannot retrieve dataset associated with hPanchroBand");
218 0 : return CE_Failure;
219 : }
220 : // Make sure that the band is really a first level child of the owning dataset
221 56 : if (poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != poPanchroBand)
222 : {
223 0 : CPLError(CE_Failure, CPLE_AppDefined,
224 : "poPanchroDS->GetRasterBand(poPanchroBand->GetBand()) != "
225 : "poPanchroBand");
226 0 : return CE_Failure;
227 : }
228 : std::array<double, 6> adfPanchroGT;
229 56 : if (poPanchroDS->GetGeoTransform(adfPanchroGT.data()) != CE_None)
230 : {
231 0 : CPLError(CE_Failure, CPLE_AppDefined,
232 : "Panchromatic band has no associated geotransform");
233 0 : return CE_Failure;
234 : }
235 :
236 56 : GDALRasterBandH hRefBand = psOptionsIn->pahInputSpectralBands[0];
237 56 : auto poRefBand = GDALRasterBand::FromHandle(hRefBand);
238 56 : auto poRefBandDS = poRefBand->GetDataset();
239 56 : if (poRefBandDS == nullptr)
240 : {
241 0 : CPLError(
242 : CE_Failure, CPLE_AppDefined,
243 : "Cannot retrieve dataset associated with ahInputSpectralBands[0]");
244 0 : return CE_Failure;
245 : }
246 : // Make sure that the band is really a first level child of the owning dataset
247 56 : if (poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand)
248 : {
249 0 : CPLError(
250 : CE_Failure, CPLE_AppDefined,
251 : "poRefBandDS->GetRasterBand(poRefBand->GetBand()) != poRefBand");
252 0 : return CE_Failure;
253 : }
254 :
255 : std::array<double, 6> adfRefMSGT;
256 56 : if (poRefBandDS->GetGeoTransform(adfRefMSGT.data()) != CE_None)
257 : {
258 0 : CPLError(CE_Failure, CPLE_AppDefined,
259 : "ahInputSpectralBands[0] band has no associated geotransform");
260 0 : return CE_Failure;
261 : }
262 :
263 : std::array<double, 6> adfInvMSGT;
264 56 : if (!GDALInvGeoTransform(adfRefMSGT.data(), adfInvMSGT.data()))
265 : {
266 0 : CPLError(CE_Failure, CPLE_AppDefined,
267 : "ahInputSpectralBands[0] geotransform is not invertible");
268 0 : return CE_Failure;
269 : }
270 :
271 : // Do InvMSGT * PanchroGT multiplication
272 112 : m_adfPanToMSGT[1] =
273 56 : adfInvMSGT[1] * adfPanchroGT[1] + adfInvMSGT[2] * adfPanchroGT[4];
274 112 : m_adfPanToMSGT[2] =
275 56 : adfInvMSGT[1] * adfPanchroGT[2] + adfInvMSGT[2] * adfPanchroGT[5];
276 56 : m_adfPanToMSGT[0] = adfInvMSGT[1] * adfPanchroGT[0] +
277 56 : adfInvMSGT[2] * adfPanchroGT[3] + adfInvMSGT[0];
278 112 : m_adfPanToMSGT[4] =
279 56 : adfInvMSGT[4] * adfPanchroGT[1] + adfInvMSGT[5] * adfPanchroGT[4];
280 112 : m_adfPanToMSGT[5] =
281 56 : adfInvMSGT[4] * adfPanchroGT[2] + adfInvMSGT[5] * adfPanchroGT[5];
282 56 : m_adfPanToMSGT[3] = adfInvMSGT[4] * adfPanchroGT[0] +
283 56 : adfInvMSGT[5] * adfPanchroGT[3] + adfInvMSGT[3];
284 : #if 0
285 : CPLDebug("GDAL", "m_adfPanToMSGT[] = %g %g %g %g %g %g",
286 : m_adfPanToMSGT[0], m_adfPanToMSGT[1], m_adfPanToMSGT[2],
287 : m_adfPanToMSGT[3], m_adfPanToMSGT[4], m_adfPanToMSGT[5]);
288 : #endif
289 112 : if (std::fabs(m_adfPanToMSGT[2]) > 1e-10 ||
290 56 : std::fabs(m_adfPanToMSGT[4]) > 1e-10)
291 : {
292 0 : CPLError(CE_Failure, CPLE_NotSupported,
293 : "Composition of panchromatic and multispectral geotransform "
294 : "has rotational terms");
295 0 : return CE_Failure;
296 : }
297 :
298 56 : bool bSameDataset = psOptionsIn->nInputSpectralBands > 1;
299 56 : if (bSameDataset)
300 41 : anInputBands.push_back(GDALGetBandNumber(hRefBand));
301 133 : for (int i = 1; i < psOptionsIn->nInputSpectralBands; i++)
302 : {
303 78 : GDALRasterBandH hBand = psOptionsIn->pahInputSpectralBands[i];
304 155 : if (GDALGetRasterBandXSize(hBand) != GDALGetRasterBandXSize(hRefBand) ||
305 77 : GDALGetRasterBandYSize(hBand) != GDALGetRasterBandYSize(hRefBand))
306 : {
307 1 : CPLError(CE_Failure, CPLE_AppDefined,
308 : "Dimensions of input spectral band %d different from "
309 : "first spectral band",
310 : i + 1);
311 1 : return CE_Failure;
312 : }
313 :
314 77 : auto poBand = GDALRasterBand::FromHandle(hBand);
315 77 : auto poBandDS = poBand->GetDataset();
316 77 : if (poBandDS == nullptr)
317 : {
318 0 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "Cannot retrieve dataset associated with "
320 : "input spectral band %d",
321 : i + 1);
322 0 : return CE_Failure;
323 : }
324 : // Make sure that the band is really a first level child of the owning dataset
325 77 : if (poBandDS->GetRasterBand(poBand->GetBand()) != poBand)
326 : {
327 0 : CPLError(CE_Failure, CPLE_AppDefined,
328 : "poBandDS->GetRasterBand(poBand->GetBand()) != poBand");
329 0 : return CE_Failure;
330 : }
331 :
332 : std::array<double, 6> adfMSGT;
333 77 : if (poBandDS->GetGeoTransform(adfMSGT.data()) != CE_None)
334 : {
335 0 : CPLError(
336 : CE_Failure, CPLE_AppDefined,
337 : "input spectral band %d band has no associated geotransform",
338 : i + 1);
339 0 : return CE_Failure;
340 : }
341 77 : if (adfMSGT != adfRefMSGT)
342 : {
343 0 : CPLError(CE_Failure, CPLE_AppDefined,
344 : "input spectral band %d has a different "
345 : "geotransform than the first spectral band",
346 : i + 1);
347 0 : return CE_Failure;
348 : }
349 :
350 77 : if (bSameDataset)
351 : {
352 75 : if (GDALGetBandDataset(hBand) != GDALGetBandDataset(hRefBand))
353 : {
354 4 : anInputBands.resize(0);
355 4 : bSameDataset = false;
356 : }
357 : else
358 : {
359 71 : anInputBands.push_back(GDALGetBandNumber(hBand));
360 : }
361 : }
362 : }
363 55 : if (psOptionsIn->nOutPansharpenedBands == 0)
364 : {
365 1 : CPLError(CE_Warning, CPLE_AppDefined,
366 : "No output pansharpened band defined");
367 : }
368 181 : for (int i = 0; i < psOptionsIn->nOutPansharpenedBands; i++)
369 : {
370 126 : if (psOptionsIn->panOutPansharpenedBands[i] < 0 ||
371 126 : psOptionsIn->panOutPansharpenedBands[i] >=
372 126 : psOptionsIn->nInputSpectralBands)
373 : {
374 0 : CPLError(CE_Failure, CPLE_AppDefined,
375 : "Invalid value panOutPansharpenedBands[%d] = %d", i,
376 0 : psOptionsIn->panOutPansharpenedBands[i]);
377 0 : return CE_Failure;
378 : }
379 : }
380 :
381 55 : GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
382 55 : if (psOptionsIn->nBitDepth)
383 : {
384 12 : if (psOptionsIn->nBitDepth < 0 || psOptionsIn->nBitDepth > 31 ||
385 12 : (eWorkDataType == GDT_Byte && psOptionsIn->nBitDepth > 8) ||
386 3 : (eWorkDataType == GDT_UInt16 && psOptionsIn->nBitDepth > 16))
387 : {
388 0 : CPLError(CE_Failure, CPLE_AppDefined,
389 : "Invalid value nBitDepth = %d for type %s",
390 0 : psOptionsIn->nBitDepth,
391 : GDALGetDataTypeName(eWorkDataType));
392 0 : return CE_Failure;
393 : }
394 : }
395 :
396 55 : psOptions = GDALClonePansharpenOptions(psOptionsIn);
397 55 : if (psOptions->nBitDepth == GDALGetDataTypeSize(eWorkDataType))
398 6 : psOptions->nBitDepth = 0;
399 55 : if (psOptions->nBitDepth &&
400 3 : !(eWorkDataType == GDT_Byte || eWorkDataType == GDT_UInt16 ||
401 : eWorkDataType == GDT_UInt32 || eWorkDataType == GDT_UInt64))
402 : {
403 0 : CPLError(CE_Warning, CPLE_AppDefined,
404 0 : "Ignoring nBitDepth = %d for type %s", psOptions->nBitDepth,
405 : GDALGetDataTypeName(eWorkDataType));
406 0 : psOptions->nBitDepth = 0;
407 : }
408 :
409 : // Detect negative weights.
410 187 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
411 : {
412 132 : if (psOptions->padfWeights[i] < 0.0)
413 : {
414 0 : bPositiveWeights = FALSE;
415 0 : break;
416 : }
417 : }
418 :
419 187 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
420 : {
421 264 : aMSBands.push_back(
422 132 : GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]));
423 : }
424 :
425 55 : if (psOptions->bHasNoData)
426 : {
427 7 : bool bNeedToWrapInVRT = false;
428 21 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
429 : {
430 : GDALRasterBand *poBand =
431 14 : GDALRasterBand::FromHandle(psOptions->pahInputSpectralBands[i]);
432 14 : int bHasNoData = FALSE;
433 14 : double dfNoData = poBand->GetNoDataValue(&bHasNoData);
434 14 : if (!bHasNoData || dfNoData != psOptions->dfNoData)
435 11 : bNeedToWrapInVRT = true;
436 : }
437 :
438 7 : if (bNeedToWrapInVRT)
439 : {
440 : // Wrap spectral bands in a VRT if they don't have the nodata value.
441 6 : VRTDataset *poVDS = nullptr;
442 17 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
443 : {
444 11 : GDALRasterBand *poSrcBand = aMSBands[i];
445 11 : int iVRTBand = 1;
446 11 : if (anInputBands.empty() || i == 0)
447 : {
448 7 : poVDS = new VRTDataset(poSrcBand->GetXSize(),
449 7 : poSrcBand->GetYSize());
450 7 : aVDS.push_back(poVDS);
451 : }
452 11 : if (!anInputBands.empty())
453 : {
454 7 : anInputBands[i] = i + 1;
455 7 : iVRTBand = i + 1;
456 : }
457 11 : poVDS->AddBand(poSrcBand->GetRasterDataType(), nullptr);
458 : VRTSourcedRasterBand *poVRTBand =
459 0 : dynamic_cast<VRTSourcedRasterBand *>(
460 11 : poVDS->GetRasterBand(iVRTBand));
461 11 : if (poVRTBand == nullptr)
462 0 : return CE_Failure;
463 11 : aMSBands[i] = poVRTBand;
464 11 : poVRTBand->SetNoDataValue(psOptions->dfNoData);
465 : const char *pszNBITS =
466 11 : poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
467 11 : if (pszNBITS)
468 0 : poVRTBand->SetMetadataItem("NBITS", pszNBITS,
469 0 : "IMAGE_STRUCTURE");
470 :
471 11 : VRTSimpleSource *poSimpleSource = new VRTSimpleSource();
472 44 : poVRTBand->ConfigureSource(
473 : poSimpleSource, poSrcBand, FALSE, 0, 0,
474 11 : poSrcBand->GetXSize(), poSrcBand->GetYSize(), 0, 0,
475 11 : poSrcBand->GetXSize(), poSrcBand->GetYSize());
476 11 : poVRTBand->AddSource(poSimpleSource);
477 : }
478 : }
479 : }
480 :
481 : // Setup thread pool.
482 55 : int nThreads = psOptions->nThreads;
483 55 : if (nThreads == -1)
484 9 : nThreads = CPLGetNumCPUs();
485 46 : else if (nThreads == 0)
486 : {
487 : const char *pszNumThreads =
488 46 : CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
489 46 : if (pszNumThreads)
490 : {
491 0 : if (EQUAL(pszNumThreads, "ALL_CPUS"))
492 0 : nThreads = CPLGetNumCPUs();
493 : else
494 0 : nThreads = std::max(0, std::min(128, atoi(pszNumThreads)));
495 : }
496 : }
497 55 : if (nThreads > 1)
498 : {
499 9 : CPLDebug("PANSHARPEN", "Using %d threads", nThreads);
500 18 : poThreadPool = new (std::nothrow) CPLWorkerThreadPool();
501 : // coverity[tainted_data]
502 18 : if (poThreadPool == nullptr ||
503 9 : !poThreadPool->Setup(nThreads, nullptr, nullptr))
504 : {
505 0 : delete poThreadPool;
506 0 : poThreadPool = nullptr;
507 : }
508 : }
509 :
510 55 : GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
511 55 : if (eResampleAlg != GRIORA_NearestNeighbour)
512 : {
513 55 : const char *pszResampling =
514 110 : (eResampleAlg == GRIORA_Bilinear) ? "BILINEAR"
515 55 : : (eResampleAlg == GRIORA_Cubic) ? "CUBIC"
516 0 : : (eResampleAlg == GRIORA_CubicSpline) ? "CUBICSPLINE"
517 0 : : (eResampleAlg == GRIORA_Lanczos) ? "LANCZOS"
518 0 : : (eResampleAlg == GRIORA_Average) ? "AVERAGE"
519 0 : : (eResampleAlg == GRIORA_RMS) ? "RMS"
520 0 : : (eResampleAlg == GRIORA_Mode) ? "MODE"
521 0 : : (eResampleAlg == GRIORA_Gauss) ? "GAUSS"
522 : : "UNKNOWN";
523 :
524 55 : GDALGetResampleFunction(pszResampling, &nKernelRadius);
525 : }
526 :
527 55 : return CE_None;
528 : }
529 :
530 : /************************************************************************/
531 : /* WeightedBroveyWithNoData() */
532 : /************************************************************************/
533 :
534 : template <class WorkDataType, class OutDataType>
535 4 : void GDALPansharpenOperation::WeightedBroveyWithNoData(
536 : const WorkDataType *pPanBuffer,
537 : const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
538 : size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
539 : {
540 : WorkDataType noData, validValue;
541 4 : GDALCopyWord(psOptions->dfNoData, noData);
542 :
543 : if constexpr (!(std::numeric_limits<WorkDataType>::is_integer))
544 0 : validValue = static_cast<WorkDataType>(noData + 1e-5);
545 4 : else if (noData == std::numeric_limits<WorkDataType>::min())
546 4 : validValue = std::numeric_limits<WorkDataType>::min() + 1;
547 : else
548 0 : validValue = noData - 1;
549 :
550 1280000 : for (size_t j = 0; j < nValues; j++)
551 : {
552 1280000 : double dfPseudoPanchro = 0.0;
553 4480000 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
554 : {
555 3200000 : WorkDataType nSpectralVal =
556 3200000 : pUpsampledSpectralBuffer[i * nBandValues + j];
557 3200000 : if (nSpectralVal == noData)
558 : {
559 0 : dfPseudoPanchro = 0.0;
560 0 : break;
561 : }
562 3200000 : dfPseudoPanchro += psOptions->padfWeights[i] * nSpectralVal;
563 : }
564 1280000 : if (dfPseudoPanchro != 0.0 && pPanBuffer[j] != noData)
565 : {
566 1277710 : const double dfFactor = pPanBuffer[j] / dfPseudoPanchro;
567 4471990 : for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
568 : {
569 3194280 : WorkDataType nRawValue = pUpsampledSpectralBuffer
570 3194280 : [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
571 : WorkDataType nPansharpenedValue;
572 3194280 : GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
573 3194280 : if (nMaxValue != 0 && nPansharpenedValue > nMaxValue)
574 0 : nPansharpenedValue = nMaxValue;
575 : // We don't want a valid value to be mapped to NoData.
576 3194280 : if (nPansharpenedValue == noData)
577 3238 : nPansharpenedValue = validValue;
578 3194280 : GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
579 1277710 : }
580 : }
581 : else
582 : {
583 8008 : for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
584 : {
585 5720 : GDALCopyWord(noData, pDataBuf[i * nBandValues + j]);
586 : }
587 : }
588 : }
589 4 : }
590 :
591 : /************************************************************************/
592 : /* ComputeFactor() */
593 : /************************************************************************/
594 :
595 : template <class T>
596 19625650 : static inline double ComputeFactor(T panValue, double dfPseudoPanchro)
597 : {
598 19625650 : if (dfPseudoPanchro == 0.0)
599 2778 : return 0.0;
600 :
601 19622880 : return panValue / dfPseudoPanchro;
602 : }
603 :
604 : /************************************************************************/
605 : /* ClampAndRound() */
606 : /************************************************************************/
607 :
608 136 : template <class T> static inline T ClampAndRound(double dfVal, T nMaxValue)
609 : {
610 136 : if (dfVal > nMaxValue)
611 50 : return nMaxValue;
612 : else
613 86 : return static_cast<T>(dfVal + 0.5);
614 : }
615 :
616 : /************************************************************************/
617 : /* WeightedBrovey() */
618 : /************************************************************************/
619 :
620 : template <class WorkDataType, class OutDataType, int bHasBitDepth>
621 132 : void GDALPansharpenOperation::WeightedBrovey3(
622 : const WorkDataType *pPanBuffer,
623 : const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
624 : size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
625 : {
626 132 : if (psOptions->bHasNoData)
627 : {
628 4 : WeightedBroveyWithNoData<WorkDataType, OutDataType>(
629 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
630 : nBandValues, nMaxValue);
631 4 : return;
632 : }
633 :
634 19405952 : for (size_t j = 0; j < nValues; j++)
635 : {
636 19376440 : double dfFactor = 0.0;
637 : // if( pPanBuffer[j] == 0 )
638 : // dfFactor = 1.0;
639 : // else
640 : {
641 19376440 : double dfPseudoPanchro = 0.0;
642 81817100 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
643 62440700 : dfPseudoPanchro +=
644 62440700 : psOptions->padfWeights[i] *
645 62440700 : pUpsampledSpectralBuffer[i * nBandValues + j];
646 19376440 : dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
647 : }
648 :
649 74466100 : for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
650 : {
651 55060200 : WorkDataType nRawValue =
652 55060200 : pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
653 55060200 : nBandValues +
654 : j];
655 : WorkDataType nPansharpenedValue;
656 55060200 : GDALCopyWord(nRawValue * dfFactor, nPansharpenedValue);
657 : if constexpr (bHasBitDepth)
658 : {
659 0 : if (nPansharpenedValue > nMaxValue)
660 0 : nPansharpenedValue = nMaxValue;
661 : }
662 52826300 : GDALCopyWord(nPansharpenedValue, pDataBuf[i * nBandValues + j]);
663 : }
664 : }
665 : }
666 :
667 : /* We restrict to 64bit processors because they are guaranteed to have SSE2 */
668 : /* Could possibly be used too on 32bit, but we would need to check at runtime */
669 : #if defined(__x86_64) || defined(_M_X64)
670 :
671 : #include "gdalsse_priv.h"
672 :
673 : template <class T, int NINPUT, int NOUTPUT>
674 41 : size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
675 : const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
676 : size_t nValues, size_t nBandValues, T nMaxValue) const
677 : {
678 : static_assert(NINPUT == 3 || NINPUT == 4);
679 : static_assert(NOUTPUT == 3 || NOUTPUT == 4);
680 41 : const XMMReg4Double w0 =
681 41 : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 0);
682 41 : const XMMReg4Double w1 =
683 41 : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 1);
684 41 : const XMMReg4Double w2 =
685 41 : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 2);
686 41 : [[maybe_unused]] const XMMReg4Double w3 =
687 : (NINPUT == 3)
688 : ? XMMReg4Double::Zero()
689 8 : : XMMReg4Double::Load1ValHighAndLow(psOptions->padfWeights + 3);
690 :
691 41 : const XMMReg4Double zero = XMMReg4Double::Zero();
692 41 : double dfMaxValue = nMaxValue;
693 41 : const XMMReg4Double maxValue =
694 : XMMReg4Double::Load1ValHighAndLow(&dfMaxValue);
695 :
696 1048 : size_t j = 0; // Used after for.
697 1314512 : for (; j + 3 < nValues; j += 4)
698 : {
699 1314471 : XMMReg4Double pseudoPanchro = zero;
700 :
701 1319570 : XMMReg4Double val0 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
702 765095 : 0 * nBandValues + j);
703 1317939 : XMMReg4Double val1 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
704 1317939 : 1 * nBandValues + j);
705 1320001 : XMMReg4Double val2 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
706 1320001 : 2 * nBandValues + j);
707 : XMMReg4Double val3;
708 : if constexpr (NINPUT == 4 || NOUTPUT == 4)
709 : {
710 505024 : val3 = XMMReg4Double::Load4Val(pUpsampledSpectralBuffer +
711 513241 : 3 * nBandValues + j);
712 : }
713 :
714 1308017 : pseudoPanchro += w0 * val0;
715 1314270 : pseudoPanchro += w1 * val1;
716 1317069 : pseudoPanchro += w2 * val2;
717 : if constexpr (NINPUT == 4)
718 513444 : pseudoPanchro += w3 * val3;
719 :
720 : /* Little trick to avoid use of ternary operator due to one of the
721 : * branch being zero */
722 1316907 : XMMReg4Double factor = XMMReg4Double::And(
723 : XMMReg4Double::NotEquals(pseudoPanchro, zero),
724 761347 : XMMReg4Double::Load4Val(pPanBuffer + j) / pseudoPanchro);
725 :
726 1314083 : val0 = XMMReg4Double::Min(val0 * factor, maxValue);
727 1313089 : val1 = XMMReg4Double::Min(val1 * factor, maxValue);
728 1312994 : val2 = XMMReg4Double::Min(val2 * factor, maxValue);
729 : if constexpr (NOUTPUT == 4)
730 : {
731 256274 : val3 = XMMReg4Double::Min(val3 * factor, maxValue);
732 : }
733 1304890 : val0.Store4Val(pDataBuf + 0 * nBandValues + j);
734 1311644 : val1.Store4Val(pDataBuf + 1 * nBandValues + j);
735 1313359 : val2.Store4Val(pDataBuf + 2 * nBandValues + j);
736 : if constexpr (NOUTPUT == 4)
737 : {
738 255051 : val3.Store4Val(pDataBuf + 3 * nBandValues + j);
739 : }
740 : }
741 41 : return j;
742 : }
743 :
744 : #else
745 :
746 : template <class T, int NINPUT, int NOUTPUT>
747 : size_t GDALPansharpenOperation::WeightedBroveyPositiveWeightsInternal(
748 : const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
749 : size_t nValues, size_t nBandValues, T nMaxValue) const
750 : {
751 : static_assert(NINPUT == 3 || NINPUT == 4);
752 : const double dfw0 = psOptions->padfWeights[0];
753 : const double dfw1 = psOptions->padfWeights[1];
754 : const double dfw2 = psOptions->padfWeights[2];
755 : // cppcheck-suppress knownConditionTrueFalse
756 : [[maybe_unused]] const double dfw3 =
757 : (NINPUT == 3) ? 0 : psOptions->padfWeights[3];
758 : size_t j = 0; // Used after for.
759 : for (; j + 1 < nValues; j += 2)
760 : {
761 : double dfFactor = 0.0;
762 : double dfFactor2 = 0.0;
763 : double dfPseudoPanchro = 0.0;
764 : double dfPseudoPanchro2 = 0.0;
765 :
766 : dfPseudoPanchro += dfw0 * pUpsampledSpectralBuffer[j];
767 : dfPseudoPanchro2 += dfw0 * pUpsampledSpectralBuffer[j + 1];
768 :
769 : dfPseudoPanchro += dfw1 * pUpsampledSpectralBuffer[nBandValues + j];
770 : dfPseudoPanchro2 +=
771 : dfw1 * pUpsampledSpectralBuffer[nBandValues + j + 1];
772 :
773 : dfPseudoPanchro += dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j];
774 : dfPseudoPanchro2 +=
775 : dfw2 * pUpsampledSpectralBuffer[2 * nBandValues + j + 1];
776 :
777 : if constexpr (NINPUT == 4)
778 : {
779 : dfPseudoPanchro +=
780 : dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j];
781 : dfPseudoPanchro2 +=
782 : dfw3 * pUpsampledSpectralBuffer[3 * nBandValues + j + 1];
783 : }
784 :
785 : dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
786 : dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
787 :
788 : for (int i = 0; i < NOUTPUT; i++)
789 : {
790 : T nRawValue = pUpsampledSpectralBuffer[i * nBandValues + j];
791 : double dfTmp = nRawValue * dfFactor;
792 : pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
793 :
794 : T nRawValue2 = pUpsampledSpectralBuffer[i * nBandValues + j + 1];
795 : double dfTmp2 = nRawValue2 * dfFactor2;
796 : pDataBuf[i * nBandValues + j + 1] =
797 : ClampAndRound(dfTmp2, nMaxValue);
798 : }
799 : }
800 : return j;
801 : }
802 : #endif
803 :
804 : template <class T>
805 44 : void GDALPansharpenOperation::WeightedBroveyPositiveWeights(
806 : const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
807 : size_t nValues, size_t nBandValues, T nMaxValue) const
808 : {
809 44 : if (psOptions->bHasNoData)
810 : {
811 0 : WeightedBroveyWithNoData<T, T>(pPanBuffer, pUpsampledSpectralBuffer,
812 : pDataBuf, nValues, nBandValues,
813 : nMaxValue);
814 0 : return;
815 : }
816 :
817 44 : if (nMaxValue == 0)
818 41 : nMaxValue = std::numeric_limits<T>::max();
819 : size_t j;
820 47 : if (psOptions->nInputSpectralBands == 3 &&
821 33 : psOptions->nOutPansharpenedBands == 3 &&
822 33 : psOptions->panOutPansharpenedBands[0] == 0 &&
823 33 : psOptions->panOutPansharpenedBands[1] == 1 &&
824 33 : psOptions->panOutPansharpenedBands[2] == 2)
825 : {
826 33 : j = WeightedBroveyPositiveWeightsInternal<T, 3, 3>(
827 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
828 : nBandValues, nMaxValue);
829 : }
830 14 : else if (psOptions->nInputSpectralBands == 4 &&
831 8 : psOptions->nOutPansharpenedBands == 4 &&
832 4 : psOptions->panOutPansharpenedBands[0] == 0 &&
833 4 : psOptions->panOutPansharpenedBands[1] == 1 &&
834 4 : psOptions->panOutPansharpenedBands[2] == 2 &&
835 4 : psOptions->panOutPansharpenedBands[3] == 3)
836 : {
837 4 : j = WeightedBroveyPositiveWeightsInternal<T, 4, 4>(
838 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
839 : nBandValues, nMaxValue);
840 : }
841 10 : else if (psOptions->nInputSpectralBands == 4 &&
842 4 : psOptions->nOutPansharpenedBands == 3 &&
843 4 : psOptions->panOutPansharpenedBands[0] == 0 &&
844 4 : psOptions->panOutPansharpenedBands[1] == 1 &&
845 4 : psOptions->panOutPansharpenedBands[2] == 2)
846 : {
847 4 : j = WeightedBroveyPositiveWeightsInternal<T, 4, 3>(
848 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
849 : nBandValues, nMaxValue);
850 : }
851 : else
852 : {
853 54 : for (j = 0; j + 1 < nValues; j += 2)
854 : {
855 48 : double dfFactor = 0.0;
856 48 : double dfFactor2 = 0.0;
857 48 : double dfPseudoPanchro = 0.0;
858 48 : double dfPseudoPanchro2 = 0.0;
859 96 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
860 : {
861 48 : dfPseudoPanchro +=
862 48 : psOptions->padfWeights[i] *
863 48 : pUpsampledSpectralBuffer[i * nBandValues + j];
864 48 : dfPseudoPanchro2 +=
865 48 : psOptions->padfWeights[i] *
866 48 : pUpsampledSpectralBuffer[i * nBandValues + j + 1];
867 : }
868 :
869 48 : dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
870 48 : dfFactor2 = ComputeFactor(pPanBuffer[j + 1], dfPseudoPanchro2);
871 :
872 96 : for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
873 : {
874 48 : const T nRawValue = pUpsampledSpectralBuffer
875 48 : [psOptions->panOutPansharpenedBands[i] * nBandValues + j];
876 48 : const double dfTmp = nRawValue * dfFactor;
877 48 : pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
878 :
879 48 : const T nRawValue2 = pUpsampledSpectralBuffer
880 48 : [psOptions->panOutPansharpenedBands[i] * nBandValues + j +
881 : 1];
882 48 : const double dfTmp2 = nRawValue2 * dfFactor2;
883 48 : pDataBuf[i * nBandValues + j + 1] =
884 48 : ClampAndRound(dfTmp2, nMaxValue);
885 : }
886 : }
887 : }
888 60 : for (; j < nValues; j++)
889 : {
890 13 : double dfFactor = 0.0;
891 13 : double dfPseudoPanchro = 0.0;
892 54 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
893 41 : dfPseudoPanchro += psOptions->padfWeights[i] *
894 41 : pUpsampledSpectralBuffer[i * nBandValues + j];
895 13 : dfFactor = ComputeFactor(pPanBuffer[j], dfPseudoPanchro);
896 :
897 53 : for (int i = 0; i < psOptions->nOutPansharpenedBands; i++)
898 : {
899 40 : T nRawValue =
900 40 : pUpsampledSpectralBuffer[psOptions->panOutPansharpenedBands[i] *
901 40 : nBandValues +
902 : j];
903 40 : double dfTmp = nRawValue * dfFactor;
904 40 : pDataBuf[i * nBandValues + j] = ClampAndRound(dfTmp, nMaxValue);
905 : }
906 : }
907 : }
908 :
909 : template <class WorkDataType, class OutDataType>
910 126 : void GDALPansharpenOperation::WeightedBrovey(
911 : const WorkDataType *pPanBuffer,
912 : const WorkDataType *pUpsampledSpectralBuffer, OutDataType *pDataBuf,
913 : size_t nValues, size_t nBandValues, WorkDataType nMaxValue) const
914 : {
915 126 : if (nMaxValue == 0)
916 126 : WeightedBrovey3<WorkDataType, OutDataType, FALSE>(
917 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
918 : nBandValues, 0);
919 : else
920 : {
921 0 : WeightedBrovey3<WorkDataType, OutDataType, TRUE>(
922 : pPanBuffer, pUpsampledSpectralBuffer, pDataBuf, nValues,
923 : nBandValues, nMaxValue);
924 : }
925 126 : }
926 :
927 : template <class T>
928 47 : void GDALPansharpenOperation::WeightedBroveyGByteOrUInt16(
929 : const T *pPanBuffer, const T *pUpsampledSpectralBuffer, T *pDataBuf,
930 : size_t nValues, size_t nBandValues, T nMaxValue) const
931 : {
932 47 : if (bPositiveWeights)
933 : {
934 47 : WeightedBroveyPositiveWeights(pPanBuffer, pUpsampledSpectralBuffer,
935 : pDataBuf, nValues, nBandValues,
936 : nMaxValue);
937 : }
938 0 : else if (nMaxValue == 0)
939 : {
940 0 : WeightedBrovey3<T, T, FALSE>(pPanBuffer, pUpsampledSpectralBuffer,
941 : pDataBuf, nValues, nBandValues, 0);
942 : }
943 : else
944 : {
945 0 : WeightedBrovey3<T, T, TRUE>(pPanBuffer, pUpsampledSpectralBuffer,
946 : pDataBuf, nValues, nBandValues, nMaxValue);
947 : }
948 47 : }
949 :
950 : template <>
951 32 : void GDALPansharpenOperation::WeightedBrovey<GByte, GByte>(
952 : const GByte *pPanBuffer, const GByte *pUpsampledSpectralBuffer,
953 : GByte *pDataBuf, size_t nValues, size_t nBandValues, GByte nMaxValue) const
954 : {
955 32 : WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
956 : nValues, nBandValues, nMaxValue);
957 32 : }
958 :
959 : template <>
960 15 : void GDALPansharpenOperation::WeightedBrovey<GUInt16, GUInt16>(
961 : const GUInt16 *pPanBuffer, const GUInt16 *pUpsampledSpectralBuffer,
962 : GUInt16 *pDataBuf, size_t nValues, size_t nBandValues,
963 : GUInt16 nMaxValue) const
964 : {
965 15 : WeightedBroveyGByteOrUInt16(pPanBuffer, pUpsampledSpectralBuffer, pDataBuf,
966 : nValues, nBandValues, nMaxValue);
967 15 : }
968 :
969 : template <class WorkDataType>
970 173 : CPLErr GDALPansharpenOperation::WeightedBrovey(
971 : const WorkDataType *pPanBuffer,
972 : const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
973 : GDALDataType eBufDataType, size_t nValues, size_t nBandValues,
974 : WorkDataType nMaxValue) const
975 : {
976 173 : switch (eBufDataType)
977 : {
978 33 : case GDT_Byte:
979 33 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
980 : static_cast<GByte *>(pDataBuf), nValues, nBandValues,
981 : nMaxValue);
982 33 : break;
983 :
984 16 : case GDT_UInt16:
985 16 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
986 : static_cast<GUInt16 *>(pDataBuf), nValues,
987 : nBandValues, nMaxValue);
988 16 : break;
989 :
990 : #ifndef LIMIT_TYPES
991 : case GDT_Int8:
992 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
993 : static_cast<GInt8 *>(pDataBuf), nValues, nBandValues,
994 : nMaxValue);
995 : break;
996 :
997 : case GDT_Int16:
998 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
999 : static_cast<GInt16 *>(pDataBuf), nValues,
1000 : nBandValues, nMaxValue);
1001 : break;
1002 :
1003 : case GDT_UInt32:
1004 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1005 : static_cast<GUInt32 *>(pDataBuf), nValues,
1006 : nBandValues, nMaxValue);
1007 : break;
1008 :
1009 : case GDT_Int32:
1010 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1011 : static_cast<GInt32 *>(pDataBuf), nValues,
1012 : nBandValues, nMaxValue);
1013 : break;
1014 :
1015 : case GDT_UInt64:
1016 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1017 : static_cast<std::uint64_t *>(pDataBuf), nValues,
1018 : nBandValues, nMaxValue);
1019 : break;
1020 :
1021 : case GDT_Int64:
1022 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1023 : static_cast<std::int64_t *>(pDataBuf), nValues,
1024 : nBandValues, nMaxValue);
1025 : break;
1026 :
1027 : case GDT_Float32:
1028 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1029 : static_cast<float *>(pDataBuf), nValues, nBandValues,
1030 : nMaxValue);
1031 : break;
1032 : #endif
1033 :
1034 124 : case GDT_Float64:
1035 124 : WeightedBrovey(pPanBuffer, pUpsampledSpectralBuffer,
1036 : static_cast<double *>(pDataBuf), nValues,
1037 : nBandValues, nMaxValue);
1038 124 : break;
1039 :
1040 0 : default:
1041 0 : CPLError(CE_Failure, CPLE_NotSupported,
1042 : "eBufDataType not supported");
1043 0 : return CE_Failure;
1044 : break;
1045 : }
1046 :
1047 173 : return CE_None;
1048 : }
1049 :
1050 : template <class WorkDataType>
1051 6 : CPLErr GDALPansharpenOperation::WeightedBrovey(
1052 : const WorkDataType *pPanBuffer,
1053 : const WorkDataType *pUpsampledSpectralBuffer, void *pDataBuf,
1054 : GDALDataType eBufDataType, size_t nValues, size_t nBandValues) const
1055 : {
1056 6 : switch (eBufDataType)
1057 : {
1058 6 : case GDT_Byte:
1059 6 : WeightedBrovey3<WorkDataType, GByte, FALSE>(
1060 : pPanBuffer, pUpsampledSpectralBuffer,
1061 : static_cast<GByte *>(pDataBuf), nValues, nBandValues, 0);
1062 6 : break;
1063 :
1064 0 : case GDT_UInt16:
1065 0 : WeightedBrovey3<WorkDataType, GUInt16, FALSE>(
1066 : pPanBuffer, pUpsampledSpectralBuffer,
1067 : static_cast<GUInt16 *>(pDataBuf), nValues, nBandValues, 0);
1068 0 : break;
1069 :
1070 : #ifndef LIMIT_TYPES
1071 : case GDT_Int8:
1072 : WeightedBrovey3<WorkDataType, GInt8, FALSE>(
1073 : pPanBuffer, pUpsampledSpectralBuffer,
1074 : static_cast<GInt8 *>(pDataBuf), nValues, nBandValues, 0);
1075 : break;
1076 :
1077 : case GDT_Int16:
1078 : WeightedBrovey3<WorkDataType, GInt16, FALSE>(
1079 : pPanBuffer, pUpsampledSpectralBuffer,
1080 : static_cast<GInt16 *>(pDataBuf), nValues, nBandValues, 0);
1081 : break;
1082 :
1083 : case GDT_UInt32:
1084 : WeightedBrovey3<WorkDataType, GUInt32, FALSE>(
1085 : pPanBuffer, pUpsampledSpectralBuffer,
1086 : static_cast<GUInt32 *>(pDataBuf), nValues, nBandValues, 0);
1087 : break;
1088 :
1089 : case GDT_Int32:
1090 : WeightedBrovey3<WorkDataType, GInt32, FALSE>(
1091 : pPanBuffer, pUpsampledSpectralBuffer,
1092 : static_cast<GInt32 *>(pDataBuf), nValues, nBandValues, 0);
1093 : break;
1094 :
1095 : case GDT_UInt64:
1096 : WeightedBrovey3<WorkDataType, std::uint64_t, FALSE>(
1097 : pPanBuffer, pUpsampledSpectralBuffer,
1098 : static_cast<std::uint64_t *>(pDataBuf), nValues, nBandValues,
1099 : 0);
1100 : break;
1101 :
1102 : case GDT_Int64:
1103 : WeightedBrovey3<WorkDataType, std::int64_t, FALSE>(
1104 : pPanBuffer, pUpsampledSpectralBuffer,
1105 : static_cast<std::int64_t *>(pDataBuf), nValues, nBandValues, 0);
1106 : break;
1107 :
1108 : case GDT_Float32:
1109 : WeightedBrovey3<WorkDataType, float, FALSE>(
1110 : pPanBuffer, pUpsampledSpectralBuffer,
1111 : static_cast<float *>(pDataBuf), nValues, nBandValues, 0);
1112 : break;
1113 : #endif
1114 :
1115 0 : case GDT_Float64:
1116 0 : WeightedBrovey3<WorkDataType, double, FALSE>(
1117 : pPanBuffer, pUpsampledSpectralBuffer,
1118 : static_cast<double *>(pDataBuf), nValues, nBandValues, 0);
1119 0 : break;
1120 :
1121 0 : default:
1122 0 : CPLError(CE_Failure, CPLE_NotSupported,
1123 : "eBufDataType not supported");
1124 0 : return CE_Failure;
1125 : break;
1126 : }
1127 :
1128 6 : return CE_None;
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* ClampValues() */
1133 : /************************************************************************/
1134 :
1135 : template <class T>
1136 2 : static void ClampValues(T *panBuffer, size_t nValues, T nMaxVal)
1137 : {
1138 34 : for (size_t i = 0; i < nValues; i++)
1139 : {
1140 32 : if (panBuffer[i] > nMaxVal)
1141 8 : panBuffer[i] = nMaxVal;
1142 : }
1143 2 : }
1144 :
1145 : /************************************************************************/
1146 : /* ProcessRegion() */
1147 : /************************************************************************/
1148 :
1149 : /** Executes a pansharpening operation on a rectangular region of the
1150 : * resulting dataset.
1151 : *
1152 : * The window is expressed with respect to the dimensions of the panchromatic
1153 : * band.
1154 : *
1155 : * Spectral bands are upsampled and merged with the panchromatic band according
1156 : * to the select algorithm and options.
1157 : *
1158 : * @param nXOff pixel offset.
1159 : * @param nYOff pixel offset.
1160 : * @param nXSize width of the pansharpened region to compute.
1161 : * @param nYSize height of the pansharpened region to compute.
1162 : * @param pDataBuf output buffer. Must be nXSize * nYSize *
1163 : * GDALGetDataTypeSizeBytes(eBufDataType) *
1164 : * psOptions->nOutPansharpenedBands large.
1165 : * It begins with all values of the first output band, followed
1166 : * by values of the second output band, etc...
1167 : * @param eBufDataType data type of the output buffer
1168 : *
1169 : * @return CE_None in case of success, CE_Failure in case of failure.
1170 : *
1171 : * @since GDAL 2.1
1172 : */
1173 80 : CPLErr GDALPansharpenOperation::ProcessRegion(int nXOff, int nYOff, int nXSize,
1174 : int nYSize, void *pDataBuf,
1175 : GDALDataType eBufDataType)
1176 : {
1177 80 : if (psOptions == nullptr)
1178 0 : return CE_Failure;
1179 :
1180 : // TODO: Avoid allocating buffers each time.
1181 : GDALRasterBand *poPanchroBand =
1182 80 : GDALRasterBand::FromHandle(psOptions->hPanchroBand);
1183 80 : GDALDataType eWorkDataType = poPanchroBand->GetRasterDataType();
1184 : #ifdef LIMIT_TYPES
1185 80 : if (eWorkDataType != GDT_Byte && eWorkDataType != GDT_UInt16)
1186 6 : eWorkDataType = GDT_Float64;
1187 : #endif
1188 80 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eWorkDataType);
1189 80 : GByte *pUpsampledSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
1190 : nXSize, nYSize,
1191 : cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1192 : GByte *pPanBuffer = static_cast<GByte *>(
1193 80 : VSI_MALLOC3_VERBOSE(nXSize, nYSize, nDataTypeSize));
1194 80 : if (pUpsampledSpectralBuffer == nullptr || pPanBuffer == nullptr)
1195 : {
1196 0 : VSIFree(pUpsampledSpectralBuffer);
1197 0 : VSIFree(pPanBuffer);
1198 0 : return CE_Failure;
1199 : }
1200 :
1201 80 : CPLErr eErr = poPanchroBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
1202 : pPanBuffer, nXSize, nYSize,
1203 : eWorkDataType, 0, 0, nullptr);
1204 80 : if (eErr != CE_None)
1205 : {
1206 0 : VSIFree(pUpsampledSpectralBuffer);
1207 0 : VSIFree(pPanBuffer);
1208 0 : return CE_Failure;
1209 : }
1210 :
1211 80 : int nTasks = 0;
1212 80 : if (poThreadPool)
1213 : {
1214 33 : nTasks = poThreadPool->GetThreadCount();
1215 33 : if (nTasks > nYSize)
1216 0 : nTasks = nYSize;
1217 : }
1218 :
1219 : GDALRasterIOExtraArg sExtraArg;
1220 80 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1221 80 : const GDALRIOResampleAlg eResampleAlg = psOptions->eResampleAlg;
1222 : // cppcheck-suppress redundantAssignment
1223 80 : sExtraArg.eResampleAlg = eResampleAlg;
1224 80 : sExtraArg.bFloatingPointWindowValidity = TRUE;
1225 80 : sExtraArg.dfXOff = m_adfPanToMSGT[0] + nXOff * m_adfPanToMSGT[1];
1226 80 : sExtraArg.dfYOff = m_adfPanToMSGT[3] + nYOff * m_adfPanToMSGT[5];
1227 80 : sExtraArg.dfXSize = nXSize * m_adfPanToMSGT[1];
1228 80 : sExtraArg.dfYSize = nYSize * m_adfPanToMSGT[5];
1229 80 : if (sExtraArg.dfXOff + sExtraArg.dfXSize > aMSBands[0]->GetXSize())
1230 0 : sExtraArg.dfXOff = aMSBands[0]->GetXSize() - sExtraArg.dfXSize;
1231 80 : if (sExtraArg.dfYOff + sExtraArg.dfYSize > aMSBands[0]->GetYSize())
1232 0 : sExtraArg.dfYOff = aMSBands[0]->GetYSize() - sExtraArg.dfYSize;
1233 80 : int nSpectralXOff = static_cast<int>(sExtraArg.dfXOff);
1234 80 : int nSpectralYOff = static_cast<int>(sExtraArg.dfYOff);
1235 80 : int nSpectralXSize = static_cast<int>(0.49999 + sExtraArg.dfXSize);
1236 80 : int nSpectralYSize = static_cast<int>(0.49999 + sExtraArg.dfYSize);
1237 80 : if (nSpectralXSize == 0)
1238 10 : nSpectralXSize = 1;
1239 80 : if (nSpectralYSize == 0)
1240 10 : nSpectralYSize = 1;
1241 :
1242 : // When upsampling, extract the multispectral data at
1243 : // full resolution in a temp buffer, and then do the upsampling.
1244 80 : if (nSpectralXSize < nXSize && nSpectralYSize < nYSize &&
1245 70 : eResampleAlg != GRIORA_NearestNeighbour && nYSize > 1)
1246 : {
1247 : // Take some margin to take into account the radius of the
1248 : // resampling kernel.
1249 70 : int nXOffExtract = nSpectralXOff - nKernelRadius;
1250 70 : int nYOffExtract = nSpectralYOff - nKernelRadius;
1251 70 : int nXSizeExtract = nSpectralXSize + 1 + 2 * nKernelRadius;
1252 70 : int nYSizeExtract = nSpectralYSize + 1 + 2 * nKernelRadius;
1253 70 : if (nXOffExtract < 0)
1254 : {
1255 65 : nXSizeExtract += nXOffExtract;
1256 65 : nXOffExtract = 0;
1257 : }
1258 70 : if (nYOffExtract < 0)
1259 : {
1260 58 : nYSizeExtract += nYOffExtract;
1261 58 : nYOffExtract = 0;
1262 : }
1263 70 : if (nXOffExtract + nXSizeExtract > aMSBands[0]->GetXSize())
1264 65 : nXSizeExtract = aMSBands[0]->GetXSize() - nXOffExtract;
1265 70 : if (nYOffExtract + nYSizeExtract > aMSBands[0]->GetYSize())
1266 58 : nYSizeExtract = aMSBands[0]->GetYSize() - nYOffExtract;
1267 :
1268 70 : GByte *pSpectralBuffer = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
1269 : nXSizeExtract, nYSizeExtract,
1270 : cpl::fits_on<int>(psOptions->nInputSpectralBands * nDataTypeSize)));
1271 70 : if (pSpectralBuffer == nullptr)
1272 : {
1273 0 : VSIFree(pUpsampledSpectralBuffer);
1274 0 : VSIFree(pPanBuffer);
1275 0 : return CE_Failure;
1276 : }
1277 :
1278 70 : if (!anInputBands.empty())
1279 : {
1280 : // Use dataset RasterIO when possible.
1281 124 : eErr = aMSBands[0]->GetDataset()->RasterIO(
1282 : GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1283 : nYSizeExtract, pSpectralBuffer, nXSizeExtract, nYSizeExtract,
1284 62 : eWorkDataType, static_cast<int>(anInputBands.size()),
1285 62 : &anInputBands[0], 0, 0, 0, nullptr);
1286 : }
1287 : else
1288 : {
1289 8 : for (int i = 0;
1290 20 : eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
1291 : {
1292 24 : eErr = aMSBands[i]->RasterIO(
1293 : GF_Read, nXOffExtract, nYOffExtract, nXSizeExtract,
1294 : nYSizeExtract,
1295 12 : pSpectralBuffer + static_cast<size_t>(i) * nXSizeExtract *
1296 12 : nYSizeExtract * nDataTypeSize,
1297 : nXSizeExtract, nYSizeExtract, eWorkDataType, 0, 0, nullptr);
1298 : }
1299 : }
1300 70 : if (eErr != CE_None)
1301 : {
1302 0 : VSIFree(pSpectralBuffer);
1303 0 : VSIFree(pUpsampledSpectralBuffer);
1304 0 : VSIFree(pPanBuffer);
1305 0 : return CE_Failure;
1306 : }
1307 :
1308 : // Create a MEM dataset that wraps the input buffer.
1309 70 : auto poMEMDS = MEMDataset::Create("", nXSizeExtract, nYSizeExtract, 0,
1310 : eWorkDataType, nullptr);
1311 :
1312 282 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1313 : {
1314 212 : GByte *pabyBuffer =
1315 212 : pSpectralBuffer + static_cast<size_t>(i) * nDataTypeSize *
1316 212 : nXSizeExtract * nYSizeExtract;
1317 212 : GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
1318 : poMEMDS, i + 1, pabyBuffer, eWorkDataType, 0, 0, false);
1319 212 : poMEMDS->AddMEMBand(hMEMBand);
1320 :
1321 : const char *pszNBITS =
1322 212 : aMSBands[i]->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1323 212 : if (pszNBITS)
1324 4 : poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
1325 4 : "NBITS", pszNBITS, "IMAGE_STRUCTURE");
1326 :
1327 212 : if (psOptions->bHasNoData)
1328 10 : poMEMDS->GetRasterBand(i + 1)->SetNoDataValue(
1329 10 : psOptions->dfNoData);
1330 : }
1331 :
1332 70 : if (nTasks <= 1)
1333 : {
1334 37 : nSpectralXOff -= nXOffExtract;
1335 37 : nSpectralYOff -= nYOffExtract;
1336 37 : sExtraArg.dfXOff -= nXOffExtract;
1337 37 : sExtraArg.dfYOff -= nYOffExtract;
1338 37 : CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
1339 : GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1340 : nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1341 37 : eWorkDataType, psOptions->nInputSpectralBands, nullptr, 0, 0, 0,
1342 : &sExtraArg));
1343 : }
1344 : else
1345 : {
1346 : // We are abusing the contract of the GDAL API by using the
1347 : // MEMDataset from several threads. In this case, this is safe. In
1348 : // case, that would no longer be the case we could create as many
1349 : // MEMDataset as threads pointing to the same buffer.
1350 :
1351 : // To avoid races in threads, we query now the mask flags,
1352 : // so that implicit mask bands are created now.
1353 150 : for (int i = 0; i < poMEMDS->GetRasterCount(); i++)
1354 : {
1355 117 : poMEMDS->GetRasterBand(i + 1)->GetMaskFlags();
1356 : }
1357 :
1358 66 : std::vector<GDALPansharpenResampleJob> asJobs;
1359 33 : asJobs.resize(nTasks);
1360 33 : GDALPansharpenResampleJob *pasJobs = &(asJobs[0]);
1361 : {
1362 66 : std::vector<void *> ahJobData;
1363 33 : ahJobData.resize(nTasks);
1364 :
1365 : #ifdef DEBUG_TIMING
1366 : struct timeval tv;
1367 : #endif
1368 165 : for (int i = 0; i < nTasks; i++)
1369 : {
1370 132 : const size_t iStartLine =
1371 132 : (static_cast<size_t>(i) * nYSize) / nTasks;
1372 132 : const size_t iNextStartLine =
1373 132 : (static_cast<size_t>(i + 1) * nYSize) / nTasks;
1374 132 : pasJobs[i].poMEMDS = poMEMDS;
1375 132 : pasJobs[i].eResampleAlg = eResampleAlg;
1376 132 : pasJobs[i].dfXOff = sExtraArg.dfXOff - nXOffExtract;
1377 132 : pasJobs[i].dfYOff =
1378 132 : m_adfPanToMSGT[3] +
1379 132 : (nYOff + iStartLine) * m_adfPanToMSGT[5] - nYOffExtract;
1380 132 : pasJobs[i].dfXSize = sExtraArg.dfXSize;
1381 132 : pasJobs[i].dfYSize =
1382 132 : (iNextStartLine - iStartLine) * m_adfPanToMSGT[5];
1383 264 : if (pasJobs[i].dfXOff + pasJobs[i].dfXSize >
1384 132 : aMSBands[0]->GetXSize())
1385 : {
1386 0 : pasJobs[i].dfXOff =
1387 0 : aMSBands[0]->GetXSize() - pasJobs[i].dfXSize;
1388 : }
1389 264 : if (pasJobs[i].dfYOff + pasJobs[i].dfYSize >
1390 132 : aMSBands[0]->GetYSize())
1391 : {
1392 0 : pasJobs[i].dfYOff =
1393 0 : aMSBands[0]->GetYSize() - pasJobs[i].dfYSize;
1394 : }
1395 132 : pasJobs[i].nXOff = static_cast<int>(pasJobs[i].dfXOff);
1396 132 : pasJobs[i].nYOff = static_cast<int>(pasJobs[i].dfYOff);
1397 132 : pasJobs[i].nXSize =
1398 132 : static_cast<int>(0.4999 + pasJobs[i].dfXSize);
1399 132 : pasJobs[i].nYSize =
1400 132 : static_cast<int>(0.4999 + pasJobs[i].dfYSize);
1401 132 : if (pasJobs[i].nXSize == 0)
1402 0 : pasJobs[i].nXSize = 1;
1403 132 : if (pasJobs[i].nYSize == 0)
1404 0 : pasJobs[i].nYSize = 1;
1405 132 : pasJobs[i].pBuffer = pUpsampledSpectralBuffer +
1406 132 : static_cast<size_t>(iStartLine) *
1407 132 : nXSize * nDataTypeSize;
1408 132 : pasJobs[i].eDT = eWorkDataType;
1409 132 : pasJobs[i].nBufXSize = nXSize;
1410 132 : pasJobs[i].nBufYSize =
1411 132 : static_cast<int>(iNextStartLine - iStartLine);
1412 132 : pasJobs[i].nBandCount = psOptions->nInputSpectralBands;
1413 132 : pasJobs[i].nBandSpace =
1414 132 : static_cast<GSpacing>(nXSize) * nYSize * nDataTypeSize;
1415 : #ifdef DEBUG_TIMING
1416 : pasJobs[i].ptv = &tv;
1417 : #endif
1418 132 : ahJobData[i] = &(pasJobs[i]);
1419 : }
1420 : #ifdef DEBUG_TIMING
1421 : gettimeofday(&tv, nullptr);
1422 : #endif
1423 33 : poThreadPool->SubmitJobs(PansharpenResampleJobThreadFunc,
1424 : ahJobData);
1425 33 : poThreadPool->WaitCompletion();
1426 : }
1427 : }
1428 :
1429 70 : GDALClose(poMEMDS);
1430 :
1431 70 : VSIFree(pSpectralBuffer);
1432 : }
1433 : else
1434 : {
1435 10 : if (!anInputBands.empty())
1436 : {
1437 : // Use dataset RasterIO when possible.
1438 20 : eErr = aMSBands[0]->GetDataset()->RasterIO(
1439 : GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1440 : nSpectralYSize, pUpsampledSpectralBuffer, nXSize, nYSize,
1441 10 : eWorkDataType, static_cast<int>(anInputBands.size()),
1442 10 : &anInputBands[0], 0, 0, 0, &sExtraArg);
1443 : }
1444 : else
1445 : {
1446 0 : for (int i = 0;
1447 0 : eErr == CE_None && i < psOptions->nInputSpectralBands; i++)
1448 : {
1449 0 : eErr = aMSBands[i]->RasterIO(
1450 : GF_Read, nSpectralXOff, nSpectralYOff, nSpectralXSize,
1451 : nSpectralYSize,
1452 0 : pUpsampledSpectralBuffer + static_cast<size_t>(i) * nXSize *
1453 0 : nYSize * nDataTypeSize,
1454 : nXSize, nYSize, eWorkDataType, 0, 0, &sExtraArg);
1455 : }
1456 : }
1457 10 : if (eErr != CE_None)
1458 : {
1459 0 : VSIFree(pUpsampledSpectralBuffer);
1460 0 : VSIFree(pPanBuffer);
1461 0 : return CE_Failure;
1462 : }
1463 : }
1464 :
1465 : // In case NBITS was not set on the spectral bands, clamp the values
1466 : // if overshoot might have occurred.
1467 80 : int nBitDepth = psOptions->nBitDepth;
1468 80 : if (nBitDepth &&
1469 0 : (eResampleAlg == GRIORA_Cubic || eResampleAlg == GRIORA_CubicSpline ||
1470 : eResampleAlg == GRIORA_Lanczos))
1471 : {
1472 12 : for (int i = 0; i < psOptions->nInputSpectralBands; i++)
1473 : {
1474 6 : GDALRasterBand *poBand = aMSBands[i];
1475 6 : int nBandBitDepth = 0;
1476 : const char *pszNBITS =
1477 6 : poBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE");
1478 6 : if (pszNBITS)
1479 4 : nBandBitDepth = atoi(pszNBITS);
1480 6 : if (nBandBitDepth < nBitDepth)
1481 : {
1482 2 : if (eWorkDataType == GDT_Byte)
1483 : {
1484 1 : ClampValues(
1485 : reinterpret_cast<GByte *>(pUpsampledSpectralBuffer) +
1486 1 : static_cast<size_t>(i) * nXSize * nYSize,
1487 1 : static_cast<size_t>(nXSize) * nYSize,
1488 1 : static_cast<GByte>((1 << nBitDepth) - 1));
1489 : }
1490 1 : else if (eWorkDataType == GDT_UInt16)
1491 : {
1492 1 : ClampValues(
1493 1 : reinterpret_cast<GUInt16 *>(pUpsampledSpectralBuffer) +
1494 1 : static_cast<size_t>(i) * nXSize * nYSize,
1495 1 : static_cast<size_t>(nXSize) * nYSize,
1496 1 : static_cast<GUInt16>((1 << nBitDepth) - 1));
1497 : }
1498 : #ifndef LIMIT_TYPES
1499 : else if (eWorkDataType == GDT_UInt32)
1500 : {
1501 : ClampValues(reinterpret_cast<GUInt32*>(pUpsampledSpectralBuffer) +
1502 : static_cast<size_t>(i) * nXSize * nYSize,
1503 : static_cast<size_t>(nXSize)*nYSize,
1504 : (static_cast<GUInt32>((1 << nBitDepth)-1));
1505 : }
1506 : #endif
1507 : }
1508 : }
1509 : }
1510 :
1511 80 : GUInt32 nMaxValue = (1 << nBitDepth) - 1;
1512 :
1513 80 : double *padfTempBuffer = nullptr;
1514 80 : GDALDataType eBufDataTypeOri = eBufDataType;
1515 80 : void *pDataBufOri = pDataBuf;
1516 : // CFloat64 is the query type used by gdallocationinfo...
1517 : #ifdef LIMIT_TYPES
1518 80 : if (eBufDataType != GDT_Byte && eBufDataType != GDT_UInt16)
1519 : #else
1520 : if (eBufDataType == GDT_CFloat64)
1521 : #endif
1522 : {
1523 43 : padfTempBuffer = static_cast<double *>(VSI_MALLOC3_VERBOSE(
1524 : nXSize, nYSize, psOptions->nOutPansharpenedBands * sizeof(double)));
1525 43 : if (padfTempBuffer == nullptr)
1526 : {
1527 0 : VSIFree(pUpsampledSpectralBuffer);
1528 0 : VSIFree(pPanBuffer);
1529 0 : return CE_Failure;
1530 : }
1531 43 : pDataBuf = padfTempBuffer;
1532 43 : eBufDataType = GDT_Float64;
1533 : }
1534 :
1535 80 : if (nTasks > 1)
1536 : {
1537 66 : std::vector<GDALPansharpenJob> asJobs;
1538 33 : asJobs.resize(nTasks);
1539 33 : GDALPansharpenJob *pasJobs = &(asJobs[0]);
1540 : {
1541 66 : std::vector<void *> ahJobData;
1542 33 : ahJobData.resize(nTasks);
1543 : #ifdef DEBUG_TIMING
1544 : struct timeval tv;
1545 : #endif
1546 165 : for (int i = 0; i < nTasks; i++)
1547 : {
1548 132 : const size_t iStartLine =
1549 132 : (static_cast<size_t>(i) * nYSize) / nTasks;
1550 132 : const size_t iNextStartLine =
1551 132 : (static_cast<size_t>(i + 1) * nYSize) / nTasks;
1552 132 : pasJobs[i].poPansharpenOperation = this;
1553 132 : pasJobs[i].eWorkDataType = eWorkDataType;
1554 132 : pasJobs[i].eBufDataType = eBufDataType;
1555 132 : pasJobs[i].pPanBuffer =
1556 132 : pPanBuffer + iStartLine * nXSize * nDataTypeSize;
1557 132 : pasJobs[i].pUpsampledSpectralBuffer =
1558 132 : pUpsampledSpectralBuffer +
1559 132 : iStartLine * nXSize * nDataTypeSize;
1560 132 : pasJobs[i].pDataBuf =
1561 132 : static_cast<GByte *>(pDataBuf) +
1562 264 : iStartLine * nXSize *
1563 132 : GDALGetDataTypeSizeBytes(eBufDataType);
1564 132 : pasJobs[i].nValues = (iNextStartLine - iStartLine) * nXSize;
1565 132 : pasJobs[i].nBandValues = static_cast<size_t>(nXSize) * nYSize;
1566 132 : pasJobs[i].nMaxValue = nMaxValue;
1567 : #ifdef DEBUG_TIMING
1568 : pasJobs[i].ptv = &tv;
1569 : #endif
1570 132 : ahJobData[i] = &(pasJobs[i]);
1571 : }
1572 : #ifdef DEBUG_TIMING
1573 : gettimeofday(&tv, nullptr);
1574 : #endif
1575 33 : poThreadPool->SubmitJobs(PansharpenJobThreadFunc, ahJobData);
1576 33 : poThreadPool->WaitCompletion();
1577 : }
1578 :
1579 33 : eErr = CE_None;
1580 165 : for (int i = 0; i < nTasks; i++)
1581 : {
1582 132 : if (pasJobs[i].eErr != CE_None)
1583 0 : eErr = CE_Failure;
1584 : }
1585 : }
1586 : else
1587 : {
1588 47 : eErr = PansharpenChunk(eWorkDataType, eBufDataType, pPanBuffer,
1589 : pUpsampledSpectralBuffer, pDataBuf,
1590 47 : static_cast<size_t>(nXSize) * nYSize,
1591 47 : static_cast<size_t>(nXSize) * nYSize, nMaxValue);
1592 : }
1593 :
1594 80 : if (padfTempBuffer)
1595 : {
1596 43 : GDALCopyWords64(padfTempBuffer, GDT_Float64, sizeof(double),
1597 : pDataBufOri, eBufDataTypeOri,
1598 : GDALGetDataTypeSizeBytes(eBufDataTypeOri),
1599 43 : static_cast<size_t>(nXSize) * nYSize *
1600 43 : psOptions->nOutPansharpenedBands);
1601 43 : VSIFree(padfTempBuffer);
1602 : }
1603 :
1604 80 : VSIFree(pUpsampledSpectralBuffer);
1605 80 : VSIFree(pPanBuffer);
1606 :
1607 80 : return eErr;
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* PansharpenResampleJobThreadFunc() */
1612 : /************************************************************************/
1613 :
1614 : // static int acc=0;
1615 :
1616 132 : void GDALPansharpenOperation::PansharpenResampleJobThreadFunc(void *pUserData)
1617 : {
1618 132 : GDALPansharpenResampleJob *psJob =
1619 : static_cast<GDALPansharpenResampleJob *>(pUserData);
1620 :
1621 : #ifdef DEBUG_TIMING
1622 : struct timeval tv;
1623 : gettimeofday(&tv, nullptr);
1624 : const GIntBig launch_time =
1625 : static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1626 : static_cast<GIntBig>(psJob->ptv->tv_usec);
1627 : const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1628 : static_cast<GIntBig>(tv.tv_usec);
1629 : #endif
1630 :
1631 : #if 0
1632 : for(int i=0;i<1000000;i++)
1633 : acc += i * i;
1634 : #else
1635 : GDALRasterIOExtraArg sExtraArg;
1636 132 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1637 : // cppcheck-suppress redundantAssignment
1638 132 : sExtraArg.eResampleAlg = psJob->eResampleAlg;
1639 132 : sExtraArg.bFloatingPointWindowValidity = TRUE;
1640 132 : sExtraArg.dfXOff = psJob->dfXOff;
1641 132 : sExtraArg.dfYOff = psJob->dfYOff;
1642 132 : sExtraArg.dfXSize = psJob->dfXSize;
1643 132 : sExtraArg.dfYSize = psJob->dfYSize;
1644 :
1645 132 : CPL_IGNORE_RET_VAL(psJob->poMEMDS->RasterIO(
1646 : GF_Read, psJob->nXOff, psJob->nYOff, psJob->nXSize, psJob->nYSize,
1647 : psJob->pBuffer, psJob->nBufXSize, psJob->nBufYSize, psJob->eDT,
1648 : psJob->nBandCount, nullptr, 0, 0, psJob->nBandSpace, &sExtraArg));
1649 : #endif
1650 :
1651 : #ifdef DEBUG_TIMING
1652 : struct timeval tv_end;
1653 : gettimeofday(&tv_end, nullptr);
1654 : const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1655 : static_cast<GIntBig>(tv_end.tv_usec);
1656 : if (start_job - launch_time > 500)
1657 : /*ok*/ printf("Resample: Delay before start=" CPL_FRMT_GIB
1658 : ", completion time=" CPL_FRMT_GIB "\n",
1659 : start_job - launch_time, end - start_job);
1660 : #endif
1661 132 : }
1662 :
1663 : /************************************************************************/
1664 : /* PansharpenJobThreadFunc() */
1665 : /************************************************************************/
1666 :
1667 132 : void GDALPansharpenOperation::PansharpenJobThreadFunc(void *pUserData)
1668 : {
1669 132 : GDALPansharpenJob *psJob = static_cast<GDALPansharpenJob *>(pUserData);
1670 :
1671 : #ifdef DEBUG_TIMING
1672 : struct timeval tv;
1673 : gettimeofday(&tv, nullptr);
1674 : const GIntBig launch_time =
1675 : static_cast<GIntBig>(psJob->ptv->tv_sec) * 1000000 +
1676 : static_cast<GIntBig>(psJob->ptv->tv_usec);
1677 : const GIntBig start_job = static_cast<GIntBig>(tv.tv_sec) * 1000000 +
1678 : static_cast<GIntBig>(tv.tv_usec);
1679 : #endif
1680 :
1681 : #if 0
1682 : for( int i = 0; i < 1000000; i++ )
1683 : acc += i * i;
1684 : psJob->eErr = CE_None;
1685 : #else
1686 132 : psJob->eErr = psJob->poPansharpenOperation->PansharpenChunk(
1687 : psJob->eWorkDataType, psJob->eBufDataType, psJob->pPanBuffer,
1688 : psJob->pUpsampledSpectralBuffer, psJob->pDataBuf, psJob->nValues,
1689 : psJob->nBandValues, psJob->nMaxValue);
1690 : #endif
1691 :
1692 : #ifdef DEBUG_TIMING
1693 : struct timeval tv_end;
1694 : gettimeofday(&tv_end, nullptr);
1695 : const GIntBig end = static_cast<GIntBig>(tv_end.tv_sec) * 1000000 +
1696 : static_cast<GIntBig>(tv_end.tv_usec);
1697 : if (start_job - launch_time > 500)
1698 : /*ok*/ printf("Pansharpen: Delay before start=" CPL_FRMT_GIB
1699 : ", completion time=" CPL_FRMT_GIB "\n",
1700 : start_job - launch_time, end - start_job);
1701 : #endif
1702 132 : }
1703 :
1704 : /************************************************************************/
1705 : /* PansharpenChunk() */
1706 : /************************************************************************/
1707 :
1708 179 : CPLErr GDALPansharpenOperation::PansharpenChunk(
1709 : GDALDataType eWorkDataType, GDALDataType eBufDataType,
1710 : const void *pPanBuffer, const void *pUpsampledSpectralBuffer,
1711 : void *pDataBuf, size_t nValues, size_t nBandValues, GUInt32 nMaxValue) const
1712 : {
1713 179 : CPLErr eErr = CE_None;
1714 :
1715 179 : switch (eWorkDataType)
1716 : {
1717 65 : case GDT_Byte:
1718 65 : eErr = WeightedBrovey(
1719 : static_cast<const GByte *>(pPanBuffer),
1720 : static_cast<const GByte *>(pUpsampledSpectralBuffer), pDataBuf,
1721 : eBufDataType, nValues, nBandValues,
1722 : static_cast<GByte>(nMaxValue));
1723 65 : break;
1724 :
1725 108 : case GDT_UInt16:
1726 108 : eErr = WeightedBrovey(
1727 : static_cast<const GUInt16 *>(pPanBuffer),
1728 : static_cast<const GUInt16 *>(pUpsampledSpectralBuffer),
1729 : pDataBuf, eBufDataType, nValues, nBandValues,
1730 : static_cast<GUInt16>(nMaxValue));
1731 108 : break;
1732 :
1733 : #ifndef LIMIT_TYPES
1734 : case GDT_Int8:
1735 : eErr = WeightedBrovey(
1736 : static_cast<const GInt8 *>(pPanBuffer),
1737 : static_cast<const GInt8 *>(pUpsampledSpectralBuffer), pDataBuf,
1738 : eBufDataType, nValues, nBandValues);
1739 : break;
1740 :
1741 : case GDT_Int16:
1742 : eErr = WeightedBrovey(
1743 : static_cast<const GInt16 *>(pPanBuffer),
1744 : static_cast<const GInt16 *>(pUpsampledSpectralBuffer), pDataBuf,
1745 : eBufDataType, nValues, nBandValues);
1746 : break;
1747 :
1748 : case GDT_UInt32:
1749 : eErr = WeightedBrovey(
1750 : static_cast<const GUInt32 *>(pPanBuffer),
1751 : static_cast<const GUInt32 *>(pUpsampledSpectralBuffer),
1752 : pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1753 : break;
1754 :
1755 : case GDT_Int32:
1756 : eErr = WeightedBrovey(
1757 : static_cast<const GInt32 *>(pPanBuffer),
1758 : static_cast<const GInt32 *>(pUpsampledSpectralBuffer), pDataBuf,
1759 : eBufDataType, nValues, nBandValues);
1760 : break;
1761 :
1762 : case GDT_UInt64:
1763 : eErr = WeightedBrovey(
1764 : static_cast<const std::uint64_t *>(pPanBuffer),
1765 : static_cast<const std::uint64_t *>(pUpsampledSpectralBuffer),
1766 : pDataBuf, eBufDataType, nValues, nBandValues, nMaxValue);
1767 : break;
1768 :
1769 : case GDT_Int64:
1770 : eErr = WeightedBrovey(
1771 : static_cast<const std::int64_t *>(pPanBuffer),
1772 : static_cast<const std::int64_t *>(pUpsampledSpectralBuffer),
1773 : pDataBuf, eBufDataType, nValues, nBandValues);
1774 : break;
1775 :
1776 : case GDT_Float32:
1777 : eErr = WeightedBrovey(
1778 : static_cast<const float *>(pPanBuffer),
1779 : static_cast<const float *>(pUpsampledSpectralBuffer), pDataBuf,
1780 : eBufDataType, nValues, nBandValues);
1781 : break;
1782 : #endif
1783 6 : case GDT_Float64:
1784 6 : eErr = WeightedBrovey(
1785 : static_cast<const double *>(pPanBuffer),
1786 : static_cast<const double *>(pUpsampledSpectralBuffer), pDataBuf,
1787 : eBufDataType, nValues, nBandValues);
1788 6 : break;
1789 :
1790 0 : default:
1791 0 : CPLError(CE_Failure, CPLE_NotSupported,
1792 : "eWorkDataType not supported");
1793 0 : eErr = CE_Failure;
1794 0 : break;
1795 : }
1796 :
1797 179 : return eErr;
1798 : }
1799 :
1800 : /************************************************************************/
1801 : /* GetOptions() */
1802 : /************************************************************************/
1803 :
1804 : /** Return options.
1805 : * @return options.
1806 : */
1807 110 : GDALPansharpenOptions *GDALPansharpenOperation::GetOptions()
1808 : {
1809 110 : return psOptions;
1810 : }
1811 :
1812 : /************************************************************************/
1813 : /* GDALCreatePansharpenOperation() */
1814 : /************************************************************************/
1815 :
1816 : /** Instantiate a pansharpening operation.
1817 : *
1818 : * The passed options are validated.
1819 : *
1820 : * @param psOptions a pansharpening option structure allocated with
1821 : * GDALCreatePansharpenOptions(). It is duplicated by this function.
1822 : * @return a valid pansharpening operation handle, or NULL in case of failure.
1823 : *
1824 : * @since GDAL 2.1
1825 : */
1826 :
1827 : GDALPansharpenOperationH
1828 0 : GDALCreatePansharpenOperation(const GDALPansharpenOptions *psOptions)
1829 : {
1830 0 : GDALPansharpenOperation *psOperation = new GDALPansharpenOperation();
1831 0 : if (psOperation->Initialize(psOptions) == CE_None)
1832 0 : return reinterpret_cast<GDALPansharpenOperationH>(psOperation);
1833 0 : delete psOperation;
1834 0 : return nullptr;
1835 : }
1836 :
1837 : /************************************************************************/
1838 : /* GDALDestroyPansharpenOperation() */
1839 : /************************************************************************/
1840 :
1841 : /** Destroy a pansharpening operation.
1842 : *
1843 : * @param hOperation a valid pansharpening operation.
1844 : *
1845 : * @since GDAL 2.1
1846 : */
1847 :
1848 0 : void GDALDestroyPansharpenOperation(GDALPansharpenOperationH hOperation)
1849 : {
1850 0 : delete reinterpret_cast<GDALPansharpenOperation *>(hOperation);
1851 0 : }
1852 :
1853 : /************************************************************************/
1854 : /* GDALPansharpenProcessRegion() */
1855 : /************************************************************************/
1856 :
1857 : /** Executes a pansharpening operation on a rectangular region of the
1858 : * resulting dataset.
1859 : *
1860 : * The window is expressed with respect to the dimensions of the panchromatic
1861 : * band.
1862 : *
1863 : * Spectral bands are upsampled and merged with the panchromatic band according
1864 : * to the select algorithm and options.
1865 : *
1866 : * @param hOperation a valid pansharpening operation.
1867 : * @param nXOff pixel offset.
1868 : * @param nYOff pixel offset.
1869 : * @param nXSize width of the pansharpened region to compute.
1870 : * @param nYSize height of the pansharpened region to compute.
1871 : * @param pDataBuf output buffer. Must be nXSize * nYSize *
1872 : * GDALGetDataTypeSizeBytes(eBufDataType) *
1873 : * psOptions->nOutPansharpenedBands large.
1874 : * It begins with all values of the first output band, followed
1875 : * by values of the second output band, etc...
1876 : * @param eBufDataType data type of the output buffer
1877 : *
1878 : * @return CE_None in case of success, CE_Failure in case of failure.
1879 : *
1880 : * @since GDAL 2.1
1881 : */
1882 0 : CPLErr GDALPansharpenProcessRegion(GDALPansharpenOperationH hOperation,
1883 : int nXOff, int nYOff, int nXSize, int nYSize,
1884 : void *pDataBuf, GDALDataType eBufDataType)
1885 : {
1886 : return reinterpret_cast<GDALPansharpenOperation *>(hOperation)
1887 0 : ->ProcessRegion(nXOff, nYOff, nXSize, nYSize, pDataBuf, eBufDataType);
1888 : }
|