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
|