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

Generated by: LCOV version 1.14