ultimatepp/bazaar/plugin/gdal/alg/rasterfill.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

886 lines
34 KiB
C++

/******************************************************************************
* $Id: rasterfill.cpp 28342 2015-01-22 12:31:07Z rouault $
*
* Project: GDAL
* Purpose: Interpolate in nodata areas.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2008, Frank Warmerdam
* Copyright (c) 2015, Sean Gillies <sean@mapbox.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
***************************************************************************/
#include "gdal_alg.h"
#include "cpl_conv.h"
#include "cpl_string.h"
CPL_CVSID("$Id: rasterfill.cpp 28342 2015-01-22 12:31:07Z rouault $");
/************************************************************************/
/* GDALFilterLine() */
/* */
/* Apply 3x3 filtering one one scanline with masking for which */
/* pixels are to be interpolated (ThisFMask) and which window */
/* pixels are valid to include in the interpolation (TMask). */
/************************************************************************/
static void
GDALFilterLine( float *pafLastLine, float *pafThisLine, float *pafNextLine,
float *pafOutLine,
GByte *pabyLastTMask, GByte *pabyThisTMask, GByte*pabyNextTMask,
GByte *pabyThisFMask, int nXSize )
{
int iX;
for( iX = 0; iX < nXSize; iX++ )
{
if( !pabyThisFMask[iX] )
{
pafOutLine[iX] = pafThisLine[iX];
continue;
}
CPLAssert( pabyThisTMask[iX] );
double dfValSum = 0.0;
double dfWeightSum = 0.0;
// Previous line
if( pafLastLine != NULL )
{
if( iX > 0 && pabyLastTMask[iX-1] )
{
dfValSum += pafLastLine[iX-1];
dfWeightSum += 1.0;
}
if( pabyLastTMask[iX] )
{
dfValSum += pafLastLine[iX];
dfWeightSum += 1.0;
}
if( iX < nXSize-1 && pabyLastTMask[iX+1] )
{
dfValSum += pafLastLine[iX+1];
dfWeightSum += 1.0;
}
}
// Current Line
if( iX > 0 && pabyThisTMask[iX-1] )
{
dfValSum += pafThisLine[iX-1];
dfWeightSum += 1.0;
}
if( pabyThisTMask[iX] )
{
dfValSum += pafThisLine[iX];
dfWeightSum += 1.0;
}
if( iX < nXSize-1 && pabyThisTMask[iX+1] )
{
dfValSum += pafThisLine[iX+1];
dfWeightSum += 1.0;
}
// Next line
if( pafNextLine != NULL )
{
if( iX > 0 && pabyNextTMask[iX-1] )
{
dfValSum += pafNextLine[iX-1];
dfWeightSum += 1.0;
}
if( pabyNextTMask[iX] )
{
dfValSum += pafNextLine[iX];
dfWeightSum += 1.0;
}
if( iX < nXSize-1 && pabyNextTMask[iX+1] )
{
dfValSum += pafNextLine[iX+1];
dfWeightSum += 1.0;
}
}
pafOutLine[iX] = (float) (dfValSum / dfWeightSum);
}
}
/************************************************************************/
/* GDALMultiFilter() */
/* */
/* Apply multiple iterations of a 3x3 smoothing filter over a */
/* band with masking controlling what pixels should be */
/* filtered (FiltMaskBand non zero) and which pixels can be */
/* considered valid contributors to the filter */
/* (TargetMaskBand non zero). */
/* */
/* This implementation attempts to apply many iterations in */
/* one IO pass by managing the filtering over a rolling buffer */
/* of nIternations+2 scanlines. While possibly clever this */
/* makes the algorithm implementation largely */
/* incomprehensible. */
/************************************************************************/
static CPLErr
GDALMultiFilter( GDALRasterBandH hTargetBand,
GDALRasterBandH hTargetMaskBand,
GDALRasterBandH hFiltMaskBand,
int nIterations,
GDALProgressFunc pfnProgress,
void * pProgressArg )
{
float *paf3PassLineBuf;
GByte *pabyTMaskBuf;
GByte *pabyFMaskBuf;
float *pafThisPass, *pafLastPass, *pafSLastPass;
int nBufLines = nIterations + 2;
int iPassCounter = 0;
int nNewLine; // the line being loaded this time (zero based scanline)
int nXSize = GDALGetRasterBandXSize( hTargetBand );
int nYSize = GDALGetRasterBandYSize( hTargetBand );
CPLErr eErr = CE_None;
/* -------------------------------------------------------------------- */
/* Report starting progress value. */
/* -------------------------------------------------------------------- */
if( !pfnProgress( 0.0, "Smoothing Filter...", pProgressArg ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Allocate rotating buffers. */
/* -------------------------------------------------------------------- */
pabyTMaskBuf = (GByte *) VSIMalloc2(nXSize, nBufLines);
pabyFMaskBuf = (GByte *) VSIMalloc2(nXSize, nBufLines);
paf3PassLineBuf = (float *) VSIMalloc3(nXSize, nBufLines, 3 * sizeof(float));
if (pabyTMaskBuf == NULL || pabyFMaskBuf == NULL || paf3PassLineBuf == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory,
"Could not allocate enough memory for temporary buffers");
eErr = CE_Failure;
goto end;
}
/* -------------------------------------------------------------------- */
/* Process rotating buffers. */
/* -------------------------------------------------------------------- */
for( nNewLine = 0;
eErr == CE_None && nNewLine < nYSize+nIterations;
nNewLine++ )
{
/* -------------------------------------------------------------------- */
/* Rotate pass buffers. */
/* -------------------------------------------------------------------- */
iPassCounter = (iPassCounter + 1) % 3;
pafSLastPass = paf3PassLineBuf
+ ((iPassCounter+0)%3) * nXSize*nBufLines;
pafLastPass = paf3PassLineBuf
+ ((iPassCounter+1)%3) * nXSize*nBufLines;
pafThisPass = paf3PassLineBuf
+ ((iPassCounter+2)%3) * nXSize*nBufLines;
/* -------------------------------------------------------------------- */
/* Where does the new line go in the rotating buffer? */
/* -------------------------------------------------------------------- */
int iBufOffset = nNewLine % nBufLines;
/* -------------------------------------------------------------------- */
/* Read the new data line if it is't off the bottom of the */
/* image. */
/* -------------------------------------------------------------------- */
if( nNewLine < nYSize )
{
eErr =
GDALRasterIO( hTargetMaskBand, GF_Read,
0, nNewLine, nXSize, 1,
pabyTMaskBuf + nXSize * iBufOffset, nXSize, 1,
GDT_Byte, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hFiltMaskBand, GF_Read,
0, nNewLine, nXSize, 1,
pabyFMaskBuf + nXSize * iBufOffset, nXSize, 1,
GDT_Byte, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hTargetBand, GF_Read,
0, nNewLine, nXSize, 1,
pafThisPass + nXSize * iBufOffset, nXSize, 1,
GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
}
/* -------------------------------------------------------------------- */
/* Loop over the loaded data, applying the filter to all loaded */
/* lines with neighbours. */
/* -------------------------------------------------------------------- */
int iFLine;
for( iFLine = nNewLine-1;
eErr == CE_None && iFLine >= nNewLine-nIterations;
iFLine-- )
{
int iLastOffset, iThisOffset, iNextOffset;
iLastOffset = (iFLine-1) % nBufLines;
iThisOffset = (iFLine ) % nBufLines;
iNextOffset = (iFLine+1) % nBufLines;
// default to preserving the old value.
if( iFLine >= 0 )
memcpy( pafThisPass + iThisOffset * nXSize,
pafLastPass + iThisOffset * nXSize,
sizeof(float) * nXSize );
// currently this skips the first and last line. Eventually
// we will enable these too. TODO
if( iFLine < 1 || iFLine >= nYSize-1 )
{
continue;
}
GDALFilterLine(
pafSLastPass + iLastOffset * nXSize,
pafLastPass + iThisOffset * nXSize,
pafThisPass + iNextOffset * nXSize,
pafThisPass + iThisOffset * nXSize,
pabyTMaskBuf + iLastOffset * nXSize,
pabyTMaskBuf + iThisOffset * nXSize,
pabyTMaskBuf + iNextOffset * nXSize,
pabyFMaskBuf + iThisOffset * nXSize,
nXSize );
}
/* -------------------------------------------------------------------- */
/* Write out the top data line that will be rolling out of our */
/* buffer. */
/* -------------------------------------------------------------------- */
int iLineToSave = nNewLine - nIterations;
if( iLineToSave >= 0 && eErr == CE_None )
{
iBufOffset = iLineToSave % nBufLines;
eErr =
GDALRasterIO( hTargetBand, GF_Write,
0, iLineToSave, nXSize, 1,
pafThisPass + nXSize * iBufOffset, nXSize, 1,
GDT_Float32, 0, 0 );
}
/* -------------------------------------------------------------------- */
/* Report progress. */
/* -------------------------------------------------------------------- */
if( eErr == CE_None
&& !pfnProgress( (nNewLine+1) / (double) (nYSize+nIterations),
"Smoothing Filter...", pProgressArg ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
eErr = CE_Failure;
}
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
end:
CPLFree( pabyTMaskBuf );
CPLFree( pabyFMaskBuf );
CPLFree( paf3PassLineBuf );
return eErr;
}
/************************************************************************/
/* QUAD_CHECK() */
/* */
/* macro for checking whether a point is nearer than the */
/* existing closest point. */
/************************************************************************/
#define QUAD_CHECK(quad_dist, quad_value, \
target_x, target_y, origin_x, origin_y, target_value ) \
\
if( quad_value != nNoDataVal ) \
{ \
double dfDx = (double)target_x - (double)origin_x; \
double dfDy = (double)target_y - (double)origin_y; \
double dfDistSq = dfDx * dfDx + dfDy * dfDy; \
\
if( dfDistSq < quad_dist*quad_dist ) \
{ \
CPLAssert( dfDistSq > 0.0 ); \
quad_dist = sqrt(dfDistSq); \
quad_value = target_value; \
} \
}
/************************************************************************/
/* GDALFillNodata() */
/************************************************************************/
/**
* Fill selected raster regions by interpolation from the edges.
*
* This algorithm will interpolate values for all designated
* nodata pixels (marked by zeros in hMaskBand). For each pixel
* a four direction conic search is done to find values to interpolate
* from (using inverse distance weighting). Once all values are
* interpolated, zero or more smoothing iterations (3x3 average
* filters on interpolated pixels) are applied to smooth out
* artifacts.
*
* This algorithm is generally suitable for interpolating missing
* regions of fairly continuously varying rasters (such as elevation
* models for instance). It is also suitable for filling small holes
* and cracks in more irregularly varying images (like airphotos). It
* is generally not so great for interpolating a raster from sparse
* point data - see the algorithms defined in gdal_grid.h for that case.
*
* @param hTargetBand the raster band to be modified in place.
* @param hMaskBand a mask band indicating pixels to be interpolated (zero valued
* @param dfMaxSearchDist the maximum number of pixels to search in all
* directions to find values to interpolate from.
* @param bDeprecatedOption unused argument, should be zero.
* @param nSmoothingIterations the number of 3x3 smoothing filter passes to
* run (0 or more).
* @param papszOptions additional name=value options in a string list (the
* temporary file driver can be specified like TEMP_FILE_DRIVER=MEM).
* @param pfnProgress the progress function to report completion.
* @param pProgressArg callback data for progress function.
*
* @return CE_None on success or CE_Failure if something goes wrong.
*/
CPLErr CPL_STDCALL
GDALFillNodata( GDALRasterBandH hTargetBand,
GDALRasterBandH hMaskBand,
double dfMaxSearchDist,
int bDeprecatedOption,
int nSmoothingIterations,
char **papszOptions,
GDALProgressFunc pfnProgress,
void * pProgressArg )
{
VALIDATE_POINTER1( hTargetBand, "GDALFillNodata", CE_Failure );
int nXSize = GDALGetRasterBandXSize( hTargetBand );
int nYSize = GDALGetRasterBandYSize( hTargetBand );
CPLErr eErr = CE_None;
// Special "x" pixel values identifying pixels as special.
GUInt32 nNoDataVal;
GDALDataType eType;
if( dfMaxSearchDist == 0.0 )
dfMaxSearchDist = MAX(nXSize,nYSize) + 1;
int nMaxSearchDist = (int) floor(dfMaxSearchDist);
if( nXSize > 65533 || nYSize > 65533 )
{
eType = GDT_UInt32;
nNoDataVal = 4000002;
}
else
{
eType = GDT_UInt16;
nNoDataVal = 65535;
}
if( hMaskBand == NULL )
hMaskBand = GDALGetMaskBand( hTargetBand );
/* If there are smoothing iterations, reserve 10% of the progress for them */
double dfProgressRatio = (nSmoothingIterations > 0) ? 0.9 : 1.0;
/* -------------------------------------------------------------------- */
/* Initialize progress counter. */
/* -------------------------------------------------------------------- */
if( pfnProgress == NULL )
pfnProgress = GDALDummyProgress;
if( !pfnProgress( 0.0, "Filling...", pProgressArg ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Determine format driver for temp work files. */
/* -------------------------------------------------------------------- */
CPLString osTmpFileDriver = CSLFetchNameValueDef(
papszOptions, "TEMP_FILE_DRIVER", "GTiff");
GDALDriverH hDriver = GDALGetDriverByName((const char *) osTmpFileDriver);
if (hDriver == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Given driver is not registered");
return CE_Failure;
}
if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, NULL) == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Given driver is incapable of creating temp work files");
return CE_Failure;
}
char **papszWorkFileOptions = NULL;
if (osTmpFileDriver == "GTiff") {
papszWorkFileOptions = CSLSetNameValue(
papszWorkFileOptions, "COMPRESS", "LZW");
papszWorkFileOptions = CSLSetNameValue(
papszWorkFileOptions, "BIGTIFF", "IF_SAFER");
}
/* -------------------------------------------------------------------- */
/* Create a work file to hold the Y "last value" indices. */
/* -------------------------------------------------------------------- */
GDALDatasetH hYDS;
GDALRasterBandH hYBand;
CPLString osTmpFile = CPLGenerateTempFilename("");
CPLString osYTmpFile = osTmpFile + "fill_y_work.tif";
hYDS = GDALCreate( hDriver, osYTmpFile, nXSize, nYSize, 1,
eType, (char **) papszWorkFileOptions );
if ( hYDS == NULL )
{
CPLError(CE_Failure, CPLE_AppDefined,
"Could not create Y index work file. Check driver capabilities.");
return CE_Failure;
}
hYBand = GDALGetRasterBand( hYDS, 1 );
/* -------------------------------------------------------------------- */
/* Create a work file to hold the pixel value associated with */
/* the "last xy value" pixel. */
/* -------------------------------------------------------------------- */
GDALDatasetH hValDS;
GDALRasterBandH hValBand;
CPLString osValTmpFile = osTmpFile + "fill_val_work.tif";
hValDS = GDALCreate( hDriver, osValTmpFile, nXSize, nYSize, 1,
GDALGetRasterDataType( hTargetBand ),
(char **) papszWorkFileOptions );
if ( hValDS == NULL )
{
CPLError(CE_Failure, CPLE_AppDefined,
"Could not create XY value work file. Check driver capabilities.");
return CE_Failure;
}
hValBand = GDALGetRasterBand( hValDS, 1 );
/* -------------------------------------------------------------------- */
/* Create a mask file to make it clear what pixels can be filtered */
/* on the filtering pass. */
/* -------------------------------------------------------------------- */
GDALDatasetH hFiltMaskDS;
GDALRasterBandH hFiltMaskBand;
CPLString osFiltMaskTmpFile = osTmpFile + "fill_filtmask_work.tif";
hFiltMaskDS =
GDALCreate( hDriver, osFiltMaskTmpFile, nXSize, nYSize, 1,
GDT_Byte, (char **) papszWorkFileOptions );
if ( hFiltMaskDS == NULL )
{
CPLError(CE_Failure, CPLE_AppDefined,
"Could not create mask work file. Check driver capabilities.");
return CE_Failure;
}
hFiltMaskBand = GDALGetRasterBand( hFiltMaskDS, 1 );
/* -------------------------------------------------------------------- */
/* Allocate buffers for last scanline and this scanline. */
/* -------------------------------------------------------------------- */
GUInt32 *panLastY, *panThisY, *panTopDownY;
float *pafLastValue, *pafThisValue, *pafScanline, *pafTopDownValue;
GByte *pabyMask, *pabyFiltMask;
int iX;
int iY;
panLastY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32));
panThisY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32));
panTopDownY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32));
pafLastValue = (float *) VSICalloc(nXSize,sizeof(float));
pafThisValue = (float *) VSICalloc(nXSize,sizeof(float));
pafTopDownValue = (float *) VSICalloc(nXSize,sizeof(float));
pafScanline = (float *) VSICalloc(nXSize,sizeof(float));
pabyMask = (GByte *) VSICalloc(nXSize,1);
pabyFiltMask = (GByte *) VSICalloc(nXSize,1);
if (panLastY == NULL || panThisY == NULL || panTopDownY == NULL ||
pafLastValue == NULL || pafThisValue == NULL || pafTopDownValue == NULL ||
pafScanline == NULL || pabyMask == NULL || pabyFiltMask == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory,
"Could not allocate enough memory for temporary buffers");
eErr = CE_Failure;
goto end;
}
for( iX = 0; iX < nXSize; iX++ )
{
panLastY[iX] = nNoDataVal;
}
/* ==================================================================== */
/* Make first pass from top to bottom collecting the "last */
/* known value" for each column and writing it out to the work */
/* files. */
/* ==================================================================== */
for( iY = 0; iY < nYSize && eErr == CE_None; iY++ )
{
/* -------------------------------------------------------------------- */
/* Read data and mask for this line. */
/* -------------------------------------------------------------------- */
eErr =
GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1,
pabyMask, nXSize, 1, GDT_Byte, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hTargetBand, GF_Read, 0, iY, nXSize, 1,
pafScanline, nXSize, 1, GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
/* -------------------------------------------------------------------- */
/* Figure out the most recent pixel for each column. */
/* -------------------------------------------------------------------- */
for( iX = 0; iX < nXSize; iX++ )
{
if( pabyMask[iX] )
{
pafThisValue[iX] = pafScanline[iX];
panThisY[iX] = iY;
}
else if( iY <= dfMaxSearchDist + panLastY[iX] )
{
pafThisValue[iX] = pafLastValue[iX];
panThisY[iX] = panLastY[iX];
}
else
{
panThisY[iX] = nNoDataVal;
}
}
/* -------------------------------------------------------------------- */
/* Write out best index/value to working files. */
/* -------------------------------------------------------------------- */
eErr = GDALRasterIO( hYBand, GF_Write, 0, iY, nXSize, 1,
panThisY, nXSize, 1, GDT_UInt32, 0, 0 );
if( eErr != CE_None )
break;
eErr = GDALRasterIO( hValBand, GF_Write, 0, iY, nXSize, 1,
pafThisValue, nXSize, 1, GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
/* -------------------------------------------------------------------- */
/* Flip this/last buffers. */
/* -------------------------------------------------------------------- */
{
float *pafTmp = pafThisValue;
pafThisValue = pafLastValue;
pafLastValue = pafTmp;
GUInt32 *panTmp = panThisY;
panThisY = panLastY;
panLastY = panTmp;
}
/* -------------------------------------------------------------------- */
/* report progress. */
/* -------------------------------------------------------------------- */
if( eErr == CE_None
&& !pfnProgress( dfProgressRatio * (0.5*(iY+1) / (double)nYSize),
"Filling...", pProgressArg ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
eErr = CE_Failure;
}
}
/* ==================================================================== */
/* Now we will do collect similar this/last information from */
/* bottom to top and use it in combination with the top to */
/* bottom search info to interpolate. */
/* ==================================================================== */
for( iY = nYSize-1; iY >= 0 && eErr == CE_None; iY-- )
{
eErr =
GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1,
pabyMask, nXSize, 1, GDT_Byte, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hTargetBand, GF_Read, 0, iY, nXSize, 1,
pafScanline, nXSize, 1, GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
/* -------------------------------------------------------------------- */
/* Figure out the most recent pixel for each column. */
/* -------------------------------------------------------------------- */
for( iX = 0; iX < nXSize; iX++ )
{
if( pabyMask[iX] )
{
pafThisValue[iX] = pafScanline[iX];
panThisY[iX] = iY;
}
else if( panLastY[iX] - iY <= dfMaxSearchDist )
{
pafThisValue[iX] = pafLastValue[iX];
panThisY[iX] = panLastY[iX];
}
else
{
panThisY[iX] = nNoDataVal;
}
}
/* -------------------------------------------------------------------- */
/* Load the last y and corresponding value from the top down pass. */
/* -------------------------------------------------------------------- */
eErr =
GDALRasterIO( hYBand, GF_Read, 0, iY, nXSize, 1,
panTopDownY, nXSize, 1, GDT_UInt32, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hValBand, GF_Read, 0, iY, nXSize, 1,
pafTopDownValue, nXSize, 1, GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
/* -------------------------------------------------------------------- */
/* Attempt to interpolate any pixels that are nodata. */
/* -------------------------------------------------------------------- */
memset( pabyFiltMask, 0, nXSize );
for( iX = 0; iX < nXSize; iX++ )
{
int iStep, iQuad;
int nThisMaxSearchDist = nMaxSearchDist;
// If this was a valid target - no change.
if( pabyMask[iX] )
continue;
// Quadrants 0:topleft, 1:bottomleft, 2:topright, 3:bottomright
double adfQuadDist[4];
double adfQuadValue[4];
for( iQuad = 0; iQuad < 4; iQuad++ )
{
adfQuadDist[iQuad] = dfMaxSearchDist + 1.0;
adfQuadValue[iQuad] = 0.0;
}
// Step left and right by one pixel searching for the closest
// target value for each quadrant.
for( iStep = 0; iStep < nThisMaxSearchDist; iStep++ )
{
int iLeftX = MAX(0,iX - iStep);
int iRightX = MIN(nXSize-1,iX + iStep);
// top left includes current line
QUAD_CHECK(adfQuadDist[0],adfQuadValue[0],
iLeftX, panTopDownY[iLeftX], iX, iY,
pafTopDownValue[iLeftX] );
// bottom left
QUAD_CHECK(adfQuadDist[1],adfQuadValue[1],
iLeftX, panLastY[iLeftX], iX, iY,
pafLastValue[iLeftX] );
// top right and bottom right do no include center pixel.
if( iStep == 0 )
continue;
// top right includes current line
QUAD_CHECK(adfQuadDist[2],adfQuadValue[2],
iRightX, panTopDownY[iRightX], iX, iY,
pafTopDownValue[iRightX] );
// bottom right
QUAD_CHECK(adfQuadDist[3],adfQuadValue[3],
iRightX, panLastY[iRightX], iX, iY,
pafLastValue[iRightX] );
// every four steps, recompute maximum distance.
if( (iStep & 0x3) == 0 )
nThisMaxSearchDist = (int) floor(
MAX(MAX(adfQuadDist[0],adfQuadDist[1]),
MAX(adfQuadDist[2],adfQuadDist[3])) );
}
double dfWeightSum = 0.0;
double dfValueSum = 0.0;
for( iQuad = 0; iQuad < 4; iQuad++ )
{
if( adfQuadDist[iQuad] <= dfMaxSearchDist )
{
double dfWeight = 1.0 / adfQuadDist[iQuad];
dfWeightSum += dfWeight;
dfValueSum += adfQuadValue[iQuad] * dfWeight;
}
}
if( dfWeightSum > 0.0 )
{
pabyMask[iX] = 255;
pabyFiltMask[iX] = 255;
pafScanline[iX] = (float) (dfValueSum / dfWeightSum);
}
}
/* -------------------------------------------------------------------- */
/* Write out the updated data and mask information. */
/* -------------------------------------------------------------------- */
eErr =
GDALRasterIO( hTargetBand, GF_Write, 0, iY, nXSize, 1,
pafScanline, nXSize, 1, GDT_Float32, 0, 0 );
if( eErr != CE_None )
break;
eErr =
GDALRasterIO( hFiltMaskBand, GF_Write, 0, iY, nXSize, 1,
pabyFiltMask, nXSize, 1, GDT_Byte, 0, 0 );
if( eErr != CE_None )
break;
/* -------------------------------------------------------------------- */
/* Flip this/last buffers. */
/* -------------------------------------------------------------------- */
{
float *pafTmp = pafThisValue;
pafThisValue = pafLastValue;
pafLastValue = pafTmp;
GUInt32 *panTmp = panThisY;
panThisY = panLastY;
panLastY = panTmp;
}
/* -------------------------------------------------------------------- */
/* report progress. */
/* -------------------------------------------------------------------- */
if( eErr == CE_None
&& !pfnProgress( dfProgressRatio*(0.5+0.5*(nYSize-iY) / (double)nYSize),
"Filling...", pProgressArg ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
eErr = CE_Failure;
}
}
/* ==================================================================== */
/* Now we will do iterative average filters over the */
/* interpolated values to smooth things out and make linear */
/* artifacts less obvious. */
/* ==================================================================== */
if( eErr == CE_None && nSmoothingIterations > 0 )
{
// force masks to be to flushed and recomputed.
GDALFlushRasterCache( hMaskBand );
void *pScaledProgress;
pScaledProgress =
GDALCreateScaledProgress( dfProgressRatio, 1.0, pfnProgress, NULL );
eErr = GDALMultiFilter( hTargetBand, hMaskBand, hFiltMaskBand,
nSmoothingIterations,
GDALScaledProgress, pScaledProgress );
GDALDestroyScaledProgress( pScaledProgress );
}
/* -------------------------------------------------------------------- */
/* Close and clean up temporary files. Free working buffers */
/* -------------------------------------------------------------------- */
end:
CPLFree(panLastY);
CPLFree(panThisY);
CPLFree(panTopDownY);
CPLFree(pafLastValue);
CPLFree(pafThisValue);
CPLFree(pafTopDownValue);
CPLFree(pafScanline);
CPLFree(pabyMask);
CPLFree(pabyFiltMask);
GDALClose( hYDS );
GDALClose( hValDS );
GDALClose( hFiltMaskDS );
CSLDestroy(papszWorkFileOptions);
GDALDeleteDataset( hDriver, osYTmpFile );
GDALDeleteDataset( hDriver, osValTmpFile );
GDALDeleteDataset( hDriver, osFiltMaskTmpFile );
return eErr;
}