LCOV - code coverage report
Current view: top level - alg - gdalsievefilter.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 166 188 88.3 %
Date: 2024-05-04 12:52:34 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Raster to Polygon Converter
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008, Frank Warmerdam
       9             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "gdal_alg.h"
      32             : 
      33             : #include <cstring>
      34             : 
      35             : #include <algorithm>
      36             : #include <cassert>
      37             : #include <set>
      38             : #include <vector>
      39             : #include <utility>
      40             : 
      41             : #include "cpl_conv.h"
      42             : #include "cpl_error.h"
      43             : #include "cpl_progress.h"
      44             : #include "cpl_vsi.h"
      45             : #include "gdal.h"
      46             : #include "gdal_alg_priv.h"
      47             : 
      48             : #define MY_MAX_INT 2147483647
      49             : 
      50             : /*
      51             :  * General Plan
      52             :  *
      53             :  * 1) make a pass with the polygon enumerator to build up the
      54             :  *    polygon map array.  Also accumulate polygon size information.
      55             :  *
      56             :  * 2) Identify the polygons that need to be merged.
      57             :  *
      58             :  * 3) Make a pass with the polygon enumerator.  For each "to be merged"
      59             :  *    polygon keep track of its largest neighbour.
      60             :  *
      61             :  * 4) Fix up remappings that would go to polygons smaller than the seive
      62             :  *    size.  Ensure these in term map to the largest neighbour of the
      63             :  *    "to be sieved" polygons.
      64             :  *
      65             :  * 5) Make another pass with the polygon enumerator. This time we remap
      66             :  *    the actual pixel values of all polygons to be merged.
      67             :  */
      68             : 
      69             : /************************************************************************/
      70             : /*                          GPMaskImageData()                           */
      71             : /*                                                                      */
      72             : /*      Mask out image pixels to a special nodata value if the mask     */
      73             : /*      band is zero.                                                   */
      74             : /************************************************************************/
      75             : 
      76         123 : static CPLErr GPMaskImageData(GDALRasterBandH hMaskBand, GByte *pabyMaskLine,
      77             :                               int iY, int nXSize, std::int64_t *panImageLine)
      78             : 
      79             : {
      80         123 :     const CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1,
      81             :                                      pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0);
      82         123 :     if (eErr == CE_None)
      83             :     {
      84        1722 :         for (int i = 0; i < nXSize; i++)
      85             :         {
      86        1599 :             if (pabyMaskLine[i] == 0)
      87          48 :                 panImageLine[i] = GP_NODATA_MARKER;
      88             :         }
      89             :     }
      90             : 
      91         123 :     return eErr;
      92             : }
      93             : 
      94             : // TODO: What is "eaches" supposed to be?
      95             : 
      96             : /************************************************************************/
      97             : /*                          CompareNeighbour()                          */
      98             : /*                                                                      */
      99             : /*      Compare two neighbouring polygons, and update eaches            */
     100             : /*      "biggest neighbour" if the other is larger than its current     */
     101             : /*      largest neighbour.                                              */
     102             : /*                                                                      */
     103             : /*      Note that this should end up with each polygon knowing the      */
     104             : /*      id of its largest neighbour.  No attempt is made to             */
     105             : /*      restrict things to small polygons that we will be merging,      */
     106             : /*      nor to exclude assigning "biggest neighbours" that are still    */
     107             : /*      smaller than our sieve threshold.                               */
     108             : /************************************************************************/
     109             : 
     110       22263 : static inline void CompareNeighbour(int nPolyId1, int nPolyId2,
     111             :                                     int *panPolyIdMap,
     112             :                                     std::int64_t * /* panPolyValue */,
     113             :                                     const std::vector<int> &anPolySizes,
     114             :                                     std::vector<int> &anBigNeighbour)
     115             : 
     116             : {
     117             :     // Nodata polygon do not need neighbours, and cannot be neighbours
     118             :     // to valid polygons.
     119       22263 :     if (nPolyId1 < 0 || nPolyId2 < 0)
     120          34 :         return;
     121             : 
     122             :     // Make sure we are working with the final merged polygon ids.
     123       22229 :     nPolyId1 = panPolyIdMap[nPolyId1];
     124       22229 :     nPolyId2 = panPolyIdMap[nPolyId2];
     125             : 
     126       22229 :     if (nPolyId1 == nPolyId2)
     127        7680 :         return;
     128             : 
     129             :     // Nodata polygon do not need neighbours, and cannot be neighbours
     130             :     // to valid polygons.
     131             :     // Should no longer happen with r28826 optimization.
     132             :     // if( panPolyValue[nPolyId1] == GP_NODATA_MARKER
     133             :     //    || panPolyValue[nPolyId2] == GP_NODATA_MARKER )
     134             :     //    return;
     135             : 
     136       25407 :     if (anBigNeighbour[nPolyId1] == -1 ||
     137       10858 :         anPolySizes[anBigNeighbour[nPolyId1]] < anPolySizes[nPolyId2])
     138        3811 :         anBigNeighbour[nPolyId1] = nPolyId2;
     139             : 
     140       29088 :     if (anBigNeighbour[nPolyId2] == -1 ||
     141       14539 :         anPolySizes[anBigNeighbour[nPolyId2]] < anPolySizes[nPolyId1])
     142         175 :         anBigNeighbour[nPolyId2] = nPolyId1;
     143             : }
     144             : 
     145             : /************************************************************************/
     146             : /*                          GDALSieveFilter()                           */
     147             : /************************************************************************/
     148             : 
     149             : /**
     150             :  * Removes small raster polygons.
     151             :  *
     152             :  * The function removes raster polygons smaller than a provided
     153             :  * threshold size (in pixels) and replaces them with the pixel value
     154             :  * of the largest neighbour polygon.
     155             :  *
     156             :  * Polygon are determined (per GDALRasterPolygonEnumerator) as regions of
     157             :  * the raster where the pixels all have the same value, and that are contiguous
     158             :  * (connected).
     159             :  *
     160             :  * Pixels determined to be "nodata" per hMaskBand will not be treated as part
     161             :  * of a polygon regardless of their pixel values.  Nodata areas will never be
     162             :  * changed nor affect polygon sizes.
     163             :  *
     164             :  * Polygons smaller than the threshold with no neighbours that are as large
     165             :  * as the threshold will not be altered.  Polygons surrounded by nodata areas
     166             :  * will therefore not be altered.
     167             :  *
     168             :  * The algorithm makes three passes over the input file to enumerate the
     169             :  * polygons and collect limited information about them.  Memory use is
     170             :  * proportional to the number of polygons (roughly 24 bytes per polygon), but
     171             :  * is not directly related to the size of the raster.  So very large raster
     172             :  * files can be processed effectively if there aren't too many polygons.  But
     173             :  * extremely noisy rasters with many one pixel polygons will end up being
     174             :  * expensive (in memory) to process.
     175             :  *
     176             :  * @param hSrcBand the source raster band to be processed.
     177             :  * @param hMaskBand an optional mask band.  All pixels in the mask band with a
     178             :  * value other than zero will be considered suitable for inclusion in polygons.
     179             :  * @param hDstBand the output raster band.  It may be the same as hSrcBand
     180             :  * to update the source in place.
     181             :  * @param nSizeThreshold raster polygons with sizes smaller than this will
     182             :  * be merged into their largest neighbour.
     183             :  * @param nConnectedness either 4 indicating that diagonal pixels are not
     184             :  * considered directly adjacent for polygon membership purposes or 8
     185             :  * indicating they are.
     186             :  * @param papszOptions algorithm options in name=value list form.  None
     187             :  * currently supported.
     188             :  * @param pfnProgress callback for reporting algorithm progress matching the
     189             :  * GDALProgressFunc() semantics.  May be NULL.
     190             :  * @param pProgressArg callback argument passed to pfnProgress.
     191             :  *
     192             :  * @return CE_None on success or CE_Failure if an error occurs.
     193             :  */
     194             : 
     195          10 : CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand,
     196             :                                    GDALRasterBandH hMaskBand,
     197             :                                    GDALRasterBandH hDstBand, int nSizeThreshold,
     198             :                                    int nConnectedness,
     199             :                                    CPL_UNUSED char **papszOptions,
     200             :                                    GDALProgressFunc pfnProgress,
     201             :                                    void *pProgressArg)
     202             : {
     203          10 :     VALIDATE_POINTER1(hSrcBand, "GDALSieveFilter", CE_Failure);
     204          10 :     VALIDATE_POINTER1(hDstBand, "GDALSieveFilter", CE_Failure);
     205             : 
     206          10 :     if (pfnProgress == nullptr)
     207           8 :         pfnProgress = GDALDummyProgress;
     208             : 
     209             :     /* -------------------------------------------------------------------- */
     210             :     /*      Allocate working buffers.                                       */
     211             :     /* -------------------------------------------------------------------- */
     212          10 :     int nXSize = GDALGetRasterBandXSize(hSrcBand);
     213          10 :     int nYSize = GDALGetRasterBandYSize(hSrcBand);
     214             :     auto *panLastLineVal = static_cast<std::int64_t *>(
     215          10 :         VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize));
     216             :     auto *panThisLineVal = static_cast<std::int64_t *>(
     217          10 :         VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize));
     218             :     auto *panLastLineId =
     219          10 :         static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
     220             :     auto *panThisLineId =
     221          10 :         static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
     222             :     auto *panThisLineWriteVal = static_cast<std::int64_t *>(
     223          10 :         VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize));
     224             :     GByte *pabyMaskLine = hMaskBand != nullptr
     225          10 :                               ? static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize))
     226          10 :                               : nullptr;
     227          10 :     if (panLastLineVal == nullptr || panThisLineVal == nullptr ||
     228          10 :         panLastLineId == nullptr || panThisLineId == nullptr ||
     229          10 :         panThisLineWriteVal == nullptr ||
     230           4 :         (hMaskBand != nullptr && pabyMaskLine == nullptr))
     231             :     {
     232           0 :         CPLFree(panThisLineId);
     233           0 :         CPLFree(panLastLineId);
     234           0 :         CPLFree(panThisLineVal);
     235           0 :         CPLFree(panLastLineVal);
     236           0 :         CPLFree(panThisLineWriteVal);
     237           0 :         CPLFree(pabyMaskLine);
     238           0 :         return CE_Failure;
     239             :     }
     240             : 
     241             :     /* -------------------------------------------------------------------- */
     242             :     /*      The first pass over the raster is only used to build up the     */
     243             :     /*      polygon id map so we will know in advance what polygons are     */
     244             :     /*      what on the second pass.                                        */
     245             :     /* -------------------------------------------------------------------- */
     246          20 :     GDALRasterPolygonEnumerator oFirstEnum(nConnectedness);
     247          20 :     std::vector<int> anPolySizes;
     248             : 
     249          10 :     CPLErr eErr = CE_None;
     250         189 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
     251             :     {
     252         179 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     253             :                             nXSize, 1, GDT_Int64, 0, 0);
     254             : 
     255         179 :         if (eErr == CE_None && hMaskBand != nullptr)
     256          41 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     257             :                                    panThisLineVal);
     258         179 :         if (eErr != CE_None)
     259           0 :             break;
     260             : 
     261         179 :         if (iY == 0)
     262          20 :             eErr = oFirstEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     263             :                                           panThisLineId, nXSize)
     264          10 :                        ? CE_None
     265             :                        : CE_Failure;
     266             :         else
     267         338 :             eErr = oFirstEnum.ProcessLine(panLastLineVal, panThisLineVal,
     268             :                                           panLastLineId, panThisLineId, nXSize)
     269         169 :                        ? CE_None
     270             :                        : CE_Failure;
     271         179 :         if (eErr != CE_None)
     272           0 :             break;
     273             : 
     274             :         /* --------------------------------------------------------------------
     275             :          */
     276             :         /*      Accumulate polygon sizes. */
     277             :         /* --------------------------------------------------------------------
     278             :          */
     279         179 :         if (oFirstEnum.nNextPolygonId > static_cast<int>(anPolySizes.size()))
     280         169 :             anPolySizes.resize(oFirstEnum.nNextPolygonId);
     281             : 
     282       11438 :         for (int iX = 0; iX < nXSize; iX++)
     283             :         {
     284       11259 :             const int iPoly = panThisLineId[iX];
     285             : 
     286       11259 :             if (iPoly >= 0 && anPolySizes[iPoly] < MY_MAX_INT)
     287       11243 :                 anPolySizes[iPoly] += 1;
     288             :         }
     289             : 
     290             :         /* --------------------------------------------------------------------
     291             :          */
     292             :         /*      swap this/last lines. */
     293             :         /* --------------------------------------------------------------------
     294             :          */
     295         179 :         std::swap(panLastLineVal, panThisLineVal);
     296         179 :         std::swap(panLastLineId, panThisLineId);
     297             : 
     298             :         /* --------------------------------------------------------------------
     299             :          */
     300             :         /*      Report progress, and support interrupts. */
     301             :         /* --------------------------------------------------------------------
     302             :          */
     303         179 :         if (!pfnProgress(0.25 * ((iY + 1) / static_cast<double>(nYSize)), "",
     304             :                          pProgressArg))
     305             :         {
     306           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     307           0 :             eErr = CE_Failure;
     308             :         }
     309             :     }
     310             : 
     311             :     /* -------------------------------------------------------------------- */
     312             :     /*      Make a pass through the maps, ensuring every polygon id         */
     313             :     /*      points to the final id it should use, not an intermediate       */
     314             :     /*      value.                                                          */
     315             :     /* -------------------------------------------------------------------- */
     316          10 :     if (eErr == CE_None)
     317          10 :         oFirstEnum.CompleteMerges();
     318             : 
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      Push the sizes of merged polygon fragments into the             */
     321             :     /*      merged polygon id's count.                                      */
     322             :     /* -------------------------------------------------------------------- */
     323          10 :     assert(oFirstEnum.panPolyIdMap != nullptr);  // for Coverity
     324          10 :     assert(oFirstEnum.panPolyValue != nullptr);  // for Coverity
     325        7199 :     for (int iPoly = 0; iPoly < oFirstEnum.nNextPolygonId; iPoly++)
     326             :     {
     327        7189 :         if (oFirstEnum.panPolyIdMap[iPoly] != iPoly)
     328             :         {
     329        3488 :             GIntBig nSize = anPolySizes[oFirstEnum.panPolyIdMap[iPoly]];
     330             : 
     331        3488 :             nSize += anPolySizes[iPoly];
     332             : 
     333        3488 :             if (nSize > MY_MAX_INT)
     334           0 :                 nSize = MY_MAX_INT;
     335             : 
     336        3488 :             anPolySizes[oFirstEnum.panPolyIdMap[iPoly]] =
     337             :                 static_cast<int>(nSize);
     338        3488 :             anPolySizes[iPoly] = 0;
     339             :         }
     340             :     }
     341             : 
     342             :     /* -------------------------------------------------------------------- */
     343             :     /*      We will use a new enumerator for the second pass primarily      */
     344             :     /*      so we can preserve the first pass map.                          */
     345             :     /* -------------------------------------------------------------------- */
     346          20 :     GDALRasterPolygonEnumerator oSecondEnum(nConnectedness);
     347             : 
     348          10 :     std::vector<int> anBigNeighbour;
     349          10 :     anBigNeighbour.resize(anPolySizes.size());
     350             : 
     351        7199 :     for (int iPoly = 0; iPoly < static_cast<int>(anPolySizes.size()); iPoly++)
     352        7189 :         anBigNeighbour[iPoly] = -1;
     353             : 
     354             :     /* ==================================================================== */
     355             :     /*      Second pass ... identify the largest neighbour for each         */
     356             :     /*      polygon.                                                        */
     357             :     /* ==================================================================== */
     358         189 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
     359             :     {
     360             :         /* --------------------------------------------------------------------
     361             :          */
     362             :         /*      Read the image data. */
     363             :         /* --------------------------------------------------------------------
     364             :          */
     365         179 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     366             :                             nXSize, 1, GDT_Int64, 0, 0);
     367             : 
     368         179 :         if (eErr == CE_None && hMaskBand != nullptr)
     369          41 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     370             :                                    panThisLineVal);
     371             : 
     372         179 :         if (eErr != CE_None)
     373           0 :             continue;
     374             : 
     375             :         /* --------------------------------------------------------------------
     376             :          */
     377             :         /*      Determine what polygon the various pixels belong to (redoing */
     378             :         /*      the same thing done in the first pass above). */
     379             :         /* --------------------------------------------------------------------
     380             :          */
     381         179 :         if (iY == 0)
     382          20 :             eErr = oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     383             :                                            panThisLineId, nXSize)
     384          10 :                        ? CE_None
     385             :                        : CE_Failure;
     386             :         else
     387         338 :             eErr = oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
     388             :                                            panLastLineId, panThisLineId, nXSize)
     389         169 :                        ? CE_None
     390             :                        : CE_Failure;
     391             : 
     392         179 :         if (eErr != CE_None)
     393           0 :             continue;
     394             : 
     395             :         /* --------------------------------------------------------------------
     396             :          */
     397             :         /*      Check our neighbours, and update our biggest neighbour map */
     398             :         /*      as appropriate. */
     399             :         /* --------------------------------------------------------------------
     400             :          */
     401       11438 :         for (int iX = 0; iX < nXSize; iX++)
     402             :         {
     403       11259 :             if (iY > 0)
     404             :             {
     405       11087 :                 CompareNeighbour(panThisLineId[iX], panLastLineId[iX],
     406             :                                  oFirstEnum.panPolyIdMap,
     407             :                                  oFirstEnum.panPolyValue, anPolySizes,
     408             :                                  anBigNeighbour);
     409             : 
     410       11087 :                 if (iX > 0 && nConnectedness == 8)
     411          48 :                     CompareNeighbour(panThisLineId[iX], panLastLineId[iX - 1],
     412             :                                      oFirstEnum.panPolyIdMap,
     413             :                                      oFirstEnum.panPolyValue, anPolySizes,
     414             :                                      anBigNeighbour);
     415             : 
     416       11087 :                 if (iX < nXSize - 1 && nConnectedness == 8)
     417          48 :                     CompareNeighbour(panThisLineId[iX], panLastLineId[iX + 1],
     418             :                                      oFirstEnum.panPolyIdMap,
     419             :                                      oFirstEnum.panPolyValue, anPolySizes,
     420             :                                      anBigNeighbour);
     421             :             }
     422             : 
     423       11259 :             if (iX > 0)
     424       11080 :                 CompareNeighbour(panThisLineId[iX], panThisLineId[iX - 1],
     425             :                                  oFirstEnum.panPolyIdMap,
     426             :                                  oFirstEnum.panPolyValue, anPolySizes,
     427             :                                  anBigNeighbour);
     428             : 
     429             :             // We don't need to compare to next pixel or next line
     430             :             // since they will be compared to us.
     431             :         }
     432             : 
     433             :         /* --------------------------------------------------------------------
     434             :          */
     435             :         /*      Swap pixel value, and polygon id lines to be ready for the */
     436             :         /*      next line. */
     437             :         /* --------------------------------------------------------------------
     438             :          */
     439         179 :         std::swap(panLastLineVal, panThisLineVal);
     440         179 :         std::swap(panLastLineId, panThisLineId);
     441             : 
     442             :         /* --------------------------------------------------------------------
     443             :          */
     444             :         /*      Report progress, and support interrupts. */
     445             :         /* --------------------------------------------------------------------
     446             :          */
     447         179 :         if (!pfnProgress(0.25 + 0.25 * ((iY + 1) / static_cast<double>(nYSize)),
     448             :                          "", pProgressArg))
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     451           0 :             eErr = CE_Failure;
     452             :         }
     453             :     }
     454             : 
     455             :     /* -------------------------------------------------------------------- */
     456             :     /*      If our biggest neighbour is still smaller than the              */
     457             :     /*      threshold, then try tracking to that polygons biggest           */
     458             :     /*      neighbour, and so forth.                                        */
     459             :     /* -------------------------------------------------------------------- */
     460          10 :     int nFailedMerges = 0;
     461          10 :     int nIsolatedSmall = 0;
     462          10 :     int nSieveTargets = 0;
     463             : 
     464        7199 :     for (int iPoly = 0; iPoly < static_cast<int>(anPolySizes.size()); iPoly++)
     465             :     {
     466        7189 :         if (oFirstEnum.panPolyIdMap[iPoly] != iPoly)
     467        3817 :             continue;
     468             : 
     469             :         // Ignore nodata polygons.
     470        3701 :         if (oFirstEnum.panPolyValue[iPoly] == GP_NODATA_MARKER)
     471           0 :             continue;
     472             : 
     473             :         // Don't try to merge polygons larger than the threshold.
     474        3701 :         if (anPolySizes[iPoly] >= nSizeThreshold)
     475             :         {
     476         309 :             anBigNeighbour[iPoly] = -1;
     477         309 :             continue;
     478             :         }
     479             : 
     480        3392 :         nSieveTargets++;
     481             : 
     482             :         // if we have no neighbours but we are small, what shall we do?
     483        3392 :         if (anBigNeighbour[iPoly] == -1)
     484             :         {
     485           0 :             nIsolatedSmall++;
     486           0 :             continue;
     487             :         }
     488             : 
     489        3392 :         std::set<int> oSetVisitedPoly;
     490        3392 :         oSetVisitedPoly.insert(iPoly);
     491             : 
     492             :         // Walk through our neighbours until we find a polygon large enough.
     493        3392 :         int iFinalId = iPoly;
     494        3392 :         bool bFoundBigEnoughPoly = false;
     495             :         while (true)
     496             :         {
     497        3420 :             iFinalId = anBigNeighbour[iFinalId];
     498        3420 :             if (iFinalId < 0)
     499             :             {
     500          19 :                 break;
     501             :             }
     502             :             // If the biggest neighbour is larger than the threshold
     503             :             // then we are golden.
     504        3401 :             if (anPolySizes[iFinalId] >= nSizeThreshold)
     505             :             {
     506        3372 :                 bFoundBigEnoughPoly = true;
     507        3372 :                 break;
     508             :             }
     509             :             // Check that we don't cycle on an already visited polygon.
     510          29 :             if (oSetVisitedPoly.find(iFinalId) != oSetVisitedPoly.end())
     511           1 :                 break;
     512          28 :             oSetVisitedPoly.insert(iFinalId);
     513             :         }
     514             : 
     515        3392 :         if (!bFoundBigEnoughPoly)
     516             :         {
     517          20 :             nFailedMerges++;
     518          20 :             anBigNeighbour[iPoly] = -1;
     519          20 :             continue;
     520             :         }
     521             : 
     522             :         // Map the whole intermediate chain to it.
     523        3372 :         int iPolyCur = iPoly;
     524        3380 :         while (anBigNeighbour[iPolyCur] != iFinalId)
     525             :         {
     526           8 :             int iNextPoly = anBigNeighbour[iPolyCur];
     527           8 :             anBigNeighbour[iPolyCur] = iFinalId;
     528           8 :             iPolyCur = iNextPoly;
     529             :         }
     530             :     }
     531             : 
     532          10 :     CPLDebug("GDALSieveFilter",
     533             :              "Small Polygons: %d, Isolated: %d, Unmergable: %d", nSieveTargets,
     534             :              nIsolatedSmall, nFailedMerges);
     535             : 
     536             :     /* ==================================================================== */
     537             :     /*      Make a third pass over the image, actually applying the         */
     538             :     /*      merges.  We reuse the second enumerator but preserve the        */
     539             :     /*      "final maps" from the first.                                    */
     540             :     /* ==================================================================== */
     541          10 :     oSecondEnum.Clear();
     542             : 
     543         378 :     for (int iY = 0; oFirstEnum.panPolyIdMap != nullptr &&  // for Coverity
     544         378 :                      eErr == CE_None && iY < nYSize;
     545             :          iY++)
     546             :     {
     547             :         /* --------------------------------------------------------------------
     548             :          */
     549             :         /*      Read the image data. */
     550             :         /* --------------------------------------------------------------------
     551             :          */
     552         179 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     553             :                             nXSize, 1, GDT_Int64, 0, 0);
     554             : 
     555         179 :         memcpy(panThisLineWriteVal, panThisLineVal,
     556         179 :                sizeof(panThisLineVal[0]) * nXSize);
     557             : 
     558         179 :         if (eErr == CE_None && hMaskBand != nullptr)
     559          41 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     560             :                                    panThisLineVal);
     561             : 
     562         179 :         if (eErr != CE_None)
     563           0 :             continue;
     564             : 
     565             :         /* --------------------------------------------------------------------
     566             :          */
     567             :         /*      Determine what polygon the various pixels belong to (redoing */
     568             :         /*      the same thing done in the first pass above). */
     569             :         /* --------------------------------------------------------------------
     570             :          */
     571         179 :         if (iY == 0)
     572          10 :             oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     573             :                                     panThisLineId, nXSize);
     574             :         else
     575         169 :             oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
     576             :                                     panLastLineId, panThisLineId, nXSize);
     577             : 
     578             :         /* --------------------------------------------------------------------
     579             :          */
     580             :         /*      Reprocess the actual pixel values according to the polygon */
     581             :         /*      merging, and write out this line of image data. */
     582             :         /* --------------------------------------------------------------------
     583             :          */
     584       11438 :         for (int iX = 0; iX < nXSize; iX++)
     585             :         {
     586       11259 :             int iThisPoly = panThisLineId[iX];
     587       11259 :             if (iThisPoly >= 0)
     588             :             {
     589       11243 :                 iThisPoly = oFirstEnum.panPolyIdMap[iThisPoly];
     590             : 
     591       11243 :                 if (anBigNeighbour[iThisPoly] != -1)
     592             :                 {
     593        3377 :                     panThisLineWriteVal[iX] =
     594        3377 :                         oFirstEnum.panPolyValue[anBigNeighbour[iThisPoly]];
     595             :                 }
     596             :             }
     597             :         }
     598             : 
     599             :         /* --------------------------------------------------------------------
     600             :          */
     601             :         /*      Write the update data out. */
     602             :         /* --------------------------------------------------------------------
     603             :          */
     604         179 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, iY, nXSize, 1,
     605             :                             panThisLineWriteVal, nXSize, 1, GDT_Int64, 0, 0);
     606             : 
     607             :         /* --------------------------------------------------------------------
     608             :          */
     609             :         /*      Swap pixel value, and polygon id lines to be ready for the */
     610             :         /*      next line. */
     611             :         /* --------------------------------------------------------------------
     612             :          */
     613         179 :         std::swap(panLastLineVal, panThisLineVal);
     614         179 :         std::swap(panLastLineId, panThisLineId);
     615             : 
     616             :         /* --------------------------------------------------------------------
     617             :          */
     618             :         /*      Report progress, and support interrupts. */
     619             :         /* --------------------------------------------------------------------
     620             :          */
     621         358 :         if (eErr == CE_None &&
     622         179 :             !pfnProgress(0.5 + 0.5 * ((iY + 1) / static_cast<double>(nYSize)),
     623             :                          "", pProgressArg))
     624             :         {
     625           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     626           0 :             eErr = CE_Failure;
     627             :         }
     628             :     }
     629             : 
     630             :     /* -------------------------------------------------------------------- */
     631             :     /*      Cleanup                                                         */
     632             :     /* -------------------------------------------------------------------- */
     633          10 :     CPLFree(panThisLineId);
     634          10 :     CPLFree(panLastLineId);
     635          10 :     CPLFree(panThisLineVal);
     636          10 :     CPLFree(panLastLineVal);
     637          10 :     CPLFree(panThisLineWriteVal);
     638          10 :     CPLFree(pabyMaskLine);
     639             : 
     640          10 :     return eErr;
     641             : }

Generated by: LCOV version 1.14