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