LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_rgb_to_palette.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 85 85 100.0 %
Date: 2025-05-15 18:21:54 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster rgb-to-palette" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_rgb_to_palette.h"
      14             : 
      15             : #include "cpl_string.h"
      16             : #include "gdal_alg.h"
      17             : #include "gdal_priv.h"
      18             : 
      19             : //! @cond Doxygen_Suppress
      20             : 
      21             : #ifndef _
      22             : #define _(x) (x)
      23             : #endif
      24             : 
      25             : /************************************************************************/
      26             : /*                    GDALRasterRGBToPaletteAlgorithm()                 */
      27             : /************************************************************************/
      28             : 
      29          26 : GDALRasterRGBToPaletteAlgorithm::GDALRasterRGBToPaletteAlgorithm(
      30          26 :     bool standaloneStep)
      31             :     : GDALRasterPipelineNonNativelyStreamingAlgorithm(NAME, DESCRIPTION,
      32          26 :                                                       HELP_URL, standaloneStep)
      33             : {
      34             :     AddArg("color-count", 0,
      35             :            _("Select the number of colors in the generated color table"),
      36          52 :            &m_colorCount)
      37          26 :         .SetDefault(m_colorCount)
      38          26 :         .SetMinValueIncluded(2)
      39          26 :         .SetMaxValueIncluded(256);
      40          26 :     AddArg("color-map", 0, _("Color map filename"), &m_colorMap);
      41          26 : }
      42             : 
      43             : /************************************************************************/
      44             : /*                GDALRasterRGBToPaletteAlgorithm::RunStep()            */
      45             : /************************************************************************/
      46             : 
      47          18 : bool GDALRasterRGBToPaletteAlgorithm::RunStep(GDALProgressFunc pfnProgress,
      48             :                                               void *pProgressData)
      49             : {
      50          18 :     auto poSrcDS = m_inputDataset.GetDatasetRef();
      51          18 :     CPLAssert(poSrcDS);
      52             : 
      53          18 :     const int nSrcBandCount = poSrcDS->GetRasterCount();
      54          18 :     if (nSrcBandCount < 3)
      55             :     {
      56           1 :         ReportError(CE_Failure, CPLE_NotSupported,
      57             :                     "Input dataset must have at least 3 bands");
      58           1 :         return false;
      59             :     }
      60          17 :     else if (nSrcBandCount >= 4)
      61             :     {
      62           1 :         ReportError(
      63             :             CE_Warning, CPLE_AppDefined,
      64             :             "Only R,G,B bands of input dataset will be taken into account");
      65             :     }
      66             : 
      67          34 :     std::map<GDALColorInterp, GDALRasterBandH> mapBands;
      68          17 :     int nFound = 0;
      69          64 :     for (int i = 1; i <= nSrcBandCount; ++i)
      70             :     {
      71          49 :         auto poSrcBand = poSrcDS->GetRasterBand(i);
      72          49 :         if (poSrcBand->GetRasterDataType() != GDT_Byte)
      73             :         {
      74           1 :             ReportError(CE_Failure, CPLE_NotSupported,
      75             :                         "Non-byte band found and not supported");
      76           2 :             return false;
      77             :         }
      78          48 :         const auto eColorInterp = poSrcBand->GetColorInterpretation();
      79         141 :         for (const auto eInterestColorInterp :
      80         189 :              {GCI_RedBand, GCI_GreenBand, GCI_BlueBand})
      81             :         {
      82         142 :             if (eColorInterp == eInterestColorInterp)
      83             :             {
      84          43 :                 if (mapBands.find(eColorInterp) != mapBands.end())
      85             :                 {
      86           1 :                     ReportError(CE_Failure, CPLE_NotSupported,
      87             :                                 "Several %s bands found",
      88             :                                 GDALGetColorInterpretationName(eColorInterp));
      89           1 :                     return false;
      90             :                 }
      91          42 :                 ++nFound;
      92          42 :                 mapBands[eColorInterp] = GDALRasterBand::ToHandle(poSrcBand);
      93             :             }
      94             :         }
      95             :     }
      96             : 
      97          15 :     if (nFound < 3)
      98             :     {
      99           2 :         if (nFound > 0)
     100             :         {
     101           1 :             ReportError(
     102             :                 CE_Warning, CPLE_AppDefined,
     103             :                 "Assuming first band is red, second green and third blue, "
     104             :                 "despite at least one band with one of those color "
     105             :                 "interpretation "
     106             :                 "found");
     107             :         }
     108           2 :         mapBands[GCI_RedBand] =
     109           2 :             GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(1));
     110           2 :         mapBands[GCI_GreenBand] =
     111           2 :             GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(2));
     112           2 :         mapBands[GCI_BlueBand] =
     113           2 :             GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(3));
     114             :     }
     115             : 
     116             :     auto poTmpDS = CreateTemporaryDataset(
     117             :         poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), 1, GDT_Byte,
     118          30 :         /* bTiledIfPossible = */ true, poSrcDS, /* bCopyMetadata = */ true);
     119          15 :     if (!poTmpDS)
     120           1 :         return false;
     121             : 
     122          14 :     const double oneOverStep = 1.0 / ((m_colorMap.empty() ? 1 : 0) + 1);
     123             : 
     124          28 :     GDALColorTable oCT;
     125             : 
     126          14 :     bool bOK = true;
     127          14 :     double dfLastProgress = 0;
     128             :     std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
     129          14 :         nullptr, GDALDestroyScaledProgress);
     130          14 :     if (m_colorMap.empty())
     131             :     {
     132          10 :         pScaledData.reset(GDALCreateScaledProgress(0, oneOverStep, pfnProgress,
     133             :                                                    pProgressData));
     134          10 :         dfLastProgress = oneOverStep;
     135          20 :         bOK = (GDALComputeMedianCutPCT(
     136          10 :                    mapBands[GCI_RedBand], mapBands[GCI_GreenBand],
     137          20 :                    mapBands[GCI_BlueBand], nullptr, m_colorCount,
     138             :                    GDALColorTable::ToHandle(&oCT),
     139          10 :                    pScaledData ? GDALScaledProgress : nullptr,
     140             :                    pScaledData.get()) == CE_None);
     141             :     }
     142             :     else
     143             :     {
     144             :         GDALDriverH hDriver;
     145           4 :         if ((hDriver = GDALIdentifyDriver(m_colorMap.c_str(), nullptr)) !=
     146           7 :                 nullptr &&
     147             :             // Palette .txt files may be misidentified by the XYZ driver
     148           3 :             !EQUAL(GDALGetDescription(hDriver), "XYZ"))
     149             :         {
     150             :             auto poPaletteDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     151             :                 m_colorMap.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
     152           4 :                 nullptr, nullptr, nullptr));
     153           2 :             bOK = poPaletteDS != nullptr && poPaletteDS->GetRasterCount() > 0;
     154           2 :             if (bOK)
     155             :             {
     156             :                 const auto poCT =
     157           2 :                     poPaletteDS->GetRasterBand(1)->GetColorTable();
     158           2 :                 if (poCT)
     159             :                 {
     160           1 :                     oCT = *poCT;
     161             :                 }
     162             :                 else
     163             :                 {
     164           1 :                     bOK = false;
     165           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     166             :                                 "Dataset '%s' does not contain a color table",
     167             :                                 m_colorMap.c_str());
     168             :                 }
     169             :             }
     170             :         }
     171             :         else
     172             :         {
     173           4 :             auto poCT = GDALColorTable::LoadFromFile(m_colorMap.c_str());
     174           2 :             bOK = poCT != nullptr;
     175           2 :             if (bOK)
     176             :             {
     177           1 :                 oCT = std::move(*(poCT.get()));
     178             :             }
     179             :         }
     180             :     }
     181             : 
     182          14 :     if (bOK)
     183             :     {
     184          12 :         poTmpDS->GetRasterBand(1)->SetColorTable(&oCT);
     185             : 
     186          12 :         pScaledData.reset(GDALCreateScaledProgress(dfLastProgress, 1.0,
     187             :                                                    pfnProgress, pProgressData));
     188             : 
     189          24 :         bOK = GDALDitherRGB2PCT(
     190          12 :                   mapBands[GCI_RedBand], mapBands[GCI_GreenBand],
     191          24 :                   mapBands[GCI_BlueBand],
     192             :                   GDALRasterBand::ToHandle(poTmpDS->GetRasterBand(1)),
     193             :                   GDALColorTable::ToHandle(&oCT),
     194          12 :                   pScaledData ? GDALScaledProgress : nullptr,
     195             :                   pScaledData.get()) == CE_None;
     196             :     }
     197             : 
     198          14 :     if (bOK)
     199             :     {
     200          12 :         m_outputDataset.Set(std::move(poTmpDS));
     201          12 :         if (pfnProgress)
     202           3 :             pfnProgress(1.0, "", pProgressData);
     203             :     }
     204             : 
     205          14 :     return bOK;
     206             : }
     207             : 
     208             : //! @endcond

Generated by: LCOV version 1.14