Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line application to do image enhancement.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : * ****************************************************************************
8 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2011, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_string.h"
15 : #include "cpl_conv.h"
16 : #include "cpl_multiproc.h"
17 : #include "gdal_version.h"
18 : #include "gdal.h"
19 : #include "gdal_cpp_functions.h"
20 : #include "vrtdataset.h"
21 : #include "commonutils.h"
22 :
23 : #include <algorithm>
24 :
25 : static int ComputeEqualizationLUTs(GDALDatasetH hDataset, int nLUTBins,
26 : double **ppadfScaleMin,
27 : double **padfScaleMax, int ***ppapanLUTs,
28 : GDALProgressFunc pfnProgress);
29 :
30 : static CPLErr ReadLUTs(const char *pszConfigFile, int nBandCount, int nLUTBins,
31 : int ***ppapanLUTs, double **ppadfScaleMin,
32 : double **ppadfScaleMax);
33 : static void WriteLUTs(int **papanLUTs, int nBandCount, int nLUTBins,
34 : double *padfScaleMin, double *padfScaleMax,
35 : const char *pszConfigFile);
36 : static CPLErr WriteEnhanced(GDALDatasetH hDataset, int **papanLUTs,
37 : int nLUTBins, double *padfScaleMin,
38 : double *padfScaleMax, GDALDataType eOutputType,
39 : GDALDriverH hDriver, const char *pszDest,
40 : char **papszCreateOptions,
41 : GDALProgressFunc pfnProgress);
42 :
43 : static CPLErr EnhancerCallback(void *hCBData, int nXOff, int nYOff, int nXSize,
44 : int nYSize, void *pData);
45 :
46 : typedef struct
47 : {
48 : GDALRasterBand *poSrcBand;
49 : GDALDataType eWrkType;
50 : double dfScaleMin;
51 : double dfScaleMax;
52 : int nLUTBins;
53 : const int *panLUT;
54 : } EnhanceCBInfo;
55 :
56 : /* ******************************************************************** */
57 : /* Usage() */
58 : /* ******************************************************************** */
59 :
60 1 : static void Usage()
61 :
62 : {
63 1 : printf("Usage: gdalenhance [--help] [--help-general]\n"
64 : " [-of <format>] [-co <NAME>=<VALUE>]...\n"
65 : " [-ot {Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/\n"
66 : " CInt16/CInt32/CFloat32/CFloat64}]\n"
67 : // " [-src_scale[_n] src_min src_max]\n"
68 : // " [-dst_scale[_n] dst_min dst_max]\n"
69 : // " [-lutbins count]\n"
70 : // " [-s_nodata[_n] value]\n"
71 : // " [-stddev multiplier]\n"
72 : " [-equalize]\n"
73 : " [-config <filename>]\n"
74 : " <src_dataset> <dst_dataset>\n\n");
75 1 : printf("%s\n\n", GDALVersionInfo("--version"));
76 1 : exit(1);
77 : }
78 :
79 : /************************************************************************/
80 : /* ProxyMain() */
81 : /************************************************************************/
82 :
83 10 : MAIN_START(argc, argv)
84 :
85 : {
86 10 : GDALDatasetH hDataset = nullptr;
87 10 : const char *pszSource = nullptr, *pszDest = nullptr, *pszFormat = nullptr;
88 10 : GDALDriverH hDriver = nullptr;
89 10 : GDALDataType eOutputType = GDT_Unknown;
90 10 : char **papszCreateOptions = nullptr;
91 10 : GDALProgressFunc pfnProgress = GDALTermProgress;
92 10 : int nBandCount = 0;
93 10 : int nLUTBins = 256;
94 10 : const char *pszMethod = "minmax";
95 : // double dfStdDevMult = 0.0;
96 10 : double *padfScaleMin = nullptr;
97 10 : double *padfScaleMax = nullptr;
98 10 : int **papanLUTs = nullptr;
99 10 : const char *pszConfigFile = nullptr;
100 10 : int nRetCode = 0;
101 :
102 : /* Check strict compilation and runtime library version as we use C++ API */
103 10 : if (!GDAL_CHECK_VERSION(argv[0]))
104 0 : exit(1);
105 : /* -------------------------------------------------------------------- */
106 : /* Register standard GDAL drivers, and process generic GDAL */
107 : /* command options. */
108 : /* -------------------------------------------------------------------- */
109 10 : GDALAllRegister();
110 10 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
111 10 : if (argc < 1)
112 : {
113 1 : GDALDestroyDriverManager();
114 1 : exit(0);
115 : }
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Handle command line arguments. */
119 : /* -------------------------------------------------------------------- */
120 42 : for (int i = 1; i < argc; i++)
121 : {
122 33 : if (EQUAL(argv[i], "--utility_version"))
123 : {
124 0 : printf("%s was compiled against GDAL %s and is running against "
125 : "GDAL %s\n",
126 : argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
127 0 : goto exit;
128 : }
129 33 : else if (EQUAL(argv[i], "--help"))
130 : {
131 0 : Usage();
132 : }
133 33 : else if (i < argc - 1 &&
134 27 : (EQUAL(argv[i], "-of") || EQUAL(argv[i], "-f")))
135 : {
136 0 : pszFormat = argv[++i];
137 : }
138 :
139 33 : else if (i < argc - 1 && EQUAL(argv[i], "-ot"))
140 : {
141 17 : for (int iType = 1; iType < GDT_TypeCount; iType++)
142 : {
143 16 : if (GDALGetDataTypeName(static_cast<GDALDataType>(iType)) !=
144 32 : nullptr &&
145 16 : EQUAL(GDALGetDataTypeName(static_cast<GDALDataType>(iType)),
146 : argv[i + 1]))
147 : {
148 1 : eOutputType = static_cast<GDALDataType>(iType);
149 : }
150 : }
151 :
152 1 : if (eOutputType == GDT_Unknown)
153 : {
154 0 : printf("Unknown output pixel type: %s\n", argv[i + 1]);
155 0 : Usage();
156 : }
157 1 : i++;
158 : }
159 :
160 32 : else if (STARTS_WITH_CI(argv[i], "-s_nodata"))
161 : {
162 : // TODO
163 0 : i += 1;
164 : }
165 :
166 32 : else if (i < argc - 1 && EQUAL(argv[i], "-co"))
167 : {
168 1 : papszCreateOptions = CSLAddString(papszCreateOptions, argv[++i]);
169 : }
170 :
171 31 : else if (i < argc - 1 && STARTS_WITH_CI(argv[i], "-src_scale"))
172 : {
173 : // TODO
174 0 : i += 2;
175 : }
176 :
177 31 : else if (i < argc - 2 && STARTS_WITH_CI(argv[i], "-dst_scale"))
178 : {
179 : // TODO
180 0 : i += 2;
181 : }
182 :
183 31 : else if (i < argc - 1 && EQUAL(argv[i], "-config"))
184 : {
185 5 : pszConfigFile = argv[++i];
186 : }
187 :
188 26 : else if (EQUAL(argv[i], "-equalize"))
189 : {
190 4 : pszMethod = "equalize";
191 : }
192 :
193 22 : else if (EQUAL(argv[i], "-quiet"))
194 : {
195 6 : pfnProgress = GDALDummyProgress;
196 : }
197 :
198 16 : else if (argv[i][0] == '-')
199 : {
200 0 : printf("Option %s incomplete, or not recognised.\n\n", argv[i]);
201 0 : Usage();
202 : }
203 16 : else if (pszSource == nullptr)
204 : {
205 9 : pszSource = argv[i];
206 : }
207 7 : else if (pszDest == nullptr)
208 : {
209 7 : pszDest = argv[i];
210 : }
211 :
212 : else
213 : {
214 0 : printf("Too many command options.\n\n");
215 0 : Usage();
216 : }
217 : }
218 :
219 9 : if (pszSource == nullptr)
220 : {
221 0 : Usage();
222 : }
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Attempt to open source file. */
226 : /* -------------------------------------------------------------------- */
227 :
228 9 : hDataset = GDALOpenShared(pszSource, GA_ReadOnly);
229 :
230 9 : if (hDataset == nullptr)
231 : {
232 0 : fprintf(stderr, "GDALOpen failed - %d\n%s\n", CPLGetLastErrorNo(),
233 : CPLGetLastErrorMsg());
234 0 : goto exit;
235 : }
236 :
237 9 : nBandCount = GDALGetRasterCount(hDataset);
238 :
239 : /* -------------------------------------------------------------------- */
240 : /* Find the output driver. */
241 : /* -------------------------------------------------------------------- */
242 : {
243 9 : CPLString osFormat;
244 9 : if (pszFormat == nullptr && pszDest != nullptr)
245 : {
246 7 : osFormat = GetOutputDriverForRaster(pszDest);
247 7 : if (osFormat.empty())
248 : {
249 0 : GDALDestroyDriverManager();
250 0 : exit(1);
251 : }
252 : }
253 2 : else if (pszFormat != nullptr)
254 : {
255 0 : osFormat = pszFormat;
256 : }
257 9 : if (EQUAL(osFormat.c_str(), "VRT"))
258 : {
259 0 : printf("Output format VRT is not supported\n");
260 0 : GDALDestroyDriverManager();
261 0 : exit(1);
262 : }
263 :
264 9 : if (!osFormat.empty())
265 : {
266 7 : hDriver = GDALGetDriverByName(osFormat);
267 7 : if (hDriver == nullptr)
268 : {
269 : int iDr;
270 :
271 0 : printf("Output driver `%s' not recognised.\n",
272 : osFormat.c_str());
273 0 : printf("The following format drivers are enabled and support "
274 : "writing:\n");
275 0 : for (iDr = 0; iDr < GDALGetDriverCount(); iDr++)
276 : {
277 0 : hDriver = GDALGetDriver(iDr);
278 :
279 0 : if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
280 0 : nullptr) != nullptr &&
281 0 : (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
282 0 : nullptr) != nullptr ||
283 0 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY,
284 : nullptr) != nullptr))
285 : {
286 0 : printf(" %s: %s\n", GDALGetDriverShortName(hDriver),
287 : GDALGetDriverLongName(hDriver));
288 : }
289 : }
290 0 : printf("\n");
291 0 : goto exit;
292 : }
293 : }
294 : }
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* If histogram equalization is requested, do it now. */
298 : /* -------------------------------------------------------------------- */
299 9 : if (EQUAL(pszMethod, "equalize"))
300 : {
301 4 : ComputeEqualizationLUTs(hDataset, nLUTBins, &padfScaleMin,
302 : &padfScaleMax, &papanLUTs, pfnProgress);
303 : }
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* If we have a config file, assume it is for input and read */
307 : /* it. */
308 : /* -------------------------------------------------------------------- */
309 5 : else if (pszConfigFile != nullptr)
310 : {
311 4 : if (ReadLUTs(pszConfigFile, nBandCount, nLUTBins, &papanLUTs,
312 4 : &padfScaleMin, &padfScaleMax) != CE_None)
313 : {
314 2 : nRetCode = 1;
315 2 : goto exit;
316 : }
317 : }
318 :
319 7 : if (padfScaleMin == nullptr || padfScaleMax == nullptr)
320 : {
321 1 : fprintf(stderr, "-equalize or -config filename command line options "
322 : "must be specified.\n");
323 1 : Usage();
324 : }
325 :
326 : /* -------------------------------------------------------------------- */
327 : /* If there is no destination, just report the scaling values */
328 : /* and luts. */
329 : /* -------------------------------------------------------------------- */
330 6 : if (pszDest == nullptr)
331 : {
332 2 : WriteLUTs(papanLUTs, nBandCount, nLUTBins, padfScaleMin, padfScaleMax,
333 : pszConfigFile);
334 : }
335 : else
336 : {
337 4 : if (WriteEnhanced(hDataset, papanLUTs, nLUTBins, padfScaleMin,
338 : padfScaleMax, eOutputType, hDriver, pszDest,
339 4 : papszCreateOptions, pfnProgress) != CE_None)
340 : {
341 1 : nRetCode = 1;
342 : }
343 : }
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Cleanup and exit. */
347 : /* -------------------------------------------------------------------- */
348 3 : exit:
349 8 : GDALClose(hDataset);
350 8 : GDALDumpOpenDatasets(stderr);
351 8 : GDALDestroyDriverManager();
352 8 : CSLDestroy(argv);
353 8 : CSLDestroy(papszCreateOptions);
354 8 : if (papanLUTs)
355 : {
356 28 : for (int iBand = 0; iBand < nBandCount; iBand++)
357 : {
358 21 : CPLFree(papanLUTs[iBand]);
359 : }
360 7 : CPLFree(papanLUTs);
361 : }
362 8 : CPLFree(padfScaleMin);
363 8 : CPLFree(padfScaleMax);
364 :
365 8 : exit(nRetCode);
366 : }
367 :
368 0 : MAIN_END
369 :
370 : /************************************************************************/
371 : /* ComputeEqualizationLUTs() */
372 : /* */
373 : /* Get an image histogram, and compute equalization luts from */
374 : /* it. */
375 : /************************************************************************/
376 :
377 4 : static int ComputeEqualizationLUTs(GDALDatasetH hDataset, int nLUTBins,
378 : double **ppadfScaleMin,
379 : double **ppadfScaleMax, int ***ppapanLUTs,
380 : GDALProgressFunc pfnProgress)
381 :
382 : {
383 4 : int nBandCount = GDALGetRasterCount(hDataset);
384 :
385 : // For now we always compute min/max
386 4 : *ppadfScaleMin =
387 4 : static_cast<double *>(CPLCalloc(sizeof(double), nBandCount));
388 4 : *ppadfScaleMax =
389 4 : static_cast<double *>(CPLCalloc(sizeof(double), nBandCount));
390 :
391 4 : *ppapanLUTs = static_cast<int **>(CPLCalloc(sizeof(int *), nBandCount));
392 :
393 : /* ==================================================================== */
394 : /* Process all bands. */
395 : /* ==================================================================== */
396 16 : for (int iBand = 0; iBand < nBandCount; iBand++)
397 : {
398 12 : GDALRasterBandH hBand = GDALGetRasterBand(hDataset, iBand + 1);
399 12 : GUIntBig *panHistogram = nullptr;
400 12 : int nHistSize = 0;
401 :
402 : /* ----------------------------------------------------------------- */
403 : /* Get a reasonable histogram. */
404 : /* ----------------------------------------------------------------- */
405 24 : const CPLErr eErr = GDALGetDefaultHistogramEx(
406 12 : hBand, *ppadfScaleMin + iBand, *ppadfScaleMax + iBand, &nHistSize,
407 : &panHistogram, TRUE, pfnProgress, nullptr);
408 :
409 12 : if (eErr != CE_None)
410 0 : return FALSE;
411 :
412 12 : panHistogram[0] = 0; // zero out extremes (nodata, etc)
413 12 : panHistogram[nHistSize - 1] = 0;
414 :
415 : /* ----------------------------------------------------------------- */
416 : /* Total histogram count, and build cumulative histogram. */
417 : /* We take care to use big integers as there may be more than 4 */
418 : /* Gigapixels. */
419 : /* ----------------------------------------------------------------- */
420 : GUIntBig *panCumHist = static_cast<GUIntBig *>(
421 12 : VSI_CALLOC_VERBOSE(sizeof(GUIntBig), nHistSize));
422 12 : if (!panCumHist)
423 0 : return FALSE;
424 12 : GUIntBig nTotal = 0;
425 :
426 3084 : for (int iHist = 0; iHist < nHistSize; iHist++)
427 : {
428 3072 : panCumHist[iHist] = nTotal + panHistogram[iHist] / 2;
429 3072 : nTotal += panHistogram[iHist];
430 : }
431 :
432 12 : CPLFree(panHistogram);
433 :
434 12 : if (nTotal == 0)
435 : {
436 0 : CPLError(CE_Warning, CPLE_AppDefined,
437 : "Zero value entries in histogram, results will not be "
438 : "meaningful.");
439 0 : nTotal = 1;
440 : }
441 :
442 : /* ----------------------------------------------------------------- */
443 : /* Now compute a LUT from the cumulative histogram. */
444 : /* ----------------------------------------------------------------- */
445 : int *panLUT =
446 12 : static_cast<int *>(VSI_CALLOC_VERBOSE(sizeof(int), nLUTBins));
447 12 : if (!panLUT)
448 : {
449 0 : CPLFree(panCumHist);
450 0 : return FALSE;
451 : }
452 :
453 3084 : for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
454 : {
455 3072 : const int iHist = static_cast<int>(
456 3072 : (static_cast<int64_t>(iLUT) * nHistSize) / nLUTBins);
457 3072 : const int nValue =
458 3072 : static_cast<int>((panCumHist[iHist] * nLUTBins) / nTotal);
459 :
460 3072 : panLUT[iLUT] = std::max(0, std::min(nLUTBins - 1, nValue));
461 : }
462 :
463 12 : CPLFree(panCumHist);
464 :
465 12 : (*ppapanLUTs)[iBand] = panLUT;
466 : }
467 :
468 4 : return TRUE;
469 : }
470 :
471 : /************************************************************************/
472 : /* EnhancerCallback() */
473 : /* */
474 : /* This is the VRT callback that actually does the image rescaling.*/
475 : /************************************************************************/
476 :
477 10 : static CPLErr EnhancerCallback(void *hCBData, int nXOff, int nYOff, int nXSize,
478 : int nYSize, void *pData)
479 :
480 : {
481 10 : const EnhanceCBInfo *psEInfo = static_cast<const EnhanceCBInfo *>(hCBData);
482 :
483 10 : if (psEInfo->eWrkType != GDT_Byte)
484 : {
485 1 : CPLError(CE_Failure, CPLE_AppDefined,
486 : "Currently gdalenhance only supports Byte output.");
487 1 : return CE_Failure;
488 : }
489 :
490 9 : GByte *pabyOutImage = static_cast<GByte *>(pData);
491 : float *pafSrcImage = static_cast<float *>(
492 9 : VSI_MALLOC3_VERBOSE(sizeof(float), nXSize, nYSize));
493 9 : if (!pafSrcImage)
494 0 : return CE_Failure;
495 :
496 9 : CPLErr eErr = psEInfo->poSrcBand->RasterIO(
497 : GF_Read, nXOff, nYOff, nXSize, nYSize, pafSrcImage, nXSize, nYSize,
498 : GDT_Float32, 0, 0, nullptr);
499 :
500 9 : if (eErr != CE_None)
501 : {
502 0 : CPLFree(pafSrcImage);
503 0 : return eErr;
504 : }
505 :
506 9 : const size_t nPixelCount = static_cast<size_t>(nXSize) * nYSize;
507 : int bHaveNoData;
508 : const float fNoData =
509 9 : static_cast<float>(psEInfo->poSrcBand->GetNoDataValue(&bHaveNoData));
510 9 : const double dfScale =
511 9 : psEInfo->nLUTBins / (psEInfo->dfScaleMax - psEInfo->dfScaleMin);
512 :
513 22509 : for (size_t iPixel = 0; iPixel < nPixelCount; iPixel++)
514 : {
515 22500 : if (bHaveNoData && pafSrcImage[iPixel] == fNoData)
516 : {
517 0 : pabyOutImage[iPixel] = static_cast<GByte>(fNoData);
518 0 : continue;
519 : }
520 :
521 22500 : const double dfBin =
522 22500 : (pafSrcImage[iPixel] - psEInfo->dfScaleMin) * dfScale;
523 22500 : int iBin = 0;
524 22500 : if (!(dfBin > 0))
525 : {
526 : // nothing to do
527 : }
528 22500 : else if (!(dfBin < psEInfo->nLUTBins - 1))
529 : {
530 0 : iBin = psEInfo->nLUTBins - 1;
531 : }
532 : else
533 : {
534 22500 : iBin = static_cast<int>(dfBin);
535 : }
536 :
537 22500 : if (psEInfo->panLUT)
538 22500 : pabyOutImage[iPixel] = static_cast<GByte>(psEInfo->panLUT[iBin]);
539 : else
540 0 : pabyOutImage[iPixel] = static_cast<GByte>(iBin);
541 : }
542 :
543 9 : CPLFree(pafSrcImage);
544 :
545 9 : return CE_None;
546 : }
547 :
548 : /************************************************************************/
549 : /* ReadLUTs() */
550 : /* */
551 : /* Read a LUT for each band from a file. */
552 : /************************************************************************/
553 :
554 4 : CPLErr ReadLUTs(const char *pszConfigFile, int nBandCount, int nLUTBins,
555 : int ***ppapanLUTs, double **ppadfScaleMin,
556 : double **ppadfScaleMax)
557 : {
558 8 : const CPLStringList aosLines(CSLLoad(pszConfigFile));
559 :
560 4 : if (aosLines.size() != nBandCount)
561 : {
562 1 : CPLError(CE_Failure, CPLE_AppDefined,
563 : "Did not get %d lines in config file as expected.\n",
564 : nBandCount);
565 1 : return CE_Failure;
566 : }
567 :
568 3 : *ppadfScaleMin =
569 3 : static_cast<double *>(CPLCalloc(nBandCount, sizeof(double)));
570 3 : *ppadfScaleMax =
571 3 : static_cast<double *>(CPLCalloc(nBandCount, sizeof(double)));
572 3 : *ppapanLUTs = static_cast<int **>(CPLCalloc(sizeof(int *), nBandCount));
573 :
574 10 : for (int iBand = 0; iBand < nBandCount; iBand++)
575 : {
576 8 : const CPLStringList aosTokens(CSLTokenizeString(aosLines[iBand]));
577 :
578 15 : if (aosTokens.size() < (nLUTBins + 3) ||
579 7 : atoi(aosTokens[0]) != iBand + 1)
580 : {
581 1 : CPLError(CE_Failure, CPLE_AppDefined,
582 : "Line %d seems to be corrupt.\n", iBand + 1);
583 1 : return CE_Failure;
584 : }
585 :
586 : // Process scale min/max
587 :
588 7 : (*ppadfScaleMin)[iBand] = CPLAtof(aosTokens[1]);
589 7 : (*ppadfScaleMax)[iBand] = CPLAtof(aosTokens[2]);
590 :
591 : // process lut
592 :
593 14 : (*ppapanLUTs)[iBand] =
594 7 : static_cast<int *>(CPLCalloc(nLUTBins, sizeof(int)));
595 :
596 1799 : for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
597 1792 : (*ppapanLUTs)[iBand][iLUT] = atoi(aosTokens[iLUT + 3]);
598 : }
599 :
600 2 : return CE_None;
601 : }
602 :
603 : /************************************************************************/
604 : /* WriteLUTs() */
605 : /* */
606 : /* Write the LUT for each band to a file or stdout. */
607 : /************************************************************************/
608 :
609 2 : void WriteLUTs(int **papanLUTs, int nBandCount, int nLUTBins,
610 : double *padfScaleMin, double *padfScaleMax,
611 : const char *pszConfigFile)
612 : {
613 2 : FILE *fpConfig = stdout;
614 2 : if (pszConfigFile)
615 1 : fpConfig = fopen(pszConfigFile, "w");
616 :
617 8 : for (int iBand = 0; iBand < nBandCount; iBand++)
618 : {
619 6 : fprintf(fpConfig, "%d:Band ", iBand + 1);
620 6 : fprintf(fpConfig, "%g:ScaleMin %g:ScaleMax ", padfScaleMin[iBand],
621 6 : padfScaleMax[iBand]);
622 :
623 6 : if (papanLUTs)
624 : {
625 1542 : for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
626 1536 : fprintf(fpConfig, "%d ", papanLUTs[iBand][iLUT]);
627 : }
628 6 : fprintf(fpConfig, "\n");
629 : }
630 :
631 2 : if (pszConfigFile)
632 1 : fclose(fpConfig);
633 2 : }
634 :
635 : /************************************************************************/
636 : /* WriteEnhanced() */
637 : /* */
638 : /* Write an enhanced image using the provided LUTs. */
639 : /************************************************************************/
640 :
641 4 : CPLErr WriteEnhanced(GDALDatasetH hDataset, int **papanLUTs, int nLUTBins,
642 : double *padfScaleMin, double *padfScaleMax,
643 : GDALDataType eOutputType, GDALDriverH hDriver,
644 : const char *pszDest, char **papszCreateOptions,
645 : GDALProgressFunc pfnProgress)
646 : {
647 4 : int nBandCount = GDALGetRasterCount(hDataset);
648 :
649 : EnhanceCBInfo *pasEInfo = static_cast<EnhanceCBInfo *>(
650 4 : CPLCalloc(nBandCount, sizeof(EnhanceCBInfo)));
651 :
652 : /* -------------------------------------------------------------------- */
653 : /* Make a virtual clone. */
654 : /* -pixe------------------------------------------------------------------- */
655 4 : VRTDataset *poVDS = new VRTDataset(GDALGetRasterXSize(hDataset),
656 4 : GDALGetRasterYSize(hDataset));
657 :
658 4 : if (GDALGetGCPCount(hDataset) == 0)
659 : {
660 4 : const char *pszProjection = GDALGetProjectionRef(hDataset);
661 4 : if (pszProjection != nullptr && strlen(pszProjection) > 0)
662 4 : poVDS->SetProjection(pszProjection);
663 :
664 4 : GDALGeoTransform gt;
665 4 : if (GDALDataset::FromHandle(hDataset)->GetGeoTransform(gt) == CE_None)
666 4 : poVDS->SetGeoTransform(gt);
667 : }
668 : else
669 : {
670 0 : poVDS->SetGCPs(GDALGetGCPCount(hDataset), GDALGetGCPs(hDataset),
671 : GDALGetGCPProjection(hDataset));
672 : }
673 :
674 4 : poVDS->SetMetadata(GDALDataset::FromHandle(hDataset)->GetMetadata());
675 :
676 16 : for (int iBand = 0; iBand < nBandCount; iBand++)
677 : {
678 : VRTSourcedRasterBand *poVRTBand;
679 : GDALRasterBand *poSrcBand;
680 : GDALDataType eBandType;
681 :
682 12 : poSrcBand = GDALDataset::FromHandle(hDataset)->GetRasterBand(iBand + 1);
683 :
684 : /* ---------------------------------------------------------------- */
685 : /* Select output data type to match source. */
686 : /* ---------------------------------------------------------------- */
687 12 : if (eOutputType == GDT_Unknown)
688 9 : eBandType = GDT_Byte;
689 : else
690 3 : eBandType = eOutputType;
691 :
692 : /* ---------------------------------------------------------------- */
693 : /* Create this band. */
694 : /* ---------------------------------------------------------------- */
695 12 : poVDS->AddBand(eBandType, nullptr);
696 12 : poVRTBand = cpl::down_cast<VRTSourcedRasterBand *>(
697 : poVDS->GetRasterBand(iBand + 1));
698 :
699 : /* ---------------------------------------------------------------- */
700 : /* Create a function based source with info on how to apply the */
701 : /* enhancement. */
702 : /* ---------------------------------------------------------------- */
703 12 : pasEInfo[iBand].poSrcBand = poSrcBand;
704 12 : pasEInfo[iBand].eWrkType = eBandType;
705 12 : pasEInfo[iBand].dfScaleMin = padfScaleMin[iBand];
706 12 : pasEInfo[iBand].dfScaleMax = padfScaleMax[iBand];
707 12 : pasEInfo[iBand].nLUTBins = nLUTBins;
708 :
709 12 : if (papanLUTs)
710 12 : pasEInfo[iBand].panLUT = papanLUTs[iBand];
711 :
712 12 : poVRTBand->AddFuncSource(EnhancerCallback, pasEInfo + iBand);
713 :
714 : /* ---------------------------------------------------------------- */
715 : /* copy over some other information of interest. */
716 : /* ---------------------------------------------------------------- */
717 12 : poVRTBand->SetColorInterpretation(poSrcBand->GetColorInterpretation());
718 12 : if (strlen(poSrcBand->GetDescription()) > 0)
719 0 : poVRTBand->SetDescription(poSrcBand->GetDescription());
720 12 : if (poSrcBand->GetRasterDataType() == poVRTBand->GetRasterDataType())
721 9 : GDALCopyNoDataValue(poVRTBand, poSrcBand);
722 : }
723 :
724 : /* -------------------------------------------------------------------- */
725 : /* Write to the output file using CopyCreate(). */
726 : /* -------------------------------------------------------------------- */
727 : GDALDatasetH hOutDS =
728 4 : GDALCreateCopy(hDriver, pszDest, static_cast<GDALDatasetH>(poVDS),
729 : FALSE, papszCreateOptions, pfnProgress, nullptr);
730 4 : CPLErr eErr = CE_None;
731 4 : if (hOutDS == nullptr)
732 : {
733 1 : eErr = CE_Failure;
734 : }
735 : else
736 : {
737 3 : GDALClose(hOutDS);
738 : }
739 :
740 4 : GDALClose(poVDS);
741 4 : CPLFree(pasEInfo);
742 :
743 4 : return eErr;
744 : }
|