Skip to content

Commit

Permalink
Warper: Mode resampling: Use src pixel coverage and allow mode of -1
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaston committed Jan 15, 2025
1 parent a803e49 commit 147d192
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 66 deletions.
134 changes: 68 additions & 66 deletions alg/gdalwarpkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6857,23 +6857,16 @@ static void GWKAverageOrModeThread(void *pData)
/* -------------------------------------------------------------------- */
int nAlgo = 0;

// These vars only used with nAlgo == 3.
int *panVals = nullptr;
// Only used for GRA_Mode
float *pafRealVals = nullptr;
float *pafCounts = nullptr;
int nBins = 0;
int nBinsOffset = 0;

// Only used with nAlgo = 2.
float *pafRealVals = nullptr;
float *pafImagVals = nullptr;
int *panRealSums = nullptr;
int *panImagSums = nullptr;
const GWKTieStrategy eTieStrategy = poWK->eTieStrategy;

// Only used with nAlgo = 6.
float quant = 0.5;

// Only used for GRA_Mode
const GWKTieStrategy eTieStrategy = poWK->eTieStrategy;

// To control array allocation only when data type is complex
const bool bIsComplex = GDALDataTypeIsComplex(poWK->eWorkingDataType) != 0;

Expand Down Expand Up @@ -6917,9 +6910,9 @@ static void GWKAverageOrModeThread(void *pData)
{
nBins = 65536;
}
panVals =
static_cast<int *>(VSI_MALLOC_VERBOSE(nBins * sizeof(int)));
if (panVals == nullptr)
pafCounts =
static_cast<float *>(VSI_MALLOC_VERBOSE(nBins * sizeof(float)));
if (pafCounts == nullptr)
return;
}
else
Expand All @@ -6930,12 +6923,12 @@ static void GWKAverageOrModeThread(void *pData)
{
pafRealVals = static_cast<float *>(
VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float)));
panRealSums = static_cast<int *>(
VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(int)));
if (pafRealVals == nullptr || panRealSums == nullptr)
pafCounts = static_cast<float *>(
VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float)));
if (pafRealVals == nullptr || pafCounts == nullptr)
{
VSIFree(pafRealVals);
VSIFree(panRealSums);
VSIFree(pafCounts);
return;
}
}
Expand Down Expand Up @@ -7570,12 +7563,12 @@ static void GWKAverageOrModeThread(void *pData)
// majority filter on floating point data? But, here it
// is for the sake of compatibility. It won't look
// right on RGB images by the nature of the filter.
int iMaxInd = 0;
int iMaxVal = -1;
int i = 0;
nBins = 0;
int iModeIndex = -1;

for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
{
const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
iSrcOffset =
iSrcXMin +
static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
Expand All @@ -7600,66 +7593,73 @@ static void GWKAverageOrModeThread(void *pData)
{
const float fVal =
static_cast<float>(dfValueRealTmp);
const double dfWeight =
COMPUTE_WEIGHT(iSrcX, dfWeightY);

// Check array for existing entry.
for (i = 0; i < iMaxInd; ++i)
int i = 0;
for (i = 0; i < nBins; ++i)
{
if (pafRealVals[i] == fVal)
{
bool bValIsMax =
(++panRealSums[i] >
panRealSums[iMaxVal]);

if (!bValIsMax &&
panRealSums[i] ==
panRealSums[iMaxVal])
pafCounts[i] +=
static_cast<float>(dfWeight);
bool bValIsMaxCount =
(pafCounts[i] >
pafCounts[iModeIndex]);

if (!bValIsMaxCount &&
pafCounts[i] ==
pafCounts[iModeIndex])
{
switch (eTieStrategy)
{
case GWKTS_First:
break;
case GWKTS_Min:
bValIsMax =
bValIsMaxCount =
fVal <
pafRealVals
[iMaxVal];
[iModeIndex];
break;
case GWKTS_Max:
bValIsMax =
bValIsMaxCount =
fVal >
pafRealVals
[iMaxVal];
[iModeIndex];
break;
}
}

if (bValIsMax)
if (bValIsMaxCount)
{
iMaxVal = i;
iModeIndex = i;
}

break;
}
}

// Add to arr if entry not already there.
if (i == iMaxInd)
if (i == nBins)
{
pafRealVals[iMaxInd] = fVal;
panRealSums[iMaxInd] = 1;
pafRealVals[i] = fVal;
pafCounts[i] =
static_cast<float>(dfWeight);

if (iMaxVal < 0)
iMaxVal = iMaxInd;
if (iModeIndex < 0)
iModeIndex = i;

++iMaxInd;
++nBins;
}
}
}
}

if (iMaxVal != -1)
if (iModeIndex != -1)
{
dfValueReal = pafRealVals[iMaxVal];
dfValueReal = pafRealVals[iModeIndex];

if (poWK->bApplyVerticalShift)
{
Expand All @@ -7680,13 +7680,15 @@ static void GWKAverageOrModeThread(void *pData)
}
else // byte or int16.
{
int nMaxVal = 0;
int iMaxInd = -1;
float fMaxCount = 0.0f;
int nMode = -1;
bool bHasSourceValues = false;

memset(panVals, 0, nBins * sizeof(int));
memset(pafCounts, 0, nBins * sizeof(float));

for (int iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++)
{
const double dfWeightY = COMPUTE_WEIGHT_Y(iSrcY);
iSrcOffset =
iSrcXMin +
static_cast<GPtrDiff_t>(iSrcY) * nSrcXSize;
Expand All @@ -7709,40 +7711,46 @@ static void GWKAverageOrModeThread(void *pData)
&dfValueRealTmp, &dfValueImagTmp) &&
dfBandDensity > BAND_DENSITY_THRESHOLD)
{
bHasSourceValues = true;
const int nVal =
static_cast<int>(dfValueRealTmp);

bool bValIsMax =
++panVals[nVal + nBinsOffset] > nMaxVal;
if (!bValIsMax &&
panVals[nVal + nBinsOffset] == nMaxVal)
const int iBin = nVal + nBinsOffset;
const double dfWeight =
COMPUTE_WEIGHT(iSrcX, dfWeightY);

// Sum the density.
pafCounts[iBin] +=
static_cast<float>(dfWeight);
// Is it the most common value so far?
bool bValIsMaxCount =
pafCounts[iBin] > fMaxCount;
if (!bValIsMaxCount &&
pafCounts[iBin] == fMaxCount)
{
switch (eTieStrategy)
{
case GWKTS_First:
break;
case GWKTS_Min:
bValIsMax = nVal < iMaxInd;
bValIsMaxCount = nVal < nMode;
break;
case GWKTS_Max:
bValIsMax = nVal > iMaxInd;
bValIsMaxCount = nVal > nMode;
break;
}
}
if (bValIsMax)
if (bValIsMaxCount)
{
// Sum the density.
// Is it the most common value so far?
iMaxInd = nVal;
nMaxVal = panVals[nVal + nBinsOffset];
nMode = nVal;
fMaxCount = pafCounts[iBin];
}
}
}
}

if (iMaxInd != -1)
if (bHasSourceValues)
{
dfValueReal = iMaxInd;
dfValueReal = nMode;

if (poWK->bApplyVerticalShift)
{
Expand Down Expand Up @@ -8003,14 +8011,8 @@ static void GWKAverageOrModeThread(void *pData)
CPLFree(padfZ2);
CPLFree(pabSuccess);
CPLFree(pabSuccess2);
VSIFree(panVals);
VSIFree(pafCounts);
VSIFree(pafRealVals);
VSIFree(panRealSums);
if (bIsComplex)
{
VSIFree(pafImagVals);
VSIFree(panImagSums);
}
}

/************************************************************************/
Expand Down
30 changes: 30 additions & 0 deletions autotest/alg/warp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,36 @@ def test_warp_weighted_average():
assert maxdiff <= 1, "Image too different from reference"


###############################################################################
# test weighted mode


@pytest.mark.parametrize(
"dtype", (gdal.GDT_Int16, gdal.GDT_Int32), ids=gdal.GetDataTypeName
)
def test_warp_weighted_mode(dtype):

np = pytest.importorskip("numpy")

with gdal.GetDriverByName("MEM").Create("", 3, 3, eType=dtype) as src_ds:
src_ds.SetGeoTransform([0, 1, 0, 3, 0, -1])
src_ds.WriteArray(np.array([[1, 1, 1], [-1, -1, -1], [3, 3, 3]]))

dst_ds = gdal.Warp(
"",
src_ds,
format="MEM",
resampleAlg="mode",
width=1,
height=1,
outputBounds=(0.5, 0.5, 2.5, 2.5),
)

result = dst_ds.ReadAsArray()[0, 0]

assert result == -1


###############################################################################
# test weighted average, with src offset (fix for #2665)

Expand Down

0 comments on commit 147d192

Please sign in to comment.