Skip to content

Commit

Permalink
Warper: add a NODATA_VALUES_PCT_THRESHOLD warping option
Browse files Browse the repository at this point in the history
This is similar to the EXCLUDED_VALUES_PCT_THRESHOLD option introduced
in #9631, but here this controls the
minimum amount of transparent/invalid/nodata source pixels to cause the
target pixels to not be set.
Only used currently for average resampling.
  • Loading branch information
rouault authored and github-actions[bot] committed May 4, 2024
1 parent 7f2a92c commit 69ec766
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 10 deletions.
12 changes: 11 additions & 1 deletion alg/gdalwarper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,17 @@ CPLErr GDALWarpDstAlphaMasker(void *pMaskFuncArg, int nBandCount,
* <li>EXCLUDED_VALUES_PCT_THRESHOLD=[0-100]: (GDAL >= 3.9) Minimum percentage
* of source pixels that must be set at one of the EXCLUDED_VALUES to cause
* the excluded value, that is in majority among source pixels, to be used as the
* target pixel value. Default value is 50 (%)</li>
* target pixel value. Default value is 50 (%).
* Only taken into account by Average currently.</li>
*
* <li>NODATA_VALUES_PCT_THRESHOLD=[0-100]: (GDAL >= 3.9) Minimum percentage
* of source pixels that must be at nodata (or alpha=0 or any other way to express
* transparent pixel) to cause the target pixel value to not be set. Default
* value is 100 (%), which means that a target pixel is not set only if all
* contributing source pixels are not set.
* Note that NODATA_VALUES_PCT_THRESHOLD is taken into account before
* EXCLUDED_VALUES_PCT_THRESHOLD.
* Only taken into account by Average currently.</li>
*
* </ul>
*/
Expand Down
30 changes: 21 additions & 9 deletions alg/gdalwarpkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6584,6 +6584,10 @@ static void GWKAverageOrModeThread(void *pData)
CPLAtof(CSLFetchNameValueDef(poWK->papszWarpOptions,
"EXCLUDED_VALUES_PCT_THRESHOLD", "50")) /
100.0;
const double dfNodataValuesThreshold =
CPLAtof(CSLFetchNameValueDef(poWK->papszWarpOptions,
"NODATA_VALUES_PCT_THRESHOLD", "100")) /
100.0;

const int nXMargin =
2 * std::max(1, static_cast<int>(std::ceil(1. / poWK->dfXScale)));
Expand Down Expand Up @@ -6743,7 +6747,8 @@ static void GWKAverageOrModeThread(void *pData)
// Special Average mode where we process all bands together,
// to avoid averaging tuples that match an entry of m_aadfExcludedValues
if (nAlgo == GWKAOM_Average &&
!poWK->m_aadfExcludedValues.empty() &&
(!poWK->m_aadfExcludedValues.empty() ||
dfNodataValuesThreshold < 1 - EPS) &&
!poWK->bApplyVerticalShift && !bIsComplex)
{
double dfTotalWeightInvalid = 0.0;
Expand All @@ -6767,17 +6772,18 @@ static void GWKAverageOrModeThread(void *pData)
(iSrcX % nSrcXSize) +
static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;

const double dfWeight =
COMPUTE_WEIGHT(iSrcX, dfWeightY);
if (dfWeight <= 0)
continue;

if (poWK->panUnifiedSrcValid != nullptr &&
!CPLMaskGet(poWK->panUnifiedSrcValid, iSrcOffset))
{
dfTotalWeightInvalid += dfWeight;
continue;
}

const double dfWeight =
COMPUTE_WEIGHT(iSrcX, dfWeightY);
if (dfWeight <= 0)
continue;

bool bAllValid = true;
for (int iBand = 0; iBand < poWK->nBands; iBand++)
{
Expand Down Expand Up @@ -6830,9 +6836,15 @@ static void GWKAverageOrModeThread(void *pData)
const double dfTotalWeight = dfTotalWeightInvalid +
dfTotalWeightExcluded +
dfTotalWeightRegular;
if (dfTotalWeightExcluded > 0 &&
dfTotalWeightExcluded >=
dfExcludedValuesThreshold * dfTotalWeight)
if (dfTotalWeightInvalid > 0 &&
dfTotalWeightInvalid >=
dfNodataValuesThreshold * dfTotalWeight)
{
// Do nothing. Let bHasFoundDensity to false.
}
else if (dfTotalWeightExcluded > 0 &&
dfTotalWeightExcluded >=
dfExcludedValuesThreshold * dfTotalWeight)
{
// Find the most represented excluded value tuple
size_t iExcludedValue = 0;
Expand Down
38 changes: 38 additions & 0 deletions autotest/alg/warp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,3 +1815,41 @@ def test_warp_average_excluded_values():
(11 + 21 + 31 + 41) // 4,
(12 + 22 + 32 + 42) // 4,
)


###############################################################################
# Test NODATA_VALUES_PCT_THRESHOLD warping option with average resampling


def test_warp_average_NODATA_VALUES_PCT_THRESHOLD():

src_ds = gdal.GetDriverByName("MEM").Create("", 2, 2, 1, gdal.GDT_Byte)
src_ds.GetRasterBand(1).WriteRaster(
0, 0, 2, 2, struct.pack("B" * 4, 10, 20, 30, 40)
)
src_ds.SetGeoTransform([1, 1, 0, 1, 0, 1])
src_ds.GetRasterBand(1).SetNoDataValue(20)

out_ds = gdal.Warp("", src_ds, options="-of MEM -ts 1 1 -r average")
assert struct.unpack("B", out_ds.ReadRaster())[0] == round((10 + 30 + 40) / 3)

out_ds = gdal.Warp(
"",
src_ds,
options="-of MEM -ts 1 1 -r average -wo NODATA_VALUES_PCT_THRESHOLD=80",
)
assert struct.unpack("B", out_ds.ReadRaster())[0] == round((10 + 30 + 40) / 3)

out_ds = gdal.Warp(
"",
src_ds,
options="-of MEM -ts 1 1 -r average -wo NODATA_VALUES_PCT_THRESHOLD=30",
)
assert struct.unpack("B", out_ds.ReadRaster())[0] == round((10 + 30 + 40) / 3)

out_ds = gdal.Warp(
"",
src_ds,
options="-of MEM -ts 1 1 -r average -wo NODATA_VALUES_PCT_THRESHOLD=25",
)
assert struct.unpack("B", out_ds.ReadRaster())[0] == 20

0 comments on commit 69ec766

Please sign in to comment.