LCOV - code coverage report
Current view: top level - frmts/vrt - vrtfilters.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 306 355 86.2 %
Date: 2025-10-25 23:36:32 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of some filter types.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "vrtdataset.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <limits>
      23             : #include <map>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_error.h"
      27             : #include "cpl_minixml.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi.h"
      30             : #include "gdal.h"
      31             : #include "gdal_priv.h"
      32             : 
      33             : /*! @cond Doxygen_Suppress */
      34             : 
      35             : /************************************************************************/
      36             : /* ==================================================================== */
      37             : /*                          VRTFilteredSource                           */
      38             : /* ==================================================================== */
      39             : /************************************************************************/
      40             : 
      41             : /************************************************************************/
      42             : /*                         VRTFilteredSource()                          */
      43             : /************************************************************************/
      44             : 
      45          42 : VRTFilteredSource::VRTFilteredSource()
      46          42 :     : m_nSupportedTypesCount(1), m_nExtraEdgePixels(0)
      47             : {
      48         882 :     for (size_t i = 0; i < CPL_ARRAYSIZE(m_aeSupportedTypes); ++i)
      49         840 :         m_aeSupportedTypes[i] = GDT_Unknown;
      50             : 
      51          42 :     m_aeSupportedTypes[0] = GDT_Float32;
      52          42 : }
      53             : 
      54             : /************************************************************************/
      55             : /*                         ~VRTFilteredSource()                         */
      56             : /************************************************************************/
      57             : 
      58          42 : VRTFilteredSource::~VRTFilteredSource()
      59             : {
      60          42 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                         SetExtraEdgePixels()                         */
      64             : /************************************************************************/
      65             : 
      66          40 : void VRTFilteredSource::SetExtraEdgePixels(int nEdgePixels)
      67             : 
      68             : {
      69          40 :     m_nExtraEdgePixels = nEdgePixels;
      70          40 : }
      71             : 
      72             : /************************************************************************/
      73             : /*                   SetFilteringDataTypesSupported()                   */
      74             : /************************************************************************/
      75             : 
      76          42 : void VRTFilteredSource::SetFilteringDataTypesSupported(int nTypeCount,
      77             :                                                        GDALDataType *paeTypes)
      78             : 
      79             : {
      80          42 :     if (nTypeCount >
      81             :         static_cast<int>(sizeof(m_aeSupportedTypes) / sizeof(GDALDataType)))
      82             :     {
      83           0 :         CPLAssert(false);
      84             :         nTypeCount =
      85             :             static_cast<int>(sizeof(m_aeSupportedTypes) / sizeof(GDALDataType));
      86             :     }
      87             : 
      88          42 :     m_nSupportedTypesCount = nTypeCount;
      89          42 :     memcpy(m_aeSupportedTypes, paeTypes, sizeof(GDALDataType) * nTypeCount);
      90          42 : }
      91             : 
      92             : /************************************************************************/
      93             : /*                          IsTypeSupported()                           */
      94             : /************************************************************************/
      95             : 
      96          88 : int VRTFilteredSource::IsTypeSupported(GDALDataType eTestType) const
      97             : 
      98             : {
      99         176 :     for (int i = 0; i < m_nSupportedTypesCount; i++)
     100             :     {
     101          88 :         if (eTestType == m_aeSupportedTypes[i])
     102           0 :             return TRUE;
     103             :     }
     104             : 
     105          88 :     return FALSE;
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                              RasterIO()                              */
     110             : /************************************************************************/
     111             : 
     112          44 : CPLErr VRTFilteredSource::RasterIO(GDALDataType eVRTBandDataType, int nXOff,
     113             :                                    int nYOff, int nXSize, int nYSize,
     114             :                                    void *pData, int nBufXSize, int nBufYSize,
     115             :                                    GDALDataType eBufType, GSpacing nPixelSpace,
     116             :                                    GSpacing nLineSpace,
     117             :                                    GDALRasterIOExtraArg *psExtraArg,
     118             :                                    WorkingState &oWorkingState)
     119             : 
     120             : {
     121             :     /* -------------------------------------------------------------------- */
     122             :     /*      For now we don't support filtered access to non-full            */
     123             :     /*      resolution requests. Just collect the data directly without     */
     124             :     /*      any operator.                                                   */
     125             :     /* -------------------------------------------------------------------- */
     126          44 :     if (nBufXSize != nXSize || nBufYSize != nYSize)
     127             :     {
     128           0 :         return VRTComplexSource::RasterIO(
     129             :             eVRTBandDataType, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
     130             :             nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg,
     131           0 :             oWorkingState);
     132             :     }
     133             : 
     134          44 :     double dfXOff = nXOff;
     135          44 :     double dfYOff = nYOff;
     136          44 :     double dfXSize = nXSize;
     137          44 :     double dfYSize = nYSize;
     138          44 :     if (psExtraArg != nullptr && psExtraArg->bFloatingPointWindowValidity)
     139             :     {
     140           4 :         dfXOff = psExtraArg->dfXOff;
     141           4 :         dfYOff = psExtraArg->dfYOff;
     142           4 :         dfXSize = psExtraArg->dfXSize;
     143           4 :         dfYSize = psExtraArg->dfYSize;
     144             :     }
     145             : 
     146             :     // The window we will actually request from the source raster band.
     147          44 :     double dfReqXOff = 0.0;
     148          44 :     double dfReqYOff = 0.0;
     149          44 :     double dfReqXSize = 0.0;
     150          44 :     double dfReqYSize = 0.0;
     151          44 :     int nReqXOff = 0;
     152          44 :     int nReqYOff = 0;
     153          44 :     int nReqXSize = 0;
     154          44 :     int nReqYSize = 0;
     155             : 
     156             :     // The window we will actual set _within_ the pData buffer.
     157          44 :     int nOutXOff = 0;
     158          44 :     int nOutYOff = 0;
     159          44 :     int nOutXSize = 0;
     160          44 :     int nOutYSize = 0;
     161             : 
     162          44 :     bool bError = false;
     163          44 :     if (!GetSrcDstWindow(dfXOff, dfYOff, dfXSize, dfYSize, nBufXSize, nBufYSize,
     164             :                          &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize,
     165             :                          &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize,
     166             :                          &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize, bError))
     167             :     {
     168           0 :         return bError ? CE_Failure : CE_None;
     169             :     }
     170             : 
     171          44 :     pData = reinterpret_cast<GByte *>(pData) + nPixelSpace * nOutXOff +
     172          44 :             nLineSpace * nOutYOff;
     173             : 
     174             :     /* -------------------------------------------------------------------- */
     175             :     /*      Determine the data type we want to request.  We try to match    */
     176             :     /*      the source or destination request, and if both those fail we    */
     177             :     /*      fallback to the first supported type at least as expressive     */
     178             :     /*      as the request.                                                 */
     179             :     /* -------------------------------------------------------------------- */
     180          44 :     GDALDataType eOperDataType = GDT_Unknown;
     181             : 
     182          44 :     if (IsTypeSupported(eBufType))
     183           0 :         eOperDataType = eBufType;
     184             : 
     185          44 :     auto l_band = GetRasterBand();
     186          44 :     if (!l_band)
     187           0 :         return CE_Failure;
     188             : 
     189          88 :     if (eOperDataType == GDT_Unknown &&
     190          44 :         IsTypeSupported(l_band->GetRasterDataType()))
     191           0 :         eOperDataType = l_band->GetRasterDataType();
     192             : 
     193          44 :     if (eOperDataType == GDT_Unknown)
     194             :     {
     195          88 :         for (int i = 0; i < m_nSupportedTypesCount; i++)
     196             :         {
     197          44 :             if (GDALDataTypeUnion(m_aeSupportedTypes[i], eBufType) ==
     198          44 :                 m_aeSupportedTypes[i])
     199             :             {
     200           7 :                 eOperDataType = m_aeSupportedTypes[i];
     201             :             }
     202             :         }
     203             :     }
     204             : 
     205          44 :     if (eOperDataType == GDT_Unknown)
     206             :     {
     207          37 :         eOperDataType = m_aeSupportedTypes[0];
     208             : 
     209          37 :         for (int i = 1; i < m_nSupportedTypesCount; i++)
     210             :         {
     211           0 :             if (GDALGetDataTypeSizeBytes(m_aeSupportedTypes[i]) >
     212           0 :                 GDALGetDataTypeSizeBytes(eOperDataType))
     213             :             {
     214           0 :                 eOperDataType = m_aeSupportedTypes[i];
     215             :             }
     216             :         }
     217             :     }
     218             : 
     219             :     /* -------------------------------------------------------------------- */
     220             :     /*      Allocate the buffer of data into which our imagery will be      */
     221             :     /*      read, with the extra edge pixels as well. This will be the      */
     222             :     /*      source data fed into the filter.                                */
     223             :     /* -------------------------------------------------------------------- */
     224          44 :     if (nOutXSize > INT_MAX - 2 * m_nExtraEdgePixels ||
     225          44 :         nOutYSize > INT_MAX - 2 * m_nExtraEdgePixels)
     226             :     {
     227           0 :         return CE_Failure;
     228             :     }
     229          44 :     const int nExtraXSize = nOutXSize + 2 * m_nExtraEdgePixels;
     230          44 :     const int nExtraYSize = nOutYSize + 2 * m_nExtraEdgePixels;
     231             : 
     232          44 :     GByte *pabyWorkData = static_cast<GByte *>(VSI_MALLOC3_VERBOSE(
     233             :         nExtraXSize, nExtraYSize, GDALGetDataTypeSizeBytes(eOperDataType)));
     234             : 
     235          44 :     if (pabyWorkData == nullptr)
     236             :     {
     237           0 :         return CE_Failure;
     238             :     }
     239             : 
     240          44 :     const GPtrDiff_t nPixelOffset = GDALGetDataTypeSizeBytes(eOperDataType);
     241          44 :     const GPtrDiff_t nLineOffset = nPixelOffset * nExtraXSize;
     242             : 
     243          44 :     int bHasNoData = false;
     244          44 :     const double dfSrcNoDataValue = l_band->GetNoDataValue(&bHasNoData);
     245          44 :     if (bHasNoData)
     246             :     {
     247           5 :         GDALCopyWords64(&dfSrcNoDataValue, GDT_Float64, 0, pabyWorkData,
     248             :                         eOperDataType, static_cast<int>(nPixelOffset),
     249           5 :                         static_cast<size_t>(nExtraXSize) * nExtraYSize);
     250             :     }
     251             :     else
     252             :     {
     253          39 :         memset(pabyWorkData, 0, nLineOffset * nExtraYSize);
     254             :     }
     255             : 
     256             :     /* -------------------------------------------------------------------- */
     257             :     /*      Allocate the output buffer in the same dimensions as the work   */
     258             :     /*      buffer. This allows the filter process to write edge pixels     */
     259             :     /*      if needed for two-pass (separable) filtering.                   */
     260             :     /* -------------------------------------------------------------------- */
     261             :     GByte *pabyOutData = static_cast<GByte *>(
     262          44 :         VSI_MALLOC3_VERBOSE(nExtraXSize, nExtraYSize, nPixelOffset));
     263          44 :     if (pabyOutData == nullptr)
     264             :     {
     265           0 :         CPLFree(pabyWorkData);
     266           0 :         return CE_Failure;
     267             :     }
     268             : 
     269             :     /* -------------------------------------------------------------------- */
     270             :     /*      Figure out the extended window that we want to load.  Note      */
     271             :     /*      that we keep track of the file window as well as the amount     */
     272             :     /*      we will need to edge fill past the edge of the source dataset.  */
     273             :     /* -------------------------------------------------------------------- */
     274          44 :     int nFileXOff = nReqXOff - m_nExtraEdgePixels;
     275          44 :     int nFileYOff = nReqYOff - m_nExtraEdgePixels;
     276          44 :     int nFileXSize = nExtraXSize;
     277          44 :     int nFileYSize = nExtraYSize;
     278             : 
     279          44 :     int nTopFill = 0;
     280          44 :     int nLeftFill = 0;
     281          44 :     int nRightFill = 0;
     282          44 :     int nBottomFill = 0;
     283             : 
     284          44 :     if (nFileXOff < 0)
     285             :     {
     286          44 :         nLeftFill = -nFileXOff;
     287          44 :         nFileXOff = 0;
     288          44 :         nFileXSize -= nLeftFill;
     289             :     }
     290             : 
     291          44 :     if (nFileYOff < 0)
     292             :     {
     293          35 :         nTopFill = -nFileYOff;
     294          35 :         nFileYOff = 0;
     295          35 :         nFileYSize -= nTopFill;
     296             :     }
     297             : 
     298          44 :     if (nFileXOff + nFileXSize > l_band->GetXSize())
     299             :     {
     300          44 :         nRightFill = nFileXOff + nFileXSize - l_band->GetXSize();
     301          44 :         nFileXSize -= nRightFill;
     302             :     }
     303             : 
     304          44 :     if (nFileYOff + nFileYSize > l_band->GetYSize())
     305             :     {
     306          35 :         nBottomFill = nFileYOff + nFileYSize - l_band->GetYSize();
     307          35 :         nFileYSize -= nBottomFill;
     308             :     }
     309             : 
     310             :     /* -------------------------------------------------------------------- */
     311             :     /*      Load the data.                                                  */
     312             :     /* -------------------------------------------------------------------- */
     313             :     {
     314             :         GDALRasterIOExtraArg sExtraArgs;
     315          44 :         INIT_RASTERIO_EXTRA_ARG(sExtraArgs);
     316             :         const bool bIsComplex =
     317          44 :             CPL_TO_BOOL(GDALDataTypeIsComplex(eOperDataType));
     318          44 :         const CPLErr eErr = VRTComplexSource::RasterIOInternal<float>(
     319             :             l_band, eVRTBandDataType, nFileXOff, nFileYOff, nFileXSize,
     320             :             nFileYSize,
     321          44 :             pabyWorkData + nLineOffset * nTopFill + nPixelOffset * nLeftFill,
     322             :             nFileXSize, nFileYSize, eOperDataType, nPixelOffset, nLineOffset,
     323             :             &sExtraArgs, bIsComplex ? GDT_CFloat32 : GDT_Float32,
     324             :             oWorkingState);
     325             : 
     326          44 :         if (eErr != CE_None)
     327             :         {
     328           0 :             VSIFree(pabyWorkData);
     329           0 :             VSIFree(pabyOutData);
     330             : 
     331           0 :             return eErr;
     332             :         }
     333             :     }
     334             : 
     335             :     /* -------------------------------------------------------------------- */
     336             :     /*      Fill in missing areas.  Note that we replicate the edge         */
     337             :     /*      valid values out.  We don't using "mirroring" which might be    */
     338             :     /*      more suitable for some times of filters.  We also don't mark    */
     339             :     /*      these pixels as "nodata" though perhaps we should.              */
     340             :     /* -------------------------------------------------------------------- */
     341          44 :     if (nLeftFill != 0 || nRightFill != 0)
     342             :     {
     343        2191 :         for (int i = nTopFill; i < nExtraYSize - nBottomFill; i++)
     344             :         {
     345        2147 :             if (nLeftFill != 0)
     346        2147 :                 GDALCopyWords(
     347        2147 :                     pabyWorkData + nPixelOffset * nLeftFill + i * nLineOffset,
     348        2147 :                     eOperDataType, 0, pabyWorkData + i * nLineOffset,
     349             :                     eOperDataType, static_cast<int>(nPixelOffset), nLeftFill);
     350             : 
     351        2147 :             if (nRightFill != 0)
     352        2147 :                 GDALCopyWords(pabyWorkData + i * nLineOffset +
     353        2147 :                                   nPixelOffset * (nExtraXSize - nRightFill - 1),
     354             :                               eOperDataType, 0,
     355        2147 :                               pabyWorkData + i * nLineOffset +
     356        2147 :                                   nPixelOffset * (nExtraXSize - nRightFill),
     357             :                               eOperDataType, static_cast<int>(nPixelOffset),
     358             :                               nRightFill);
     359             :         }
     360             :     }
     361             : 
     362          86 :     for (int i = 0; i < nTopFill; i++)
     363             :     {
     364          42 :         memcpy(pabyWorkData + i * nLineOffset,
     365          42 :                pabyWorkData + nTopFill * nLineOffset, nLineOffset);
     366             :     }
     367             : 
     368          86 :     for (int i = nExtraYSize - nBottomFill; i < nExtraYSize; i++)
     369             :     {
     370          42 :         memcpy(pabyWorkData + i * nLineOffset,
     371          42 :                pabyWorkData + (nExtraYSize - nBottomFill - 1) * nLineOffset,
     372             :                nLineOffset);
     373             :     }
     374             : 
     375             :     /* -------------------------------------------------------------------- */
     376             :     /*      Filter the data.                                                */
     377             :     /* -------------------------------------------------------------------- */
     378          88 :     const CPLErr eErr = FilterData(nExtraXSize, nExtraYSize, eOperDataType,
     379          44 :                                    pabyWorkData, pabyOutData);
     380             : 
     381          44 :     VSIFree(pabyWorkData);
     382          44 :     if (eErr != CE_None)
     383             :     {
     384           0 :         VSIFree(pabyOutData);
     385             : 
     386           0 :         return eErr;
     387             :     }
     388             : 
     389             :     /* -------------------------------------------------------------------- */
     390             :     /*      Copy from work buffer to target buffer.                         */
     391             :     /* -------------------------------------------------------------------- */
     392          44 :     GByte *pabySrcRow =
     393          44 :         pabyOutData + (nLineOffset + nPixelOffset) * m_nExtraEdgePixels;
     394          44 :     GByte *pabyDstRow = reinterpret_cast<GByte *>(pData);
     395             : 
     396        2173 :     for (int i = 0; i < nOutYSize;
     397        2129 :          i++, pabySrcRow += nLineOffset, pabyDstRow += nLineSpace)
     398             :     {
     399        2129 :         GDALCopyWords(pabySrcRow, eOperDataType, static_cast<int>(nPixelOffset),
     400             :                       pabyDstRow, eBufType, static_cast<int>(nPixelSpace),
     401             :                       nOutXSize);
     402             :     }
     403             : 
     404          44 :     VSIFree(pabyOutData);
     405             : 
     406          44 :     return CE_None;
     407             : }
     408             : 
     409             : /************************************************************************/
     410             : /* ==================================================================== */
     411             : /*                       VRTKernelFilteredSource                        */
     412             : /* ==================================================================== */
     413             : /************************************************************************/
     414             : 
     415             : /************************************************************************/
     416             : /*                      VRTKernelFilteredSource()                       */
     417             : /************************************************************************/
     418             : 
     419          42 : VRTKernelFilteredSource::VRTKernelFilteredSource()
     420             : {
     421          42 :     GDALDataType aeSupTypes[] = {GDT_Float32};
     422          42 :     SetFilteringDataTypesSupported(1, aeSupTypes);
     423          42 : }
     424             : 
     425             : /************************************************************************/
     426             : /*                            GetType()                                 */
     427             : /************************************************************************/
     428             : 
     429          83 : const char *VRTKernelFilteredSource::GetType() const
     430             : {
     431             :     static const char *TYPE = "KernelFilteredSource";
     432          83 :     return TYPE;
     433             : }
     434             : 
     435             : /************************************************************************/
     436             : /*                           SetNormalized()                            */
     437             : /************************************************************************/
     438             : 
     439          40 : void VRTKernelFilteredSource::SetNormalized(bool bNormalizedIn)
     440             : 
     441             : {
     442          40 :     m_bNormalized = bNormalizedIn;
     443          40 : }
     444             : 
     445             : /************************************************************************/
     446             : /*                             SetKernel()                              */
     447             : /************************************************************************/
     448             : 
     449             : CPLErr
     450          40 : VRTKernelFilteredSource::SetKernel(int nNewKernelSize, bool bSeparable,
     451             :                                    const std::vector<double> &adfNewCoefs)
     452             : 
     453             : {
     454          40 :     if (nNewKernelSize < 1 || (nNewKernelSize % 2) != 1)
     455             :     {
     456           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     457             :                  "Illegal filtering kernel size %d, "
     458             :                  "must be odd positive number.",
     459             :                  nNewKernelSize);
     460           0 :         return CE_Failure;
     461             :     }
     462          40 :     if (adfNewCoefs.size() !=
     463          40 :         static_cast<size_t>(nNewKernelSize) * (bSeparable ? 1 : nNewKernelSize))
     464             :     {
     465           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     466             :                  "adfNewCoefs[] is not of expected size");
     467           0 :         return CE_Failure;
     468             :     }
     469             : 
     470          40 :     m_nKernelSize = nNewKernelSize;
     471          40 :     m_bSeparable = bSeparable;
     472          40 :     m_adfKernelCoefs = adfNewCoefs;
     473             : 
     474          40 :     SetExtraEdgePixels((nNewKernelSize - 1) / 2);
     475             : 
     476          40 :     return CE_None;
     477             : }
     478             : 
     479             : /************************************************************************/
     480             : /*                             FilterData()                             */
     481             : /************************************************************************/
     482             : 
     483          44 : CPLErr VRTKernelFilteredSource::FilterData(int nXSize, int nYSize,
     484             :                                            GDALDataType eType,
     485             :                                            GByte *pabySrcData,
     486             :                                            GByte *pabyDstData)
     487             : 
     488             : {
     489             :     /* -------------------------------------------------------------------- */
     490             :     /*      Validate data type.                                             */
     491             :     /* -------------------------------------------------------------------- */
     492          44 :     if (eType != GDT_Float32)
     493             :     {
     494           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     495             :                  "Unsupported data type (%s) in "
     496             :                  "VRTKernelFilteredSource::FilterData()",
     497             :                  GDALGetDataTypeName(eType));
     498           0 :         return CE_Failure;
     499             :     }
     500             : 
     501          44 :     CPLAssert(m_nExtraEdgePixels * 2 + 1 == m_nKernelSize ||
     502             :               (m_nKernelSize == 0 && m_nExtraEdgePixels == 0));
     503             : 
     504          44 :     const bool bMin = m_function == "min";
     505          44 :     const bool bMax = m_function == "max";
     506          44 :     const bool bStdDev = m_function == "stddev";
     507          44 :     const bool bMedian = m_function == "median";
     508          44 :     const bool bMode = m_function == "mode";
     509             : 
     510             :     /* -------------------------------------------------------------------- */
     511             :     /*      Float32 case.                                                   */
     512             :     /* -------------------------------------------------------------------- */
     513             :     // if( eType == GDT_Float32 )
     514             :     {
     515          44 :         float *pafSrcData = reinterpret_cast<float *>(pabySrcData);
     516          44 :         float *pafDstData = reinterpret_cast<float *>(pabyDstData);
     517             : 
     518          44 :         int bHasNoData = FALSE;
     519          44 :         auto l_poBand = GetRasterBand();
     520          44 :         if (!l_poBand)
     521           0 :             return CE_Failure;
     522             :         const float fNoData =
     523          44 :             static_cast<float>(l_poBand->GetNoDataValue(&bHasNoData));
     524             : 
     525          44 :         const int nAxisCount = m_bSeparable ? 2 : 1;
     526             : 
     527          89 :         for (int nAxis = 0; nAxis < nAxisCount; ++nAxis)
     528             :         {
     529          45 :             const int nISize = nAxis == 0 ? nYSize : nXSize;
     530          45 :             const int nJSize = nAxis == 0 ? nXSize : nYSize;
     531          45 :             const int nIStride = nAxis == 0 ? nXSize : 1;
     532          45 :             const int nJStride = nAxis == 0 ? 1 : nXSize;
     533             : 
     534          45 :             const int nIMin = m_nExtraEdgePixels;
     535          45 :             const int nIMax = nISize - m_nExtraEdgePixels;
     536          45 :             const int nJMin = (m_bSeparable ? 0 : m_nExtraEdgePixels);
     537          45 :             const int nJMax = nJSize - (m_bSeparable ? 0 : m_nExtraEdgePixels);
     538             : 
     539        7048 :             for (GPtrDiff_t iJ = nJMin; iJ < nJMax; ++iJ)
     540             :             {
     541        7003 :                 if (nAxis == 1)
     542          62 :                     memcpy(pafSrcData + iJ * nJStride,
     543          62 :                            pafDstData + iJ * nJStride, sizeof(float) * nXSize);
     544             : 
     545      768120 :                 for (int iI = nIMin; iI < nIMax; ++iI)
     546             :                 {
     547      761117 :                     const GPtrDiff_t iIndex =
     548      761117 :                         static_cast<GPtrDiff_t>(iI) * nIStride + iJ * nJStride;
     549             : 
     550      761117 :                     if (bHasNoData && pafSrcData[iIndex] == fNoData)
     551             :                     {
     552         204 :                         pafDstData[iIndex] = fNoData;
     553         204 :                         continue;
     554             :                     }
     555             : 
     556      760913 :                     double dfSum = 0.0, dfKernSum = 0.0;
     557      760913 :                     size_t nValidCount = 0;
     558             :                     double dfRes =
     559     1521820 :                         bMin   ? std::numeric_limits<double>::infinity()
     560      760904 :                         : bMax ? -std::numeric_limits<double>::infinity()
     561      760913 :                                : 0.0;
     562      760913 :                     double dfMean = 0.0;
     563      760913 :                     double dfM2 = 0.0;
     564     1521830 :                     std::vector<double> adfVals;
     565      760913 :                     std::map<double, size_t> mapValToCount;
     566      760913 :                     size_t maxCount = 0;
     567             : 
     568     3107250 :                     for (GPtrDiff_t iII = -m_nExtraEdgePixels, iK = 0;
     569     3107250 :                          iII <= m_nExtraEdgePixels; ++iII)
     570             :                     {
     571     9232160 :                         for (GPtrDiff_t iJJ =
     572     2346340 :                                  (m_bSeparable ? 0 : -m_nExtraEdgePixels);
     573     9232160 :                              iJJ <= (m_bSeparable ? 0 : m_nExtraEdgePixels);
     574             :                              ++iJJ, ++iK)
     575             :                         {
     576     6885820 :                             const float *pfData = pafSrcData + iIndex +
     577     6885820 :                                                   iII * nIStride +
     578     6885820 :                                                   iJJ * nJStride;
     579     6885820 :                             if (bHasNoData &&
     580     6885820 :                                 (*pfData == fNoData || std::isnan(*pfData)))
     581             :                             {
     582      976182 :                                 continue;
     583             :                             }
     584     6884960 :                             if (m_adfKernelCoefs[iK] == 0.0)
     585             :                             {
     586      975326 :                                 continue;
     587             :                             }
     588    11819300 :                             const double dfVal = static_cast<double>(*pfData) *
     589     5909640 :                                                  m_adfKernelCoefs[iK];
     590     5909640 :                             ++nValidCount;
     591             : 
     592     5909640 :                             if (bMax)
     593             :                             {
     594         282 :                                 if (dfVal > dfRes)
     595         175 :                                     dfRes = dfVal;
     596             :                             }
     597     5909350 :                             else if (bMin)
     598             :                             {
     599          81 :                                 if (dfVal < dfRes)
     600           9 :                                     dfRes = dfVal;
     601             :                             }
     602     5909270 :                             else if (bStdDev)
     603             :                             {
     604          81 :                                 const double dfDelta = dfVal - dfMean;
     605          81 :                                 dfMean += dfDelta / nValidCount;
     606          81 :                                 dfM2 += dfDelta * (dfVal - dfMean);
     607             :                             }
     608     5909190 :                             else if (bMedian)
     609             :                             {
     610         148 :                                 adfVals.push_back(dfVal);
     611             :                             }
     612     5909040 :                             else if (bMode)
     613             :                             {
     614          81 :                                 const size_t nCountVal = ++mapValToCount[dfVal];
     615          81 :                                 if (nCountVal > maxCount)
     616             :                                 {
     617          26 :                                     maxCount = nCountVal;
     618          26 :                                     dfRes = dfVal;
     619             :                                 }
     620             :                             }
     621             :                             else
     622             :                             {
     623     5908960 :                                 dfSum += dfVal;
     624     5908960 :                                 dfKernSum += m_adfKernelCoefs[iK];
     625             :                             }
     626             :                         }
     627             :                     }
     628             : 
     629             :                     float fResult;
     630      760913 :                     if (bMax || bMin || bMode)
     631             :                     {
     632          51 :                         fResult = nValidCount  ? static_cast<float>(dfRes)
     633           0 :                                   : bHasNoData ? fNoData
     634           0 :                                                : 0.0f;
     635             :                     }
     636      760862 :                     else if (bStdDev)
     637             :                     {
     638           9 :                         fResult =
     639             :                             nValidCount
     640           9 :                                 ? static_cast<float>(sqrt(
     641           9 :                                       dfM2 / static_cast<double>(nValidCount)))
     642           0 :                             : bHasNoData ? fNoData
     643           0 :                                          : 0.0f;
     644             :                     }
     645      760853 :                     else if (bMedian)
     646             :                     {
     647          17 :                         if (!adfVals.empty())
     648             :                         {
     649          17 :                             if ((adfVals.size() % 2) == 1)
     650             :                             {
     651          32 :                                 std::nth_element(adfVals.begin(),
     652           0 :                                                  adfVals.begin() +
     653          16 :                                                      adfVals.size() / 2,
     654             :                                                  adfVals.end());
     655          16 :                                 dfRes = adfVals[adfVals.size() / 2];
     656             :                             }
     657             :                             else
     658             :                             {
     659           2 :                                 std::nth_element(adfVals.begin(),
     660           0 :                                                  adfVals.begin() +
     661           1 :                                                      adfVals.size() / 2 - 1,
     662             :                                                  adfVals.end());
     663           1 :                                 dfRes = adfVals[adfVals.size() / 2 - 1];
     664           2 :                                 std::nth_element(adfVals.begin(),
     665           0 :                                                  adfVals.begin() +
     666           1 :                                                      adfVals.size() / 2,
     667             :                                                  adfVals.end());
     668           1 :                                 dfRes =
     669           1 :                                     (dfRes + adfVals[adfVals.size() / 2]) / 2;
     670             :                             }
     671          17 :                             fResult = static_cast<float>(dfRes);
     672             :                         }
     673             :                         else
     674           0 :                             fResult = bHasNoData ? fNoData : 0.0f;
     675             :                     }
     676      760836 :                     else if (!m_bNormalized)
     677      511627 :                         fResult = static_cast<float>(dfSum);
     678      249209 :                     else if (nValidCount == 0 || dfKernSum == 0.0)
     679           0 :                         fResult = bHasNoData ? fNoData : 0.0f;
     680             :                     else
     681      249209 :                         fResult = static_cast<float>(dfSum / dfKernSum);
     682      760913 :                     pafDstData[iIndex] = fResult;
     683             :                 }
     684             :             }
     685             :         }
     686             :     }
     687             : 
     688          44 :     return CE_None;
     689             : }
     690             : 
     691             : /************************************************************************/
     692             : /*                              XMLInit()                               */
     693             : /************************************************************************/
     694             : 
     695             : CPLErr
     696          14 : VRTKernelFilteredSource::XMLInit(const CPLXMLNode *psTree,
     697             :                                  const char *pszVRTPath,
     698             :                                  VRTMapSharedResources &oMapSharedSources)
     699             : 
     700             : {
     701             :     {
     702             :         const CPLErr eErr =
     703          14 :             VRTFilteredSource::XMLInit(psTree, pszVRTPath, oMapSharedSources);
     704          14 :         if (eErr != CE_None)
     705           0 :             return eErr;
     706             :     }
     707             : 
     708          14 :     const int nNewKernelSize = atoi(CPLGetXMLValue(psTree, "Kernel.Size", "0"));
     709             : 
     710          14 :     if (nNewKernelSize == 0)
     711           0 :         return CE_None;
     712             :     // To prevent a integer overflow when computing nNewKernelSize *
     713             :     // nNewKernelSize
     714          27 :     if (nNewKernelSize < 0 ||
     715          13 :         nNewKernelSize > static_cast<int>(std::sqrt(static_cast<double>(
     716          13 :                              std::numeric_limits<int>::max()))))
     717             :     {
     718           2 :         CPLError(CE_Failure, CPLE_IllegalArg,
     719             :                  "Invalid value for kernel size: %d", nNewKernelSize);
     720           2 :         return CE_Failure;
     721             :     }
     722             : 
     723             :     const CPLStringList aosCoefItems(
     724          24 :         CSLTokenizeString(CPLGetXMLValue(psTree, "Kernel.Coefs", "")));
     725             : 
     726          12 :     const int nCoefs = aosCoefItems.size();
     727             : 
     728          12 :     const bool bSquare = nCoefs == nNewKernelSize * nNewKernelSize;
     729          12 :     const bool bSeparable = nCoefs == nNewKernelSize && nCoefs != 1;
     730             : 
     731          12 :     if (!bSquare && !bSeparable)
     732             :     {
     733           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     734             :                  "Got wrong number of filter kernel coefficients (%s).  "
     735             :                  "Expected %d or %d, got %d.",
     736             :                  CPLGetXMLValue(psTree, "Kernel.Coefs", ""),
     737             :                  nNewKernelSize * nNewKernelSize, nNewKernelSize, nCoefs);
     738           0 :         return CE_Failure;
     739             :     }
     740             : 
     741          24 :     std::vector<double> adfNewCoefs;
     742          12 :     adfNewCoefs.reserve(nCoefs);
     743         122 :     for (int i = 0; i < nCoefs; i++)
     744         110 :         adfNewCoefs.push_back(CPLAtof(aosCoefItems[i]));
     745             : 
     746          12 :     const CPLErr eErr = SetKernel(nNewKernelSize, bSeparable, adfNewCoefs);
     747          12 :     if (eErr == CE_None)
     748             :     {
     749          12 :         SetNormalized(atoi(CPLGetXMLValue(psTree, "Kernel.normalized", "0")) !=
     750             :                       0);
     751             :     }
     752             : 
     753          12 :     const char *pszFunction = CPLGetXMLValue(psTree, "Function", nullptr);
     754          12 :     if (pszFunction)
     755             :     {
     756           0 :         if (!EQUAL(pszFunction, "max") && !EQUAL(pszFunction, "min") &&
     757           0 :             !EQUAL(pszFunction, "median") && !EQUAL(pszFunction, "mode") &&
     758           0 :             !EQUAL(pszFunction, "stddev"))
     759             :         {
     760           0 :             CPLError(CE_Failure, CPLE_IllegalArg, "Unsupported function: %s",
     761             :                      pszFunction);
     762           0 :             return CE_Failure;
     763             :         }
     764           0 :         SetFunction(pszFunction);
     765             :     }
     766             : 
     767          12 :     return eErr;
     768             : }
     769             : 
     770             : /************************************************************************/
     771             : /*                           SerializeToXML()                           */
     772             : /************************************************************************/
     773             : 
     774           2 : CPLXMLNode *VRTKernelFilteredSource::SerializeToXML(const char *pszVRTPath)
     775             : 
     776             : {
     777           2 :     CPLXMLNode *psSrc = VRTFilteredSource::SerializeToXML(pszVRTPath);
     778             : 
     779           2 :     if (psSrc == nullptr)
     780           0 :         return nullptr;
     781             : 
     782           2 :     CPLFree(psSrc->pszValue);
     783           2 :     psSrc->pszValue = CPLStrdup("KernelFilteredSource");
     784             : 
     785           2 :     if (m_nKernelSize == 0)
     786           0 :         return psSrc;
     787             : 
     788           2 :     CPLXMLNode *psKernel = CPLCreateXMLNode(psSrc, CXT_Element, "Kernel");
     789             : 
     790           2 :     CPLCreateXMLNode(CPLCreateXMLNode(psKernel, CXT_Attribute, "normalized"),
     791           2 :                      CXT_Text, m_bNormalized ? "1" : "0");
     792             : 
     793           2 :     std::string osCoefs;
     794          14 :     for (auto dfVal : m_adfKernelCoefs)
     795             :     {
     796          12 :         if (!osCoefs.empty())
     797          10 :             osCoefs += ' ';
     798          12 :         osCoefs += CPLSPrintf("%.8g", dfVal);
     799             :     }
     800             : 
     801           2 :     CPLSetXMLValue(psKernel, "Size", CPLSPrintf("%d", m_nKernelSize));
     802           2 :     CPLSetXMLValue(psKernel, "Coefs", osCoefs.c_str());
     803             : 
     804           2 :     if (!m_function.empty())
     805           0 :         CPLCreateXMLElementAndValue(psSrc, "Function", m_function.c_str());
     806             : 
     807           2 :     return psSrc;
     808             : }
     809             : 
     810             : /************************************************************************/
     811             : /*                       VRTParseFilterSources()                        */
     812             : /************************************************************************/
     813             : 
     814          14 : VRTSource *VRTParseFilterSources(const CPLXMLNode *psChild,
     815             :                                  const char *pszVRTPath,
     816             :                                  VRTMapSharedResources &oMapSharedSources)
     817             : 
     818             : {
     819          14 :     if (EQUAL(psChild->pszValue, "KernelFilteredSource"))
     820             :     {
     821          14 :         VRTSource *poSrc = new VRTKernelFilteredSource();
     822          14 :         if (poSrc->XMLInit(psChild, pszVRTPath, oMapSharedSources) == CE_None)
     823          12 :             return poSrc;
     824             : 
     825           2 :         delete poSrc;
     826             :     }
     827             : 
     828           2 :     return nullptr;
     829             : }
     830             : 
     831             : /*! @endcond */

Generated by: LCOV version 1.14