diff --git a/alg/gdalwarpkernel.cpp b/alg/gdalwarpkernel.cpp index e917cbe24c04..6382ef6afae5 100644 --- a/alg/gdalwarpkernel.cpp +++ b/alg/gdalwarpkernel.cpp @@ -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; @@ -6917,9 +6910,9 @@ static void GWKAverageOrModeThread(void *pData) { nBins = 65536; } - panVals = - static_cast(VSI_MALLOC_VERBOSE(nBins * sizeof(int))); - if (panVals == nullptr) + pafCounts = + static_cast(VSI_MALLOC_VERBOSE(nBins * sizeof(float))); + if (pafCounts == nullptr) return; } else @@ -6930,12 +6923,12 @@ static void GWKAverageOrModeThread(void *pData) { pafRealVals = static_cast( VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float))); - panRealSums = static_cast( - VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(int))); - if (pafRealVals == nullptr || panRealSums == nullptr) + pafCounts = static_cast( + VSI_MALLOC3_VERBOSE(nSrcXSize, nSrcYSize, sizeof(float))); + if (pafRealVals == nullptr || pafCounts == nullptr) { VSIFree(pafRealVals); - VSIFree(panRealSums); + VSIFree(pafCounts); return; } } @@ -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(iSrcY) * nSrcXSize; @@ -7600,42 +7593,48 @@ static void GWKAverageOrModeThread(void *pData) { const float fVal = static_cast(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(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; @@ -7643,23 +7642,24 @@ static void GWKAverageOrModeThread(void *pData) } // 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(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) { @@ -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(iSrcY) * nSrcXSize; @@ -7709,40 +7711,46 @@ static void GWKAverageOrModeThread(void *pData) &dfValueRealTmp, &dfValueImagTmp) && dfBandDensity > BAND_DENSITY_THRESHOLD) { + bHasSourceValues = true; const int nVal = static_cast(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(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) { @@ -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); - } } /************************************************************************/ diff --git a/autotest/alg/warp.py b/autotest/alg/warp.py index 9eff6a367566..24158ceaec6a 100755 --- a/autotest/alg/warp.py +++ b/autotest/alg/warp.py @@ -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)