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

Generated by: LCOV version 1.14