From d8fb22f8f3b10fe85522f9d3f5e02653f61b970d Mon Sep 17 00:00:00 2001 From: Austin Sanders <arsanders@usgs.gov> Date: Tue, 19 Nov 2024 10:07:38 -0700 Subject: [PATCH] Add majority replacement option for Reduce (#5670) * Added option for majority replacement * Updated changelog --- CHANGELOG.md | 1 + isis/src/base/apps/reduce/reduce.xml | 7 ++++++ isis/src/base/objs/Reduce/Reduce.cpp | 33 +++++++++++++++++++++++++++- isis/src/base/objs/Reduce/Reduce.h | 2 +- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 490f3f7f14..44965ad94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ release. - Added Vectorize to ProcessGroundPolygon library - Added gtest files for the app and unit test - Added Chandrayaan2 template for isisimport +- Added majority replacement for reduce app [#5101](https://github.com/DOI-USGS/ISIS3/issues/5101). - Added HRSC support in socetlinescankeywords [#5465](https://github.com/DOI-USGS/ISIS3/issues/5465) ### Changed diff --git a/isis/src/base/apps/reduce/reduce.xml b/isis/src/base/apps/reduce/reduce.xml index e53f5845c0..65eb9ff79e 100644 --- a/isis/src/base/apps/reduce/reduce.xml +++ b/isis/src/base/apps/reduce/reduce.xml @@ -334,6 +334,13 @@ is closest to the center of the window being analyzed. </description> </option> + <option value="MAJORITY"> + <brief>Majority replacement</brief> + <description> + When VALIDPER is not met, propagate the most commonly occurring special pixel + within the input region to the output pixel. + </description> + </option> </list> </parameter> </group> diff --git a/isis/src/base/objs/Reduce/Reduce.cpp b/isis/src/base/objs/Reduce/Reduce.cpp index f6210eb7f2..7c1723bd2b 100644 --- a/isis/src/base/objs/Reduce/Reduce.cpp +++ b/isis/src/base/objs/Reduce/Reduce.cpp @@ -171,9 +171,11 @@ namespace Isis { */ void Average::operator() (Isis::Buffer & out) const { + // Index of the last input line that corresponds to this output line. double rline = (double)out.Line() * mdLineScale; if(out.Line() == 1 && out.Band() == 1) { + // Index of the last input sample that corresponds to the output sample. mdIncTab = new double[miOutputSamples]; mdSum = new double[miOutputSamples]; mdNpts = new double[miOutputSamples]; @@ -191,7 +193,9 @@ namespace Isis { mdIncTab[miOutputSamples-1] = miInputSamples; } - while(mdLine <= rline) { + unordered_map<QString, int> specialPixelCounts[miOutputSamples]; + + while(mdLine < rline) { if((int)mdLine <= miInputLines) { m_iPortal->SetPosition(miStartSample, mdLine, miBandIndex); mInCube->read(*m_iPortal); @@ -204,6 +208,9 @@ namespace Isis { mdSum[osamp] += (*m_iPortal)[isamp-1]; mdNpts[osamp] += 1.0; } + if (IsSpecial((*m_iPortal)[isamp-1])) { + specialPixelCounts[osamp][PixelToString((*m_iPortal)[isamp-1])]++; + } isamp++; } @@ -218,6 +225,12 @@ namespace Isis { mdNpts[osamp+1] += sdel; } } + if (IsSpecial((*m_iPortal)[isamp-1])) { + specialPixelCounts[osamp][PixelToString((*m_iPortal)[isamp-1])]++; + if(osamp+1 <= miOutputSamples){ + specialPixelCounts[osamp+1][PixelToString((*m_iPortal)[isamp-1])]++; + } + } isamp++; } mdLine++; @@ -238,6 +251,9 @@ namespace Isis { mdSum2[osamp] += (*m_iPortal)[isamp-1] * ldel; mdNpts2[osamp] += ldel; } + if (IsSpecial((*m_iPortal)[isamp-1])) { + specialPixelCounts[osamp][PixelToString((*m_iPortal)[isamp-1])]++; + } isamp++; } @@ -257,6 +273,12 @@ namespace Isis { mdNpts2[osamp+1] += sdel * ldel; } } + if (IsSpecial((*m_iPortal)[isamp-1])) { + specialPixelCounts[osamp][PixelToString((*m_iPortal)[isamp-1])]++; + if(osamp+1 <= miOutputSamples){ + specialPixelCounts[osamp+1][PixelToString((*m_iPortal)[isamp-1])]++; + } + } isamp++; } @@ -271,6 +293,14 @@ namespace Isis { if(msReplaceMode == "NEAREST") { out[osamp] = (*m_iPortal)[(int)(mdIncTab[osamp] + 0.5) - 1]; } + else if (msReplaceMode == "MAJORITY") { + // Iterate over the map and compare element counts + // Map iterator yields a pair: pair.first == pixel names, pair.second == pixel count. + auto mode = std::max_element(specialPixelCounts[osamp].begin(), + specialPixelCounts[osamp].end(), + [](const pair<QString, int> &a, const pair<QString, int> &b) {return a.second < b.second;}); + out[osamp] = StringToPixel(mode->first); + } else { out[osamp] = Isis::Null; } @@ -281,6 +311,7 @@ namespace Isis { mdNpts2[osamp] = 0.0; } + // If this is the last line of the band, reset stats and increment band if(out.Line() == miOutputLines && out.Band() != miInputBands) { miBandIndex++; mdLine = 1; diff --git a/isis/src/base/objs/Reduce/Reduce.h b/isis/src/base/objs/Reduce/Reduce.h index 9a6f1dd408..d69e347cea 100644 --- a/isis/src/base/objs/Reduce/Reduce.h +++ b/isis/src/base/objs/Reduce/Reduce.h @@ -111,7 +111,7 @@ namespace Isis { double pdValidPer, QString psReplaceMode) : Reduce(pInCube, pdSampleScale, pdLineScale){ mdValidPer = pdValidPer; - msReplaceMode = psReplaceMode; + msReplaceMode = psReplaceMode.toUpper(); } //! Operator () overload -- GitLab