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
|