LCOV - code coverage report
Current view: top level - apps - gdalenhance.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 222 264 84.1 %
Date: 2024-11-21 22:18:42 Functions: 7 7 100.0 %

          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           0 :         GDALDestroyDriverManager();
     113           0 :         exit(0);
     114             :     }
     115             : 
     116             :     /* -------------------------------------------------------------------- */
     117             :     /*      Handle command line arguments.                                  */
     118             :     /* -------------------------------------------------------------------- */
     119          43 :     for (int i = 1; i < argc; i++)
     120             :     {
     121          34 :         if (EQUAL(argv[i], "--utility_version"))
     122             :         {
     123           1 :             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           1 :             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          15 :             for (int iType = 1; iType < GDT_TypeCount; iType++)
     141             :             {
     142          14 :                 if (GDALGetDataTypeName(static_cast<GDALDataType>(iType)) !=
     143          28 :                         nullptr &&
     144          14 :                     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(
     267             :                     "The following format drivers are configured and support "
     268             :                     "output:\n");
     269           0 :                 for (iDr = 0; iDr < GDALGetDriverCount(); iDr++)
     270             :                 {
     271           0 :                     hDriver = GDALGetDriver(iDr);
     272             : 
     273           0 :                     if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
     274           0 :                                             nullptr) != nullptr &&
     275           0 :                         (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
     276           0 :                                              nullptr) != nullptr ||
     277           0 :                          GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY,
     278             :                                              nullptr) != nullptr))
     279             :                     {
     280           0 :                         printf("  %s: %s\n", GDALGetDriverShortName(hDriver),
     281             :                                GDALGetDriverLongName(hDriver));
     282             :                     }
     283             :                 }
     284           0 :                 printf("\n");
     285           0 :                 goto exit;
     286             :             }
     287             :         }
     288             :     }
     289             : 
     290             :     /* -------------------------------------------------------------------- */
     291             :     /*      If histogram equalization is requested, do it now.              */
     292             :     /* -------------------------------------------------------------------- */
     293           9 :     if (EQUAL(pszMethod, "equalize"))
     294             :     {
     295           4 :         ComputeEqualizationLUTs(hDataset, nLUTBins, &padfScaleMin,
     296             :                                 &padfScaleMax, &papanLUTs, pfnProgress);
     297             :     }
     298             : 
     299             :     /* -------------------------------------------------------------------- */
     300             :     /*      If we have a config file, assume it is for input and read       */
     301             :     /*      it.                                                             */
     302             :     /* -------------------------------------------------------------------- */
     303           5 :     else if (pszConfigFile != nullptr)
     304             :     {
     305           4 :         if (ReadLUTs(pszConfigFile, nBandCount, nLUTBins, &papanLUTs,
     306           4 :                      &padfScaleMin, &padfScaleMax) != CE_None)
     307             :         {
     308           2 :             nRetCode = 1;
     309           2 :             goto exit;
     310             :         }
     311             :     }
     312             : 
     313           7 :     if (padfScaleMin == nullptr || padfScaleMax == nullptr)
     314             :     {
     315           1 :         fprintf(stderr, "-equalize or -config filename command line options "
     316             :                         "must be specified.\n");
     317           1 :         Usage();
     318             :     }
     319             : 
     320             :     /* -------------------------------------------------------------------- */
     321             :     /*      If there is no destination, just report the scaling values      */
     322             :     /*      and luts.                                                       */
     323             :     /* -------------------------------------------------------------------- */
     324           6 :     if (pszDest == nullptr)
     325             :     {
     326           2 :         WriteLUTs(papanLUTs, nBandCount, nLUTBins, padfScaleMin, padfScaleMax,
     327             :                   pszConfigFile);
     328             :     }
     329             :     else
     330             :     {
     331           4 :         if (WriteEnhanced(hDataset, papanLUTs, nLUTBins, padfScaleMin,
     332             :                           padfScaleMax, eOutputType, hDriver, pszDest,
     333           4 :                           papszCreateOptions, pfnProgress) != CE_None)
     334             :         {
     335           1 :             nRetCode = 1;
     336             :         }
     337             :     }
     338             : 
     339             :     /* -------------------------------------------------------------------- */
     340             :     /*      Cleanup and exit.                                               */
     341             :     /* -------------------------------------------------------------------- */
     342           3 : exit:
     343           9 :     GDALClose(hDataset);
     344           9 :     GDALDumpOpenDatasets(stderr);
     345           9 :     GDALDestroyDriverManager();
     346           9 :     CSLDestroy(argv);
     347           9 :     CSLDestroy(papszCreateOptions);
     348           9 :     if (papanLUTs)
     349             :     {
     350          28 :         for (int iBand = 0; iBand < nBandCount; iBand++)
     351             :         {
     352          21 :             CPLFree(papanLUTs[iBand]);
     353             :         }
     354           7 :         CPLFree(papanLUTs);
     355             :     }
     356           9 :     CPLFree(padfScaleMin);
     357           9 :     CPLFree(padfScaleMax);
     358             : 
     359           9 :     exit(nRetCode);
     360             : }
     361             : 
     362           0 : MAIN_END
     363             : 
     364             : /************************************************************************/
     365             : /*                      ComputeEqualizationLUTs()                       */
     366             : /*                                                                      */
     367             : /*      Get an image histogram, and compute equalization luts from      */
     368             : /*      it.                                                             */
     369             : /************************************************************************/
     370             : 
     371           4 : static int ComputeEqualizationLUTs(GDALDatasetH hDataset, int nLUTBins,
     372             :                                    double **ppadfScaleMin,
     373             :                                    double **ppadfScaleMax, int ***ppapanLUTs,
     374             :                                    GDALProgressFunc pfnProgress)
     375             : 
     376             : {
     377           4 :     int nBandCount = GDALGetRasterCount(hDataset);
     378             : 
     379             :     // For now we always compute min/max
     380           4 :     *ppadfScaleMin =
     381           4 :         static_cast<double *>(CPLCalloc(sizeof(double), nBandCount));
     382           4 :     *ppadfScaleMax =
     383           4 :         static_cast<double *>(CPLCalloc(sizeof(double), nBandCount));
     384             : 
     385           4 :     *ppapanLUTs = static_cast<int **>(CPLCalloc(sizeof(int *), nBandCount));
     386             : 
     387             :     /* ==================================================================== */
     388             :     /*      Process all bands.                                              */
     389             :     /* ==================================================================== */
     390          16 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     391             :     {
     392          12 :         GDALRasterBandH hBand = GDALGetRasterBand(hDataset, iBand + 1);
     393          12 :         GUIntBig *panHistogram = nullptr;
     394          12 :         int nHistSize = 0;
     395             : 
     396             :         /* ----------------------------------------------------------------- */
     397             :         /*      Get a reasonable histogram.                                  */
     398             :         /* ----------------------------------------------------------------- */
     399          24 :         CPLErr eErr = GDALGetDefaultHistogramEx(
     400          12 :             hBand, *ppadfScaleMin + iBand, *ppadfScaleMax + iBand, &nHistSize,
     401             :             &panHistogram, TRUE, pfnProgress, nullptr);
     402             : 
     403          12 :         if (eErr != CE_None)
     404           0 :             return FALSE;
     405             : 
     406          12 :         panHistogram[0] = 0;  // zero out extremes (nodata, etc)
     407          12 :         panHistogram[nHistSize - 1] = 0;
     408             : 
     409             :         /* ----------------------------------------------------------------- */
     410             :         /*      Total histogram count, and build cumulative histogram.       */
     411             :         /*      We take care to use big integers as there may be more than 4 */
     412             :         /*      Gigapixels.                                                  */
     413             :         /* ----------------------------------------------------------------- */
     414             :         GUIntBig *panCumHist =
     415          12 :             static_cast<GUIntBig *>(CPLCalloc(sizeof(GUIntBig), nHistSize));
     416          12 :         GUIntBig nTotal = 0;
     417             : 
     418        3084 :         for (int iHist = 0; iHist < nHistSize; iHist++)
     419             :         {
     420        3072 :             panCumHist[iHist] = nTotal + panHistogram[iHist] / 2;
     421        3072 :             nTotal += panHistogram[iHist];
     422             :         }
     423             : 
     424          12 :         CPLFree(panHistogram);
     425             : 
     426          12 :         if (nTotal == 0)
     427             :         {
     428           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     429             :                      "Zero value entries in histogram, results will not be "
     430             :                      "meaningful.");
     431           0 :             nTotal = 1;
     432             :         }
     433             : 
     434             :         /* ----------------------------------------------------------------- */
     435             :         /*      Now compute a LUT from the cumulative histogram.             */
     436             :         /* ----------------------------------------------------------------- */
     437          12 :         int *panLUT = static_cast<int *>(CPLCalloc(sizeof(int), nLUTBins));
     438             : 
     439        3084 :         for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
     440             :         {
     441        3072 :             int iHist = (iLUT * nHistSize) / nLUTBins;
     442        3072 :             int nValue =
     443        3072 :                 static_cast<int>((panCumHist[iHist] * nLUTBins) / nTotal);
     444             : 
     445        3072 :             panLUT[iLUT] = std::max(0, std::min(nLUTBins - 1, nValue));
     446             :         }
     447             : 
     448          12 :         CPLFree(panCumHist);
     449             : 
     450          12 :         (*ppapanLUTs)[iBand] = panLUT;
     451             :     }
     452             : 
     453           4 :     return TRUE;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                          EnhancerCallback()                          */
     458             : /*                                                                      */
     459             : /*      This is the VRT callback that actually does the image rescaling.*/
     460             : /************************************************************************/
     461             : 
     462          10 : static CPLErr EnhancerCallback(void *hCBData, int nXOff, int nYOff, int nXSize,
     463             :                                int nYSize, void *pData)
     464             : 
     465             : {
     466          10 :     const EnhanceCBInfo *psEInfo = static_cast<const EnhanceCBInfo *>(hCBData);
     467             : 
     468          10 :     if (psEInfo->eWrkType != GDT_Byte)
     469             :     {
     470           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     471             :                  "Currently gdalenhance only supports Byte output.");
     472           1 :         return CE_Failure;
     473             :     }
     474             : 
     475           9 :     GByte *pabyOutImage = static_cast<GByte *>(pData);
     476             :     float *pafSrcImage = static_cast<float *>(
     477           9 :         CPLCalloc(sizeof(float), static_cast<size_t>(nXSize) * nYSize));
     478             : 
     479           9 :     CPLErr eErr = psEInfo->poSrcBand->RasterIO(
     480             :         GF_Read, nXOff, nYOff, nXSize, nYSize, pafSrcImage, nXSize, nYSize,
     481             :         GDT_Float32, 0, 0, nullptr);
     482             : 
     483           9 :     if (eErr != CE_None)
     484             :     {
     485           0 :         CPLFree(pafSrcImage);
     486           0 :         return eErr;
     487             :     }
     488             : 
     489           9 :     int nPixelCount = nXSize * nYSize;
     490             :     int bHaveNoData;
     491             :     float fNoData =
     492           9 :         static_cast<float>(psEInfo->poSrcBand->GetNoDataValue(&bHaveNoData));
     493           9 :     double dfScale =
     494           9 :         psEInfo->nLUTBins / (psEInfo->dfScaleMax - psEInfo->dfScaleMin);
     495             : 
     496       22509 :     for (int iPixel = 0; iPixel < nPixelCount; iPixel++)
     497             :     {
     498       22500 :         if (bHaveNoData && pafSrcImage[iPixel] == fNoData)
     499             :         {
     500           0 :             pabyOutImage[iPixel] = static_cast<GByte>(fNoData);
     501           0 :             continue;
     502             :         }
     503             : 
     504       22500 :         int iBin = static_cast<int>(
     505       22500 :             (pafSrcImage[iPixel] - psEInfo->dfScaleMin) * dfScale);
     506       22500 :         iBin = std::max(0, std::min(psEInfo->nLUTBins - 1, iBin));
     507             : 
     508       22500 :         if (psEInfo->panLUT)
     509       22500 :             pabyOutImage[iPixel] = static_cast<GByte>(psEInfo->panLUT[iBin]);
     510             :         else
     511           0 :             pabyOutImage[iPixel] = static_cast<GByte>(iBin);
     512             :     }
     513             : 
     514           9 :     CPLFree(pafSrcImage);
     515             : 
     516           9 :     return CE_None;
     517             : }
     518             : 
     519             : /************************************************************************/
     520             : /*                      ReadLUTs()                                      */
     521             : /*                                                                      */
     522             : /*               Read a LUT for each band from a file.                  */
     523             : /************************************************************************/
     524             : 
     525           4 : CPLErr ReadLUTs(const char *pszConfigFile, int nBandCount, int nLUTBins,
     526             :                 int ***ppapanLUTs, double **ppadfScaleMin,
     527             :                 double **ppadfScaleMax)
     528             : {
     529           8 :     const CPLStringList aosLines(CSLLoad(pszConfigFile));
     530             : 
     531           4 :     if (aosLines.size() != nBandCount)
     532             :     {
     533           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     534             :                  "Did not get %d lines in config file as expected.\n",
     535             :                  nBandCount);
     536           1 :         return CE_Failure;
     537             :     }
     538             : 
     539           3 :     *ppadfScaleMin =
     540           3 :         static_cast<double *>(CPLCalloc(nBandCount, sizeof(double)));
     541           3 :     *ppadfScaleMax =
     542           3 :         static_cast<double *>(CPLCalloc(nBandCount, sizeof(double)));
     543           3 :     *ppapanLUTs = static_cast<int **>(CPLCalloc(sizeof(int *), nBandCount));
     544             : 
     545          10 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     546             :     {
     547           8 :         const CPLStringList aosTokens(CSLTokenizeString(aosLines[iBand]));
     548             : 
     549          15 :         if (aosTokens.size() < (nLUTBins + 3) ||
     550           7 :             atoi(aosTokens[0]) != iBand + 1)
     551             :         {
     552           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     553             :                      "Line %d seems to be corrupt.\n", iBand + 1);
     554           1 :             return CE_Failure;
     555             :         }
     556             : 
     557             :         // Process scale min/max
     558             : 
     559           7 :         (*ppadfScaleMin)[iBand] = CPLAtof(aosTokens[1]);
     560           7 :         (*ppadfScaleMax)[iBand] = CPLAtof(aosTokens[2]);
     561             : 
     562             :         // process lut
     563             : 
     564          14 :         (*ppapanLUTs)[iBand] =
     565           7 :             static_cast<int *>(CPLCalloc(nLUTBins, sizeof(int)));
     566             : 
     567        1799 :         for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
     568        1792 :             (*ppapanLUTs)[iBand][iLUT] = atoi(aosTokens[iLUT + 3]);
     569             :     }
     570             : 
     571           2 :     return CE_None;
     572             : }
     573             : 
     574             : /************************************************************************/
     575             : /*                      WriteLUTs()                                     */
     576             : /*                                                                      */
     577             : /*      Write the LUT for each band to a file or stdout.                */
     578             : /************************************************************************/
     579             : 
     580           2 : void WriteLUTs(int **papanLUTs, int nBandCount, int nLUTBins,
     581             :                double *padfScaleMin, double *padfScaleMax,
     582             :                const char *pszConfigFile)
     583             : {
     584           2 :     FILE *fpConfig = stdout;
     585           2 :     if (pszConfigFile)
     586           1 :         fpConfig = fopen(pszConfigFile, "w");
     587             : 
     588           8 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     589             :     {
     590           6 :         fprintf(fpConfig, "%d:Band ", iBand + 1);
     591           6 :         fprintf(fpConfig, "%g:ScaleMin %g:ScaleMax ", padfScaleMin[iBand],
     592           6 :                 padfScaleMax[iBand]);
     593             : 
     594           6 :         if (papanLUTs)
     595             :         {
     596        1542 :             for (int iLUT = 0; iLUT < nLUTBins; iLUT++)
     597        1536 :                 fprintf(fpConfig, "%d ", papanLUTs[iBand][iLUT]);
     598             :         }
     599           6 :         fprintf(fpConfig, "\n");
     600             :     }
     601             : 
     602           2 :     if (pszConfigFile)
     603           1 :         fclose(fpConfig);
     604           2 : }
     605             : 
     606             : /************************************************************************/
     607             : /*                      WriteEnhanced()                                 */
     608             : /*                                                                      */
     609             : /*      Write an enhanced image using the provided LUTs.                */
     610             : /************************************************************************/
     611             : 
     612           4 : CPLErr WriteEnhanced(GDALDatasetH hDataset, int **papanLUTs, int nLUTBins,
     613             :                      double *padfScaleMin, double *padfScaleMax,
     614             :                      GDALDataType eOutputType, GDALDriverH hDriver,
     615             :                      const char *pszDest, char **papszCreateOptions,
     616             :                      GDALProgressFunc pfnProgress)
     617             : {
     618           4 :     int nBandCount = GDALGetRasterCount(hDataset);
     619             : 
     620             :     EnhanceCBInfo *pasEInfo = static_cast<EnhanceCBInfo *>(
     621           4 :         CPLCalloc(nBandCount, sizeof(EnhanceCBInfo)));
     622             : 
     623             :     /* -------------------------------------------------------------------- */
     624             :     /*      Make a virtual clone.                                           */
     625             :     /* -pixe------------------------------------------------------------------- */
     626           4 :     VRTDataset *poVDS = new VRTDataset(GDALGetRasterXSize(hDataset),
     627           4 :                                        GDALGetRasterYSize(hDataset));
     628             : 
     629           4 :     if (GDALGetGCPCount(hDataset) == 0)
     630             :     {
     631             :         double adfGeoTransform[6];
     632             : 
     633           4 :         const char *pszProjection = GDALGetProjectionRef(hDataset);
     634           4 :         if (pszProjection != nullptr && strlen(pszProjection) > 0)
     635           4 :             poVDS->SetProjection(pszProjection);
     636             : 
     637           4 :         if (GDALGetGeoTransform(hDataset, adfGeoTransform) == CE_None)
     638           4 :             poVDS->SetGeoTransform(adfGeoTransform);
     639             :     }
     640             :     else
     641             :     {
     642           0 :         poVDS->SetGCPs(GDALGetGCPCount(hDataset), GDALGetGCPs(hDataset),
     643             :                        GDALGetGCPProjection(hDataset));
     644             :     }
     645             : 
     646           4 :     poVDS->SetMetadata(GDALDataset::FromHandle(hDataset)->GetMetadata());
     647             : 
     648          16 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     649             :     {
     650             :         VRTSourcedRasterBand *poVRTBand;
     651             :         GDALRasterBand *poSrcBand;
     652             :         GDALDataType eBandType;
     653             : 
     654          12 :         poSrcBand = GDALDataset::FromHandle(hDataset)->GetRasterBand(iBand + 1);
     655             : 
     656             :         /* ---------------------------------------------------------------- */
     657             :         /*      Select output data type to match source.                    */
     658             :         /* ---------------------------------------------------------------- */
     659          12 :         if (eOutputType == GDT_Unknown)
     660           9 :             eBandType = GDT_Byte;
     661             :         else
     662           3 :             eBandType = eOutputType;
     663             : 
     664             :         /* ---------------------------------------------------------------- */
     665             :         /*      Create this band.                                           */
     666             :         /* ---------------------------------------------------------------- */
     667          12 :         poVDS->AddBand(eBandType, nullptr);
     668          12 :         poVRTBand = cpl::down_cast<VRTSourcedRasterBand *>(
     669             :             poVDS->GetRasterBand(iBand + 1));
     670             : 
     671             :         /* ---------------------------------------------------------------- */
     672             :         /*     Create a function based source with info on how to apply the */
     673             :         /*     enhancement.                                                 */
     674             :         /* ---------------------------------------------------------------- */
     675          12 :         pasEInfo[iBand].poSrcBand = poSrcBand;
     676          12 :         pasEInfo[iBand].eWrkType = eBandType;
     677          12 :         pasEInfo[iBand].dfScaleMin = padfScaleMin[iBand];
     678          12 :         pasEInfo[iBand].dfScaleMax = padfScaleMax[iBand];
     679          12 :         pasEInfo[iBand].nLUTBins = nLUTBins;
     680             : 
     681          12 :         if (papanLUTs)
     682          12 :             pasEInfo[iBand].panLUT = papanLUTs[iBand];
     683             : 
     684          12 :         poVRTBand->AddFuncSource(EnhancerCallback, pasEInfo + iBand);
     685             : 
     686             :         /* ---------------------------------------------------------------- */
     687             :         /*      copy over some other information of interest.               */
     688             :         /* ---------------------------------------------------------------- */
     689          12 :         poVRTBand->CopyCommonInfoFrom(poSrcBand);
     690             :     }
     691             : 
     692             :     /* -------------------------------------------------------------------- */
     693             :     /*      Write to the output file using CopyCreate().                    */
     694             :     /* -------------------------------------------------------------------- */
     695             :     GDALDatasetH hOutDS =
     696           4 :         GDALCreateCopy(hDriver, pszDest, static_cast<GDALDatasetH>(poVDS),
     697             :                        FALSE, papszCreateOptions, pfnProgress, nullptr);
     698           4 :     CPLErr eErr = CE_None;
     699           4 :     if (hOutDS == nullptr)
     700             :     {
     701           1 :         eErr = CE_Failure;
     702             :     }
     703             :     else
     704             :     {
     705           3 :         GDALClose(hOutDS);
     706             :     }
     707             : 
     708           4 :     GDALClose(poVDS);
     709           4 :     CPLFree(pasEInfo);
     710             : 
     711           4 :     return eErr;
     712             : }

Generated by: LCOV version 1.14