LCOV - code coverage report
Current view: top level - alg - gdal_interpolateatpoint.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 203 210 96.7 %
Date: 2025-05-20 14:45:53 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Project:  GDAL DEM Interpolation
       5             :  * Purpose:  Interpolation algorithms with cache
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2001, Frank Warmerdam
      10             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      11             :  * Copyright (c) 2024, Javier Jimenez Shaw
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "gdal_interpolateatpoint.h"
      17             : 
      18             : #include "gdalresamplingkernels.h"
      19             : 
      20             : #include "gdal_vectorx.h"
      21             : 
      22             : #include <algorithm>
      23             : #include <complex>
      24             : 
      25             : template <typename T> bool areEqualReal(double dfNoDataValue, T dfOut);
      26             : 
      27       16750 : template <> bool areEqualReal(double dfNoDataValue, double dfOut)
      28             : {
      29       16750 :     return ARE_REAL_EQUAL(dfNoDataValue, dfOut);
      30             : }
      31             : 
      32          76 : template <> bool areEqualReal(double dfNoDataValue, std::complex<double> dfOut)
      33             : {
      34          76 :     return ARE_REAL_EQUAL(dfNoDataValue, dfOut.real());
      35             : }
      36             : 
      37             : // Only valid for T = double or std::complex<double>
      38             : template <typename T>
      39      254600 : bool GDALInterpExtractValuesWindow(GDALRasterBand *pBand,
      40             :                                    std::unique_ptr<DoublePointsCache> &cache,
      41             :                                    gdal::Vector2i point,
      42             :                                    gdal::Vector2i dimensions, T *padfOut)
      43             : {
      44      254600 :     constexpr int BLOCK_SIZE = 64;
      45             : 
      46      254600 :     const int nX = point.x();
      47      254600 :     const int nY = point.y();
      48      254600 :     const int nWidth = dimensions.x();
      49      254600 :     const int nHeight = dimensions.y();
      50             : 
      51             :     // Request the DEM by blocks of BLOCK_SIZE * BLOCK_SIZE and put them
      52             :     // in cache
      53      254600 :     if (!cache)
      54         100 :         cache.reset(new DoublePointsCache{});
      55             : 
      56      254600 :     const int nXIters = (nX + nWidth - 1) / BLOCK_SIZE - nX / BLOCK_SIZE + 1;
      57      254600 :     const int nYIters = (nY + nHeight - 1) / BLOCK_SIZE - nY / BLOCK_SIZE + 1;
      58      254600 :     const int nRasterXSize = pBand->GetXSize();
      59      254600 :     const int nRasterYSize = pBand->GetYSize();
      60             :     const bool bIsComplex =
      61      254600 :         CPL_TO_BOOL(GDALDataTypeIsComplex(pBand->GetRasterDataType()));
      62             : 
      63      509636 :     for (int iY = 0; iY < nYIters; iY++)
      64             :     {
      65      255036 :         const int nBlockY = nY / BLOCK_SIZE + iY;
      66      255036 :         const int nReqYSize =
      67      255036 :             std::min(nRasterYSize - nBlockY * BLOCK_SIZE, BLOCK_SIZE);
      68      255036 :         const int nFirstLineInCachedBlock = (iY == 0) ? nY % BLOCK_SIZE : 0;
      69      255036 :         const int nFirstLineInOutput =
      70             :             (iY == 0) ? 0
      71         436 :                       : BLOCK_SIZE - (nY % BLOCK_SIZE) + (iY - 1) * BLOCK_SIZE;
      72      255908 :         const int nLinesToCopy = (nYIters == 1) ? nHeight
      73         436 :                                  : (iY == 0)    ? BLOCK_SIZE - (nY % BLOCK_SIZE)
      74         436 :                                  : (iY == nYIters - 1)
      75         436 :                                      ? 1 + (nY + nHeight - 1) % BLOCK_SIZE
      76             :                                      : BLOCK_SIZE;
      77      514121 :         for (int iX = 0; iX < nXIters; iX++)
      78             :         {
      79      259085 :             const int nBlockX = nX / BLOCK_SIZE + iX;
      80      259085 :             const int nReqXSize =
      81      259085 :                 std::min(nRasterXSize - nBlockX * BLOCK_SIZE, BLOCK_SIZE);
      82      259085 :             const uint64_t nKey =
      83      259085 :                 (static_cast<uint64_t>(nBlockY) << 32) | nBlockX;
      84      259085 :             const int nFirstColInCachedBlock = (iX == 0) ? nX % BLOCK_SIZE : 0;
      85      259085 :             const int nFirstColInOutput =
      86             :                 (iX == 0)
      87             :                     ? 0
      88        4049 :                     : BLOCK_SIZE - (nX % BLOCK_SIZE) + (iX - 1) * BLOCK_SIZE;
      89      267183 :             const int nColsToCopy = (nXIters == 1) ? nWidth
      90        4049 :                                     : (iX == 0) ? BLOCK_SIZE - (nX % BLOCK_SIZE)
      91        4049 :                                     : (iX == nXIters - 1)
      92        4049 :                                         ? 1 + (nX + nWidth - 1) % BLOCK_SIZE
      93             :                                         : BLOCK_SIZE;
      94             : 
      95             : #if 0
      96             :             CPLDebug("RPC", "nY=%d nX=%d nBlockY=%d nBlockX=%d "
      97             :                      "nFirstLineInCachedBlock=%d nFirstLineInOutput=%d nLinesToCopy=%d "
      98             :                      "nFirstColInCachedBlock=%d nFirstColInOutput=%d nColsToCopy=%d",
      99             :                      nY, nX, nBlockY, nBlockX, nFirstLineInCachedBlock, nFirstLineInOutput, nLinesToCopy,
     100             :                      nFirstColInCachedBlock, nFirstColInOutput, nColsToCopy);
     101             : #endif
     102             : 
     103      259085 :             constexpr int nTypeFactor = sizeof(T) / sizeof(double);
     104           0 :             std::shared_ptr<std::vector<double>> poValue;
     105      259085 :             if (!cache->tryGet(nKey, poValue))
     106             :             {
     107         739 :                 const GDALDataType eDataType =
     108             :                     bIsComplex ? GDT_CFloat64 : GDT_Float64;
     109         739 :                 const size_t nVectorSize =
     110         739 :                     size_t(nReqXSize) * nReqYSize * nTypeFactor;
     111         739 :                 poValue = std::make_shared<std::vector<double>>(nVectorSize);
     112         739 :                 CPLErr eErr = pBand->RasterIO(
     113             :                     GF_Read, nBlockX * BLOCK_SIZE, nBlockY * BLOCK_SIZE,
     114         739 :                     nReqXSize, nReqYSize, poValue->data(), nReqXSize, nReqYSize,
     115             :                     eDataType, 0, 0, nullptr);
     116         739 :                 if (eErr != CE_None)
     117             :                 {
     118           0 :                     return false;
     119             :                 }
     120         739 :                 cache->insert(nKey, poValue);
     121             :             }
     122             : 
     123      259085 :             double *padfAsDouble = reinterpret_cast<double *>(padfOut);
     124             :             // Compose the cached block to the final buffer
     125      776350 :             for (int j = 0; j < nLinesToCopy; j++)
     126             :             {
     127      517265 :                 const size_t dstOffset =
     128      517265 :                     (static_cast<size_t>(nFirstLineInOutput + j) * nWidth +
     129      517265 :                      nFirstColInOutput) *
     130             :                     nTypeFactor;
     131      517265 :                 const size_t srcOffset =
     132      517265 :                     (static_cast<size_t>(nFirstLineInCachedBlock + j) *
     133      517265 :                          nReqXSize +
     134      517265 :                      nFirstColInCachedBlock) *
     135             :                     nTypeFactor;
     136      517265 :                 if (srcOffset + nColsToCopy * nTypeFactor > poValue->size())
     137             :                 {
     138           0 :                     return false;
     139             :                 }
     140      517265 :                 memcpy(padfAsDouble + dstOffset, poValue->data() + srcOffset,
     141      517265 :                        nColsToCopy * sizeof(T));
     142             :             }
     143             :         }
     144             :     }
     145             : 
     146             : #if 0
     147             :     CPLDebug("RPC_DEM", "DEM for %d,%d,%d,%d", nX, nY, nWidth, nHeight);
     148             :     for(int j = 0; j < nHeight; j++)
     149             :     {
     150             :         std::string osLine;
     151             :         for(int i = 0; i < nWidth; ++i )
     152             :         {
     153             :             if( !osLine.empty() )
     154             :                 osLine += ", ";
     155             :             osLine += std::to_string(padfOut[j * nWidth + i]);
     156             :         }
     157             :         CPLDebug("RPC_DEM", "%s", osLine.c_str());
     158             :     }
     159             : #endif
     160             : 
     161      254600 :     return true;
     162             : }
     163             : 
     164             : template <typename T>
     165      307910 : bool GDALInterpolateAtPointImpl(GDALRasterBand *pBand,
     166             :                                 GDALRIOResampleAlg eResampleAlg,
     167             :                                 std::unique_ptr<DoublePointsCache> &cache,
     168             :                                 double dfXIn, double dfYIn, T &out)
     169             : {
     170      307910 :     const gdal::Vector2i rasterSize{pBand->GetXSize(), pBand->GetYSize()};
     171             : 
     172      307910 :     if (eResampleAlg == GRIORA_NearestNeighbour)
     173             :     {
     174             :         // Allow input coordinates right at the bottom or right edge
     175             :         // with GRIORA_NearestNeighbour.
     176             :         // "introduce" them in the pixel of the image.
     177         100 :         if (dfXIn >= rasterSize.x() && dfXIn <= rasterSize.x() + 1e-5)
     178           5 :             dfXIn -= 0.25;
     179         100 :         if (dfYIn >= rasterSize.y() && dfYIn <= rasterSize.y() + 1e-5)
     180           5 :             dfYIn -= 0.25;
     181             :     }
     182      307910 :     const gdal::Vector2d inLoc{dfXIn, dfYIn};
     183             : 
     184      307910 :     int bGotNoDataValue = FALSE;
     185      307910 :     const double dfNoDataValue = pBand->GetNoDataValue(&bGotNoDataValue);
     186             : 
     187      562530 :     if (inLoc.x() < 0 || inLoc.x() > rasterSize.x() || inLoc.y() < 0 ||
     188      254620 :         inLoc.y() > rasterSize.y())
     189             :     {
     190       53310 :         return FALSE;
     191             :     }
     192             : 
     193             :     // Downgrade the interpolation algorithm if the image is too small
     194      254613 :     if ((rasterSize.x() < 4 || rasterSize.y() < 4) &&
     195          13 :         (eResampleAlg == GRIORA_CubicSpline || eResampleAlg == GRIORA_Cubic))
     196             :     {
     197           0 :         eResampleAlg = GRIORA_Bilinear;
     198             :     }
     199      254605 :     if ((rasterSize.x() < 2 || rasterSize.y() < 2) &&
     200           5 :         eResampleAlg == GRIORA_Bilinear)
     201             :     {
     202           0 :         eResampleAlg = GRIORA_NearestNeighbour;
     203             :     }
     204             : 
     205             :     auto outOfBorderCorrectionSimple =
     206      509018 :         [](int dNew, int nRasterSize, int nKernelsize)
     207             :     {
     208      509018 :         int dOutOfBorder = 0;
     209      509018 :         if (dNew < 0)
     210             :         {
     211       34586 :             dOutOfBorder = dNew;
     212             :         }
     213      509018 :         if (dNew + nKernelsize >= nRasterSize)
     214             :         {
     215       45848 :             dOutOfBorder = dNew + nKernelsize - nRasterSize;
     216             :         }
     217      509018 :         return dOutOfBorder;
     218             :     };
     219             : 
     220      254600 :     auto outOfBorderCorrection =
     221      509018 :         [&outOfBorderCorrectionSimple,
     222             :          &rasterSize](gdal::Vector2i input, int nKernelsize) -> gdal::Vector2i
     223             :     {
     224             :         return {
     225      254509 :             outOfBorderCorrectionSimple(input.x(), rasterSize.x(), nKernelsize),
     226      254509 :             outOfBorderCorrectionSimple(input.y(), rasterSize.y(),
     227      763527 :                                         nKernelsize)};
     228             :     };
     229             : 
     230             :     auto dragReadDataInBorderSimple =
     231     1052638 :         [](T *adfElevData, int dOutOfBorder, int nKernelSize, bool bIsX)
     232             :     {
     233      543616 :         while (dOutOfBorder < 0)
     234             :         {
     235      103848 :             for (int j = 0; j < nKernelSize; j++)
     236      138716 :                 for (int ii = 0; ii < nKernelSize - 1; ii++)
     237             :                 {
     238       69466 :                     const int i = nKernelSize - ii - 2;  // iterate in reverse
     239       69466 :                     const int row_src = bIsX ? j : i;
     240       69466 :                     const int row_dst = bIsX ? j : i + 1;
     241       69466 :                     const int col_src = bIsX ? i : j;
     242       69466 :                     const int col_dst = bIsX ? i + 1 : j;
     243       69466 :                     adfElevData[nKernelSize * row_dst + col_dst] =
     244       69466 :                         adfElevData[nKernelSize * row_src + col_src];
     245             :                 }
     246       34598 :             dOutOfBorder++;
     247             :         }
     248      543482 :         while (dOutOfBorder > 0)
     249             :         {
     250      103410 :             for (int j = 0; j < nKernelSize; j++)
     251      137964 :                 for (int i = 0; i < nKernelSize - 1; i++)
     252             :                 {
     253       69018 :                     const int row_src = bIsX ? j : i + 1;
     254       69018 :                     const int row_dst = bIsX ? j : i;
     255       69018 :                     const int col_src = bIsX ? i + 1 : j;
     256       69018 :                     const int col_dst = bIsX ? i : j;
     257       69018 :                     adfElevData[nKernelSize * row_dst + col_dst] =
     258       69018 :                         adfElevData[nKernelSize * row_src + col_src];
     259             :                 }
     260       34464 :             dOutOfBorder--;
     261             :         }
     262             :     };
     263     1018127 :     auto dragReadDataInBorder = [&dragReadDataInBorderSimple](
     264             :                                     T *adfElevData, gdal::Vector2i dOutOfBorder,
     265             :                                     int nKernelSize) -> void
     266             :     {
     267      254509 :         dragReadDataInBorderSimple(adfElevData, dOutOfBorder.x(), nKernelSize,
     268             :                                    true);
     269      254509 :         dragReadDataInBorderSimple(adfElevData, dOutOfBorder.y(), nKernelSize,
     270             :                                    false);
     271             :     };
     272             : 
     273      525756 :     auto applyBilinearKernel = [&](gdal::Vector2d dfDelta, T *adfValues,
     274             :                                    T &pdfRes) -> bool
     275             :     {
     276      254468 :         if (bGotNoDataValue)
     277             :         {
     278             :             // TODO: We could perhaps use a valid sample if there's one.
     279        4172 :             bool bFoundNoDataElev = false;
     280       20860 :             for (int k_i = 0; k_i < 4; k_i++)
     281             :             {
     282       16688 :                 if (areEqualReal(dfNoDataValue, adfValues[k_i]))
     283          16 :                     bFoundNoDataElev = true;
     284             :             }
     285        4172 :             if (bFoundNoDataElev)
     286             :             {
     287           4 :                 return FALSE;
     288             :             }
     289             :         }
     290      254464 :         const gdal::Vector2d dfDelta1 = 1.0 - dfDelta;
     291             : 
     292      254459 :         const T dfXZ1 =
     293      254464 :             adfValues[0] * dfDelta1.x() + adfValues[1] * dfDelta.x();
     294      254459 :         const T dfXZ2 =
     295      254464 :             adfValues[2] * dfDelta1.x() + adfValues[3] * dfDelta.x();
     296      254464 :         const T dfYZ = dfXZ1 * dfDelta1.y() + dfXZ2 * dfDelta.y();
     297             : 
     298      254464 :         pdfRes = dfYZ;
     299      254464 :         return TRUE;
     300             :     };
     301             : 
     302      256081 :     auto apply4x4Kernel = [&](gdal::Vector2d dfDelta, T *adfValues,
     303             :                               T &pdfRes) -> bool
     304             :     {
     305          41 :         T dfSumH = 0.0;
     306          41 :         T dfSumWeight = 0.0;
     307         205 :         for (int k_i = 0; k_i < 4; k_i++)
     308             :         {
     309             :             // Loop across the X axis.
     310         820 :             for (int k_j = 0; k_j < 4; k_j++)
     311             :             {
     312             :                 // Calculate the weight for the specified pixel according
     313             :                 // to the bicubic b-spline kernel we're using for
     314             :                 // interpolation.
     315         656 :                 const gdal::Vector2i dKernInd = {k_j - 1, k_i - 1};
     316         656 :                 const gdal::Vector2d fPoint = dKernInd.cast<double>() - dfDelta;
     317         656 :                 const double dfPixelWeight =
     318         656 :                     eResampleAlg == GDALRIOResampleAlg::GRIORA_CubicSpline
     319         320 :                         ? CubicSplineKernel(fPoint.x()) *
     320         320 :                               CubicSplineKernel(fPoint.y())
     321         336 :                         : CubicKernel(fPoint.x()) * CubicKernel(fPoint.y());
     322             : 
     323             :                 // Create a sum of all values
     324             :                 // adjusted for the pixel's calculated weight.
     325         656 :                 const T dfElev = adfValues[k_j + k_i * 4];
     326         656 :                 if (bGotNoDataValue && areEqualReal(dfNoDataValue, dfElev))
     327          64 :                     continue;
     328             : 
     329         112 :                 dfSumH += dfElev * dfPixelWeight;
     330         592 :                 dfSumWeight += dfPixelWeight;
     331             :             }
     332             :         }
     333          41 :         if (dfSumWeight == 0.0)
     334             :         {
     335           4 :             return FALSE;
     336             :         }
     337             : 
     338           7 :         pdfRes = dfSumH / dfSumWeight;
     339             : 
     340          37 :         return TRUE;
     341             :     };
     342             : 
     343      254600 :     if (eResampleAlg == GDALRIOResampleAlg::GRIORA_CubicSpline ||
     344      254580 :         eResampleAlg == GDALRIOResampleAlg::GRIORA_Cubic)
     345             :     {
     346             :         // Convert from upper left corner of pixel coordinates to center of
     347             :         // pixel coordinates:
     348          41 :         const gdal::Vector2d df = inLoc - 0.5;
     349          41 :         const gdal::Vector2i d = df.floor().template cast<int>();
     350          41 :         const gdal::Vector2d delta = df - d.cast<double>();
     351          41 :         const gdal::Vector2i dNew = d - 1;
     352          41 :         const int nKernelSize = 4;
     353             :         const gdal::Vector2i dOutOfBorder =
     354          41 :             outOfBorderCorrection(dNew, nKernelSize);
     355             : 
     356             :         // CubicSpline interpolation.
     357          41 :         T adfReadData[16] = {0.0};
     358          41 :         if (!GDALInterpExtractValuesWindow(pBand, cache, dNew - dOutOfBorder,
     359             :                                            {nKernelSize, nKernelSize},
     360             :                                            adfReadData))
     361             :         {
     362           0 :             return FALSE;
     363             :         }
     364          41 :         dragReadDataInBorder(adfReadData, dOutOfBorder, nKernelSize);
     365          41 :         if (!apply4x4Kernel(delta, adfReadData, out))
     366           4 :             return FALSE;
     367             : 
     368          37 :         return TRUE;
     369             :     }
     370      254559 :     else if (eResampleAlg == GDALRIOResampleAlg::GRIORA_Bilinear)
     371             :     {
     372             :         // Convert from upper left corner of pixel coordinates to center of
     373             :         // pixel coordinates:
     374      254468 :         const gdal::Vector2d df = inLoc - 0.5;
     375      254468 :         const gdal::Vector2i d = df.floor().template cast<int>();
     376      254468 :         const gdal::Vector2d delta = df - d.cast<double>();
     377      254468 :         const int nKernelSize = 2;
     378             :         const gdal::Vector2i dOutOfBorder =
     379      254468 :             outOfBorderCorrection(d, nKernelSize);
     380             : 
     381             :         // Bilinear interpolation.
     382      254468 :         T adfReadData[4] = {0.0};
     383      254468 :         if (!GDALInterpExtractValuesWindow(pBand, cache, d - dOutOfBorder,
     384             :                                            {nKernelSize, nKernelSize},
     385             :                                            adfReadData))
     386             :         {
     387           0 :             return FALSE;
     388             :         }
     389      254468 :         dragReadDataInBorder(adfReadData, dOutOfBorder, nKernelSize);
     390      254468 :         if (!applyBilinearKernel(delta, adfReadData, out))
     391           4 :             return FALSE;
     392             : 
     393      254464 :         return TRUE;
     394             :     }
     395             :     else
     396             :     {
     397          91 :         const gdal::Vector2i d = inLoc.cast<int>();
     398          91 :         T adfOut[1] = {};
     399         182 :         if (!GDALInterpExtractValuesWindow(pBand, cache, d, {1, 1}, adfOut) ||
     400          91 :             (bGotNoDataValue && areEqualReal(dfNoDataValue, adfOut[0])))
     401             :         {
     402           5 :             return FALSE;
     403             :         }
     404             : 
     405          86 :         out = adfOut[0];
     406             : 
     407          86 :         return TRUE;
     408             :     }
     409             : }
     410             : 
     411             : /************************************************************************/
     412             : /*                        GDALInterpolateAtPoint()                      */
     413             : /************************************************************************/
     414             : 
     415      307910 : bool GDALInterpolateAtPoint(GDALRasterBand *pBand,
     416             :                             GDALRIOResampleAlg eResampleAlg,
     417             :                             std::unique_ptr<DoublePointsCache> &cache,
     418             :                             const double dfXIn, const double dfYIn,
     419             :                             double *pdfOutputReal, double *pdfOutputImag)
     420             : {
     421             :     const bool bIsComplex =
     422      307910 :         CPL_TO_BOOL(GDALDataTypeIsComplex(pBand->GetRasterDataType()));
     423      307910 :     bool res = TRUE;
     424      307910 :     if (bIsComplex)
     425             :     {
     426          23 :         std::complex<double> out{};
     427          23 :         res = GDALInterpolateAtPointImpl(pBand, eResampleAlg, cache, dfXIn,
     428             :                                          dfYIn, out);
     429          23 :         *pdfOutputReal = out.real();
     430          23 :         if (pdfOutputImag)
     431          23 :             *pdfOutputImag = out.imag();
     432             :     }
     433             :     else
     434             :     {
     435      307887 :         double out{};
     436      307887 :         res = GDALInterpolateAtPointImpl(pBand, eResampleAlg, cache, dfXIn,
     437             :                                          dfYIn, out);
     438      307887 :         *pdfOutputReal = out;
     439      307887 :         if (pdfOutputImag)
     440         119 :             *pdfOutputImag = 0;
     441             :     }
     442      307910 :     return res;
     443             : }

Generated by: LCOV version 1.14