ultimatepp/bazaar/plugin/geotiff/libgeotiff/gcore/overview.cpp
cxl b67920c1ca bazaar: plugin/geotiff
git-svn-id: svn://ultimatepp.org/upp/trunk@7587 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2014-08-12 18:56:26 +00:00

1646 lines
65 KiB
C++

/******************************************************************************
* $Id: overview.cpp 15729 2008-11-13 20:33:46Z warmerdam $
*
* Project: GDAL Core
* Purpose: Helper code to implement overview support in different drivers.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2000, Frank Warmerdam
*
* 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_priv.h"
CPL_CVSID("$Id: overview.cpp 15729 2008-11-13 20:33:46Z warmerdam $");
typedef enum
{
GRM_Near = 0,
GRM_Average = 1,
GRM_Gauss = 2,
GRM_Mode = 3,
GRM_Cubic = 4,
} GDALResamplingMethod;
/************************************************************************/
/* GDALDownsampleChunk32R() */
/************************************************************************/
static CPLErr
GDALDownsampleChunk32R( int nSrcWidth, int nSrcHeight,
float * pafChunk,
GByte * pabyChunkNodataMask,
int nChunkXOff, int nChunkXSize,
int nChunkYOff, int nChunkYSize,
GDALRasterBand * poOverview,
const char * pszResampling,
int bHasNoData, float fNoDataValue,
GDALColorTable* poColorTable,
GDALDataType eSrcDataType)
{
/* -------------------------------------------------------------------- */
/* Check the input parameters. */
/* TODO: I think that instead of passing the pszResampling string */
/* and endless string comparisons this function should take */
/* GDALResamplingMethod flag. The string to flag conversion */
/* should be done on upper level. */
/* -------------------------------------------------------------------- */
CPLErr eErr = CE_None;
GDALResamplingMethod eResampling;
if ( EQUALN(pszResampling, "NEAR", 4) )
eResampling = GRM_Near;
else if ( EQUALN(pszResampling, "AVER", 4) )
eResampling = GRM_Average;
else if ( EQUALN(pszResampling, "GAUSS", 5) )
eResampling = GRM_Gauss;
else if ( EQUALN(pszResampling, "MODE", 4) )
eResampling = GRM_Mode;
else if ( EQUALN(pszResampling, "CUBIC", 6) )
eResampling = GRM_Cubic;
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"GDALDownsampleChunk32R: Unsupported resampling method \"%s\".",
pszResampling );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Create the filter kernel and allocate scanline buffer. */
/* -------------------------------------------------------------------- */
int nDstXOff, nDstXOff2, nDstYOff, nDstYOff2, nOXSize, nOYSize;
float *pafDstScanline;
int nGaussMatrixDim = 3;
const int *panGaussMatrix;
static const int anGaussMatrix3x3[] ={
1,2,1,
2,4,2,
1,2,1
};
static const int anGaussMatrix5x5[] = {
1,4,6,4,1,
4,16,24,16,4,
6,24,36,24,6,
4,16,24,16,4,
1,4,6,4,1};
static const int anGaussMatrix7x7[] = {
1,6,15,20,15,6,1,
6,36,90,120,90,36,6,
15,90,225,300,225,90,15,
20,120,300,400,300,120,20,
15,90,225,300,225,90,15,
6,36,90,120,90,36,6,
1,6,15,20,15,6,1};
nOXSize = poOverview->GetXSize();
nOYSize = poOverview->GetYSize();
int nResYFactor = (int) (0.5 + (double)nSrcHeight/(double)nOYSize);
// matrix for gauss filter
if(nResYFactor <= 2)
{
panGaussMatrix = anGaussMatrix3x3;
nGaussMatrixDim=3;
}
else if (nResYFactor <= 4)
{
panGaussMatrix = anGaussMatrix5x5;
nGaussMatrixDim=5;
}
else
{
panGaussMatrix = anGaussMatrix7x7;
nGaussMatrixDim=7;
}
/* -------------------------------------------------------------------- */
/* Figure out the column to start writing to, and the first column */
/* to not write to. */
/* -------------------------------------------------------------------- */
nDstXOff = (int) (0.5 + (nChunkXOff/(double)nSrcWidth) * nOXSize);
nDstXOff2 = (int)
(0.5 + ((nChunkXOff+nChunkXSize)/(double)nSrcWidth) * nOXSize);
if( nChunkXOff + nChunkXSize == nSrcWidth )
nDstXOff2 = nOXSize;
pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float));
if( pafDstScanline == NULL )
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALDownsampleChunk32R: Out of memory for line buffer." );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Figure out the line to start writing to, and the first line */
/* to not write to. In theory this approach should ensure that */
/* every output line will be written if all input chunks are */
/* processed. */
/* -------------------------------------------------------------------- */
nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize);
nDstYOff2 = (int)
(0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize);
if( nChunkYOff + nChunkYSize == nSrcHeight )
nDstYOff2 = nOYSize;
int nEntryCount = 0;
GDALColorEntry* aEntries = NULL;
if (poColorTable)
{
int i;
nEntryCount = poColorTable->GetColorEntryCount();
aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount);
for(i=0;i<nEntryCount;i++)
{
poColorTable->GetColorEntryAsRGB(i, &aEntries[i]);
}
}
int nMaxNumPx = 0;
float* pafVals = NULL;
int* panSums = NULL;
/* ==================================================================== */
/* Loop over destination scanlines. */
/* ==================================================================== */
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
{
float *pafSrcScanline;
GByte *pabySrcScanlineNodataMask;
int nSrcYOff, nSrcYOff2 = 0, iDstPixel;
if( eResampling == GRM_Near || eResampling == GRM_Average || eResampling == GRM_Mode )
{
nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight);
if ( nSrcYOff < nChunkYOff )
nSrcYOff = nChunkYOff;
nSrcYOff2 =
(int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight);
}
else /* if (eResampling == GRM_Gauss) */
{
nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight);
nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight) + 1;
if( nSrcYOff < nChunkYOff )
{
nSrcYOff = nChunkYOff;
nSrcYOff2++;
}
int iSizeY = nSrcYOff2 - nSrcYOff;
nSrcYOff = nSrcYOff + iSizeY/2 - nGaussMatrixDim/2;
nSrcYOff2 = nSrcYOff + nGaussMatrixDim;
if(nSrcYOff < 0)
nSrcYOff = 0;
}
if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 )
nSrcYOff2 = nSrcHeight;
if( nSrcYOff2 > nChunkYOff + nChunkYSize)
nSrcYOff2 = nChunkYOff + nChunkYSize;
pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize);
if (pabyChunkNodataMask != NULL)
pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize);
else
pabySrcScanlineNodataMask = NULL;
/* -------------------------------------------------------------------- */
/* Loop over destination pixels */
/* -------------------------------------------------------------------- */
for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ )
{
int nSrcXOff, nSrcXOff2;
if ( eResampling == GRM_Near || eResampling == GRM_Average || eResampling == GRM_Mode )
{
nSrcXOff =
(int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth);
if ( nSrcXOff < nChunkXOff )
nSrcXOff = nChunkXOff;
nSrcXOff2 = (int)
(0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth);
}
else /* if (eResampling == GRM_Gauss) */
{
nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth);
nSrcXOff2 = (int)(0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth) + 1;
int iSizeX = nSrcXOff2 - nSrcXOff;
nSrcXOff = nSrcXOff + iSizeX/2 - nGaussMatrixDim/2;
nSrcXOff2 = nSrcXOff + nGaussMatrixDim;
if(nSrcXOff < 0)
nSrcXOff = 0;
}
if( nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize-1 )
nSrcXOff2 = nSrcWidth;
if( nSrcXOff2 > nChunkXOff + nChunkXSize )
nSrcXOff2 = nChunkXOff + nChunkXSize;
if ( eResampling == GRM_Near )
{
pafDstScanline[iDstPixel - nDstXOff] = pafSrcScanline[nSrcXOff - nChunkXOff];
}
else if ( eResampling == GRM_Average )
{
if (poColorTable == NULL)
{
double val;
double dfTotal = 0.0;
int nCount = 0, iX, iY;
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
{
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
{
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
if (pabySrcScanlineNodataMask == NULL ||
pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize])
{
dfTotal += val;
nCount++;
}
}
}
if (bHasNoData && nCount == 0)
{
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
}
else
{
if( nCount == 0 )
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
else
pafDstScanline[iDstPixel - nDstXOff] = (float) (dfTotal / nCount);
}
}
else
{
double val;
int nTotalR = 0, nTotalG = 0, nTotalB = 0;
int nCount = 0, iX, iY;
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
{
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
{
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
if (bHasNoData == FALSE || val != fNoDataValue)
{
int nVal = (int)val;
if (nVal >= 0 && nVal < nEntryCount)
{
nTotalR += aEntries[nVal].c1;
nTotalG += aEntries[nVal].c2;
nTotalB += aEntries[nVal].c3;
nCount++;
}
}
}
}
if (bHasNoData && nCount == 0)
{
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
}
else
{
CPLAssert( nCount > 0 );
if( nCount == 0 )
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
else
{
int nR = nTotalR / nCount, nG = nTotalG / nCount, nB = nTotalB / nCount;
int i;
double dfMinDist = 0;
int iBestEntry = 0;
for(i=0;i<nEntryCount;i++)
{
double dfDist = (nR - aEntries[i].c1) * (nR - aEntries[i].c1) +
(nG - aEntries[i].c2) * (nG - aEntries[i].c2) +
(nB - aEntries[i].c3) * (nB - aEntries[i].c3);
if (i == 0 || dfDist < dfMinDist)
{
dfMinDist = dfDist;
iBestEntry = i;
}
}
pafDstScanline[iDstPixel - nDstXOff] = iBestEntry;
}
}
}
}
else if ( eResampling == GRM_Mode )
{
if (eSrcDataType != GDT_Byte || nEntryCount > 256)
{
/* I'm not sure how much sense it makes to run a majority
filter on floating point data, but here it is for the sake
of compatability. It won't look right on RGB images by the
nature of the filter. */
int nNumPx = (nSrcYOff2-nSrcYOff)*(nSrcXOff2-nSrcXOff);
int iMaxInd = 0, iMaxVal = -1, iY, iX;
if (nNumPx > nMaxNumPx)
{
pafVals = (float*) CPLRealloc(pafVals, nNumPx * sizeof(float));
panSums = (int*) CPLRealloc(panSums, nNumPx * sizeof(int));
nMaxNumPx = nNumPx;
}
for( iY = nSrcYOff; iY < nSrcYOff2; ++iY )
{
int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff;
for( iX = nSrcXOff; iX < nSrcXOff2; ++iX )
{
if (pabySrcScanlineNodataMask == NULL ||
pabySrcScanlineNodataMask[iX+iTotYOff])
{
float fVal = pafSrcScanline[iX+iTotYOff];
int i;
//Check array for existing entry
for( i = 0; i < iMaxInd; ++i )
if( pafVals[i] == fVal
&& ++panSums[i] > panSums[iMaxVal] )
{
iMaxVal = i;
break;
}
//Add to arr if entry not already there
if( i == iMaxInd )
{
pafVals[iMaxInd] = fVal;
panSums[iMaxInd] = 1;
if( iMaxVal < 0 )
iMaxVal = iMaxInd;
++iMaxInd;
}
}
}
}
if( iMaxVal == -1 )
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
else
pafDstScanline[iDstPixel - nDstXOff] = pafVals[iMaxVal];
}
else /* if (eSrcDataType == GDT_Byte && nEntryCount < 256) */
{
/* So we go here for a paletted or non-paletted byte band */
/* The input values are then between 0 and 255 */
int anVals[256], nMaxVal = 0, iMaxInd = -1, iY, iX;
memset(anVals, 0, 256*sizeof(int));
for( iY = nSrcYOff; iY < nSrcYOff2; ++iY )
{
int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff;
for( iX = nSrcXOff; iX < nSrcXOff2; ++iX )
{
float val = pafSrcScanline[iX+iTotYOff];
if (bHasNoData == FALSE || val != fNoDataValue)
{
int nVal = (int) val;
if ( ++anVals[nVal] > nMaxVal)
{
//Sum the density
//Is it the most common value so far?
iMaxInd = nVal;
nMaxVal = anVals[nVal];
}
}
}
}
if( iMaxInd == -1 )
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
else
pafDstScanline[iDstPixel - nDstXOff] = iMaxInd;
}
}
else /* if ( eResampling == GRM_Gauss ) */
{
if (poColorTable == NULL)
{
double dfTotal = 0.0, val;
int nCount = 0, iX, iY;
int i = 0,j = 0;
const int *panLineWeight = panGaussMatrix;
for( j=0, iY = nSrcYOff; iY < nSrcYOff2;
iY++, j++, panLineWeight += nGaussMatrixDim )
{
for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i )
{
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
if (pabySrcScanlineNodataMask == NULL ||
pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize])
{
int nWeight = panLineWeight[i];
dfTotal += val * nWeight;
nCount += nWeight;
}
}
}
if (bHasNoData && nCount == 0)
{
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
}
else
{
if( nCount == 0 )
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
else
pafDstScanline[iDstPixel - nDstXOff] = (float) (dfTotal / nCount);
}
}
else
{
double val;
int nTotalR = 0, nTotalG = 0, nTotalB = 0;
int nTotalWeight = 0, iX, iY;
int i = 0,j = 0;
const int *panLineWeight = panGaussMatrix;
for( j=0, iY = nSrcYOff; iY < nSrcYOff2;
iY++, j++, panLineWeight += nGaussMatrixDim )
{
for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i )
{
val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize];
if (bHasNoData == FALSE || val != fNoDataValue)
{
int nVal = (int)val;
if (nVal >= 0 && nVal < nEntryCount)
{
int nWeight = panLineWeight[i];
nTotalR += aEntries[nVal].c1 * nWeight;
nTotalG += aEntries[nVal].c2 * nWeight;
nTotalB += aEntries[nVal].c3 * nWeight;
nTotalWeight += nWeight;
}
}
}
}
if (bHasNoData && nTotalWeight == 0)
{
pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue;
}
else
{
CPLAssert( nTotalWeight > 0 );
if( nTotalWeight == 0 )
pafDstScanline[iDstPixel - nDstXOff] = 0.0;
else
{
int nR = nTotalR / nTotalWeight, nG = nTotalG / nTotalWeight, nB = nTotalB / nTotalWeight;
int i;
double dfMinDist = 0;
int iBestEntry = 0;
for(i=0;i<nEntryCount;i++)
{
double dfDist = (nR - aEntries[i].c1) * (nR - aEntries[i].c1) +
(nG - aEntries[i].c2) * (nG - aEntries[i].c2) +
(nB - aEntries[i].c3) * (nB - aEntries[i].c3);
if (i == 0 || dfDist < dfMinDist)
{
dfMinDist = dfDist;
iBestEntry = i;
}
}
pafDstScanline[iDstPixel - nDstXOff] = iBestEntry;
}
}
}
} // end of gauss
}
eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1,
pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32,
0, 0 );
}
CPLFree( pafDstScanline );
CPLFree( aEntries );
CPLFree( pafVals );
CPLFree( panSums );
return eErr;
}
/************************************************************************/
/* GDALDownsampleChunkC32R() */
/************************************************************************/
static CPLErr
GDALDownsampleChunkC32R( int nSrcWidth, int nSrcHeight,
float * pafChunk, int nChunkYOff, int nChunkYSize,
GDALRasterBand * poOverview,
const char * pszResampling )
{
int nDstYOff, nDstYOff2, nOXSize, nOYSize;
float *pafDstScanline;
CPLErr eErr = CE_None;
nOXSize = poOverview->GetXSize();
nOYSize = poOverview->GetYSize();
pafDstScanline = (float *) VSIMalloc(nOXSize * sizeof(float) * 2);
if( pafDstScanline == NULL )
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALDownsampleChunkC32R: Out of memory for line buffer." );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Figure out the line to start writing to, and the first line */
/* to not write to. In theory this approach should ensure that */
/* every output line will be written if all input chunks are */
/* processed. */
/* -------------------------------------------------------------------- */
nDstYOff = (int) (0.5 + (nChunkYOff/(double)nSrcHeight) * nOYSize);
nDstYOff2 = (int)
(0.5 + ((nChunkYOff+nChunkYSize)/(double)nSrcHeight) * nOYSize);
if( nChunkYOff + nChunkYSize == nSrcHeight )
nDstYOff2 = nOYSize;
/* ==================================================================== */
/* Loop over destination scanlines. */
/* ==================================================================== */
for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ )
{
float *pafSrcScanline;
int nSrcYOff, nSrcYOff2, iDstPixel;
nSrcYOff = (int) (0.5 + (iDstLine/(double)nOYSize) * nSrcHeight);
if( nSrcYOff < nChunkYOff )
nSrcYOff = nChunkYOff;
nSrcYOff2 = (int) (0.5 + ((iDstLine+1)/(double)nOYSize) * nSrcHeight);
if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 )
nSrcYOff2 = nSrcHeight;
if( nSrcYOff2 > nChunkYOff + nChunkYSize )
nSrcYOff2 = nChunkYOff + nChunkYSize;
pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nSrcWidth) * 2;
/* -------------------------------------------------------------------- */
/* Loop over destination pixels */
/* -------------------------------------------------------------------- */
for( iDstPixel = 0; iDstPixel < nOXSize; iDstPixel++ )
{
int nSrcXOff, nSrcXOff2;
nSrcXOff = (int) (0.5 + (iDstPixel/(double)nOXSize) * nSrcWidth);
nSrcXOff2 = (int)
(0.5 + ((iDstPixel+1)/(double)nOXSize) * nSrcWidth);
if( nSrcXOff2 > nSrcWidth )
nSrcXOff2 = nSrcWidth;
if( EQUALN(pszResampling,"NEAR",4) )
{
pafDstScanline[iDstPixel*2] = pafSrcScanline[nSrcXOff*2];
pafDstScanline[iDstPixel*2+1] = pafSrcScanline[nSrcXOff*2+1];
}
else if( EQUAL(pszResampling,"AVERAGE_MAGPHASE") )
{
double dfTotalR = 0.0, dfTotalI = 0.0, dfTotalM = 0.0;
int nCount = 0, iX, iY;
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
{
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
{
double dfR, dfI;
dfR = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2];
dfI = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1];
dfTotalR += dfR;
dfTotalI += dfI;
dfTotalM += sqrt( dfR*dfR + dfI*dfI );
nCount++;
}
}
CPLAssert( nCount > 0 );
if( nCount == 0 )
{
pafDstScanline[iDstPixel*2] = 0.0;
pafDstScanline[iDstPixel*2+1] = 0.0;
}
else
{
double dfM, dfDesiredM, dfRatio=1.0;
pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount);
pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount);
dfM = sqrt(pafDstScanline[iDstPixel*2 ]*pafDstScanline[iDstPixel*2 ]
+ pafDstScanline[iDstPixel*2+1]*pafDstScanline[iDstPixel*2+1]);
dfDesiredM = dfTotalM / nCount;
if( dfM != 0.0 )
dfRatio = dfDesiredM / dfM;
pafDstScanline[iDstPixel*2 ] *= (float) dfRatio;
pafDstScanline[iDstPixel*2+1] *= (float) dfRatio;
}
}
else if( EQUALN(pszResampling,"AVER",4) )
{
double dfTotalR = 0.0, dfTotalI = 0.0;
int nCount = 0, iX, iY;
for( iY = nSrcYOff; iY < nSrcYOff2; iY++ )
{
for( iX = nSrcXOff; iX < nSrcXOff2; iX++ )
{
dfTotalR += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2];
dfTotalI += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1];
nCount++;
}
}
CPLAssert( nCount > 0 );
if( nCount == 0 )
{
pafDstScanline[iDstPixel*2] = 0.0;
pafDstScanline[iDstPixel*2+1] = 0.0;
}
else
{
pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount);
pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount);
}
}
}
eErr = poOverview->RasterIO( GF_Write, 0, iDstLine, nOXSize, 1,
pafDstScanline, nOXSize, 1, GDT_CFloat32,
0, 0 );
}
CPLFree( pafDstScanline );
return eErr;
}
/************************************************************************/
/* GDALRegenerateCascadingOverviews() */
/* */
/* Generate a list of overviews in order from largest to */
/* smallest, computing each from the next larger. */
/************************************************************************/
static CPLErr
GDALRegenerateCascadingOverviews(
GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands,
const char * pszResampling,
GDALProgressFunc pfnProgress, void * pProgressData )
{
/* -------------------------------------------------------------------- */
/* First, we must put the overviews in order from largest to */
/* smallest. */
/* -------------------------------------------------------------------- */
int i, j;
for( i = 0; i < nOverviews-1; i++ )
{
for( j = 0; j < nOverviews - i - 1; j++ )
{
if( papoOvrBands[j]->GetXSize()
* (float) papoOvrBands[j]->GetYSize() <
papoOvrBands[j+1]->GetXSize()
* (float) papoOvrBands[j+1]->GetYSize() )
{
GDALRasterBand * poTempBand;
poTempBand = papoOvrBands[j];
papoOvrBands[j] = papoOvrBands[j+1];
papoOvrBands[j+1] = poTempBand;
}
}
}
/* -------------------------------------------------------------------- */
/* Count total pixels so we can prepare appropriate scaled */
/* progress functions. */
/* -------------------------------------------------------------------- */
double dfTotalPixels = 0.0;
for( i = 0; i < nOverviews; i++ )
{
dfTotalPixels += papoOvrBands[i]->GetXSize()
* (double) papoOvrBands[i]->GetYSize();
}
/* -------------------------------------------------------------------- */
/* Generate all the bands. */
/* -------------------------------------------------------------------- */
double dfPixelsProcessed = 0.0;
for( i = 0; i < nOverviews; i++ )
{
void *pScaledProgressData;
double dfPixels;
GDALRasterBand *poBaseBand;
CPLErr eErr;
if( i == 0 )
poBaseBand = poSrcBand;
else
poBaseBand = papoOvrBands[i-1];
dfPixels = papoOvrBands[i]->GetXSize()
* (double) papoOvrBands[i]->GetYSize();
pScaledProgressData = GDALCreateScaledProgress(
dfPixelsProcessed / dfTotalPixels,
(dfPixelsProcessed + dfPixels) / dfTotalPixels,
pfnProgress, pProgressData );
eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBaseBand,
1, (GDALRasterBandH *) papoOvrBands+i,
pszResampling,
GDALScaledProgress,
pScaledProgressData );
GDALDestroyScaledProgress( pScaledProgressData );
if( eErr != CE_None )
return eErr;
dfPixelsProcessed += dfPixels;
/* we only do the bit2grayscale promotion on the base band */
if( EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13) )
pszResampling = "AVERAGE";
}
return CE_None;
}
/************************************************************************/
/* GDALRegenerateOverviews() */
/************************************************************************/
/**
* Generate downsampled overviews.
*
* This function will generate one or more overview images from a base
* image using the requested downsampling algorithm. It's primary use
* is for generating overviews via GDALDataset::BuildOverviews(), but it
* can also be used to generate downsampled images in one file from another
* outside the overview architecture.
*
* The output bands need to exist in advance.
*
* The full set of resampling algorithms is documented in
* GDALDataset::BuildOverviews().
*
* @param hSrcBand the source (base level) band.
* @param nOverviewCount the number of downsampled bands being generated.
* @param pahOvrBands the list of downsampled bands to be generated.
* @param pszResampling Resampling algorithm (eg. "AVERAGE").
* @param pfnProgress progress report function.
* @param pProgressData progress function callback data.
* @return CE_None on success or CE_Failure on failure.
*/
CPLErr
GDALRegenerateOverviews( GDALRasterBandH hSrcBand,
int nOverviewCount, GDALRasterBandH *pahOvrBands,
const char * pszResampling,
GDALProgressFunc pfnProgress, void * pProgressData )
{
GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand;
GDALRasterBand **papoOvrBands = (GDALRasterBand **) pahOvrBands;
int nFullResYChunk, nWidth;
int nFRXBlockSize, nFRYBlockSize;
GDALDataType eType;
int bHasNoData;
float fNoDataValue;
GDALColorTable* poColorTable = NULL;
if( pfnProgress == NULL )
pfnProgress = GDALDummyProgress;
if( EQUAL(pszResampling,"NONE") )
return CE_None;
/* -------------------------------------------------------------------- */
/* Check color tables... */
/* -------------------------------------------------------------------- */
if ((EQUALN(pszResampling,"AVER",4)
|| EQUALN(pszResampling,"MODE",4)
|| EQUALN(pszResampling,"GAUSS",5)) &&
poSrcBand->GetColorInterpretation() == GCI_PaletteIndex)
{
poColorTable = poSrcBand->GetColorTable();
if (poColorTable != NULL)
{
if (poColorTable->GetPaletteInterpretation() != GPI_RGB)
{
CPLError(CE_Warning, CPLE_AppDefined,
"Computing overviews on palette index raster bands "
"with a palette whose color interpreation is not RGB "
"will probably lead to unexpected results.");
poColorTable = NULL;
}
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Computing overviews on palette index raster bands "
"without a palette will probably lead to unexpected results.");
}
}
/* -------------------------------------------------------------------- */
/* If we are operating on multiple overviews, and using */
/* averaging, lets do them in cascading order to reduce the */
/* amount of computation. */
/* -------------------------------------------------------------------- */
if( (EQUALN(pszResampling,"AVER",4) || EQUALN(pszResampling,"GAUSS",5)) && nOverviewCount > 1 )
return GDALRegenerateCascadingOverviews( poSrcBand,
nOverviewCount, papoOvrBands,
pszResampling,
pfnProgress,
pProgressData );
/* -------------------------------------------------------------------- */
/* Setup one horizontal swath to read from the raw buffer. */
/* -------------------------------------------------------------------- */
float *pafChunk;
GByte *pabyChunkNodataMask = NULL;
poSrcBand->GetBlockSize( &nFRXBlockSize, &nFRYBlockSize );
if( nFRYBlockSize < 4 || nFRYBlockSize > 256 )
nFullResYChunk = 32;
else
nFullResYChunk = nFRYBlockSize;
if( GDALDataTypeIsComplex( poSrcBand->GetRasterDataType() ) )
eType = GDT_CFloat32;
else
eType = GDT_Float32;
/* If we have a nodata mask and we are doing something more complicated */
/* than nearest neighbouring, we have to fetch to nodata mask */
int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) &&
(poSrcBand->GetMaskFlags() & GMF_ALL_VALID) == 0);
nWidth = poSrcBand->GetXSize();
pafChunk = (float *)
VSIMalloc3((GDALGetDataTypeSize(eType)/8), nFullResYChunk, nWidth );
if (bUseNoDataMask)
{
pabyChunkNodataMask = (GByte *)
(GByte*) VSIMalloc2( nFullResYChunk, nWidth );
}
if( pafChunk == NULL || (bUseNoDataMask && pabyChunkNodataMask == NULL))
{
CPLFree(pafChunk);
CPLFree(pabyChunkNodataMask);
CPLError( CE_Failure, CPLE_OutOfMemory,
"Out of memory in GDALRegenerateOverviews()." );
return CE_Failure;
}
fNoDataValue = (float) poSrcBand->GetNoDataValue(&bHasNoData);
if (!bHasNoData)
{
if (!EQUALN(pszResampling,"NEAR",4) && poSrcBand->GetDataset() != NULL)
{
const char* pszNoDataValues = poSrcBand->GetDataset()->GetMetadataItem("NODATA_VALUES");
if (pszNoDataValues)
{
char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE);
/* Make sure we have as many values as bands */
if (CSLCount(papszNoDataValues) == poSrcBand->GetDataset()->GetRasterCount() &&
poSrcBand->GetDataset()->GetRasterCount() != 0)
{
fNoDataValue = (float)atof(papszNoDataValues[poSrcBand->GetBand() - 1]);
bHasNoData = TRUE;
}
CSLDestroy(papszNoDataValues);
}
}
}
/* -------------------------------------------------------------------- */
/* Loop over image operating on chunks. */
/* -------------------------------------------------------------------- */
int nChunkYOff = 0;
CPLErr eErr = CE_None;
for( nChunkYOff = 0;
nChunkYOff < poSrcBand->GetYSize() && eErr == CE_None;
nChunkYOff += nFullResYChunk )
{
if( !pfnProgress( nChunkYOff / (double) poSrcBand->GetYSize(),
NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
return CE_Failure;
}
if( nFullResYChunk + nChunkYOff > poSrcBand->GetYSize() )
nFullResYChunk = poSrcBand->GetYSize() - nChunkYOff;
/* read chunk */
poSrcBand->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk,
pafChunk, nWidth, nFullResYChunk, eType,
0, 0 );
if (bUseNoDataMask)
poSrcBand->GetMaskBand()->RasterIO( GF_Read, 0, nChunkYOff, nWidth, nFullResYChunk,
pabyChunkNodataMask, nWidth, nFullResYChunk, GDT_Byte,
0, 0 );
/* special case to promote 1bit data to 8bit 0/255 values */
if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE") )
{
int i;
for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- )
{
if( pafChunk[i] == 1.0 )
pafChunk[i] = 255.0;
}
}
else if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE_MINISWHITE") )
{
int i;
for( i = nFullResYChunk*nWidth - 1; i >= 0; i-- )
{
if( pafChunk[i] == 1.0 )
pafChunk[i] = 0.0;
else if( pafChunk[i] == 0.0 )
pafChunk[i] = 255.0;
}
}
for( int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None; iOverview++ )
{
if( eType == GDT_Float32 )
eErr = GDALDownsampleChunk32R(nWidth, poSrcBand->GetYSize(),
pafChunk,
pabyChunkNodataMask,
0, nWidth,
nChunkYOff, nFullResYChunk,
papoOvrBands[iOverview], pszResampling,
bHasNoData, fNoDataValue, poColorTable,
poSrcBand->GetRasterDataType());
else
eErr = GDALDownsampleChunkC32R(nWidth, poSrcBand->GetYSize(),
pafChunk, nChunkYOff, nFullResYChunk,
papoOvrBands[iOverview], pszResampling);
}
}
VSIFree( pafChunk );
VSIFree( pabyChunkNodataMask );
/* -------------------------------------------------------------------- */
/* Renormalized overview mean / stddev if needed. */
/* -------------------------------------------------------------------- */
if( EQUAL(pszResampling,"AVERAGE_MP") )
{
GDALOverviewMagnitudeCorrection( (GDALRasterBandH) poSrcBand,
nOverviewCount,
(GDALRasterBandH *) papoOvrBands,
GDALDummyProgress, NULL );
}
/* -------------------------------------------------------------------- */
/* It can be important to flush out data to overviews. */
/* -------------------------------------------------------------------- */
for( int iOverview = 0;
eErr == CE_None && iOverview < nOverviewCount;
iOverview++ )
{
eErr = papoOvrBands[iOverview]->FlushCache();
}
if (eErr == CE_None)
pfnProgress( 1.0, NULL, pProgressData );
return eErr;
}
/************************************************************************/
/* GDALRegenerateOverviewsMultiBand() */
/************************************************************************/
/**
* Variant of GDALRegenerateOverviews, specialy dedicated for generating
* compressed pixel-interleaved overviews (JPEG-IN-TIFF for example)
*
* This function will generate one or more overview images from a base
* image using the requested downsampling algorithm. It's primary use
* is for generating overviews via GDALDataset::BuildOverviews(), but it
* can also be used to generate downsampled images in one file from another
* outside the overview architecture.
*
* The output bands need to exist in advance and share the same characteristics
* (type, dimensions)
*
* The resampling algorithms supported for the moment are "NEAREST", "AVERAGE"
* and "GAUSS"
*
* The pseudo-algorithm used by the function is :
* for each overview
* iterate on lines of the source by a step of deltay
* iterate on columns of the source by a step of deltax
* read the source data of size deltax * deltay for all the bands
* generate the corresponding overview block for all the bands
*
* @param nBands the number of bands, size of papoSrcBands and size of
* first dimension of papapoOverviewBands
* @param papoSrcBands the list of source bands to downsample
* @param nOverviews the number of downsampled overview levels being generated.
* @param papapoOverviewBands bidimension array of bands. First dimension is indexed
* by nBands. Second dimension is indexed by nOverviews.
* @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE" or "GAUSS").
* @param pfnProgress progress report function.
* @param pProgressData progress function callback data.
* @return CE_None on success or CE_Failure on failure.
*/
CPLErr
GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands,
int nOverviews,
GDALRasterBand*** papapoOverviewBands,
const char * pszResampling,
GDALProgressFunc pfnProgress, void * pProgressData )
{
CPLErr eErr = CE_None;
int iOverview, iBand;
if( pfnProgress == NULL )
pfnProgress = GDALDummyProgress;
if( EQUAL(pszResampling,"NONE") )
return CE_None;
/* Sanity checks */
if (!EQUALN(pszResampling, "NEAR", 4) && !EQUAL(pszResampling, "AVERAGE") && !EQUAL(pszResampling, "GAUSS"))
{
CPLError(CE_Failure, CPLE_NotSupported,
"GDALRegenerateOverviewsMultiBand: pszResampling='%s' not supported", pszResampling);
return CE_Failure;
}
int nSrcWidth = papoSrcBands[0]->GetXSize();
int nSrcHeight = papoSrcBands[0]->GetYSize();
GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType();
for(iBand=1;iBand<nBands;iBand++)
{
if (papoSrcBands[iBand]->GetXSize() != nSrcWidth ||
papoSrcBands[iBand]->GetYSize() != nSrcHeight)
{
CPLError(CE_Failure, CPLE_NotSupported,
"GDALRegenerateOverviewsMultiBand: all the source bands must have the same dimensions");
return CE_Failure;
}
if (papoSrcBands[iBand]->GetRasterDataType() != eDataType)
{
CPLError(CE_Failure, CPLE_NotSupported,
"GDALRegenerateOverviewsMultiBand: all the source bands must have the same data type");
return CE_Failure;
}
}
for(iOverview=0;iOverview<nOverviews;iOverview++)
{
int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
int nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize();
for(iBand=1;iBand<nBands;iBand++)
{
if (papapoOverviewBands[iBand][iOverview]->GetXSize() != nDstWidth ||
papapoOverviewBands[iBand][iOverview]->GetYSize() != nDstHeight)
{
CPLError(CE_Failure, CPLE_NotSupported,
"GDALRegenerateOverviewsMultiBand: all the overviews bands of the same level must have the same dimensions");
return CE_Failure;
}
if (papapoOverviewBands[iBand][iOverview]->GetRasterDataType() != eDataType)
{
CPLError(CE_Failure, CPLE_NotSupported,
"GDALRegenerateOverviewsMultiBand: all the overviews bands must have the same data type as the source bands");
return CE_Failure;
}
}
}
/* First pass to compute the total number of pixels to read */
double dfTotalPixelCount = 0;
for(iOverview=0;iOverview<nOverviews;iOverview++)
{
nSrcWidth = papoSrcBands[0]->GetXSize();
nSrcHeight = papoSrcBands[0]->GetYSize();
int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
/* Try to use previous level of overview as the source to compute */
/* the next level */
if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth)
{
nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
}
dfTotalPixelCount += (double)nSrcWidth * nSrcHeight;
}
nSrcWidth = papoSrcBands[0]->GetXSize();
nSrcHeight = papoSrcBands[0]->GetYSize();
/* If we have a nodata mask and we are doing something more complicated */
/* than nearest neighbouring, we have to fetch to nodata mask */
int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) &&
(papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0);
int* pabHasNoData = (int*)CPLMalloc(nBands * sizeof(int));
float* pafNoDataValue = (float*)CPLMalloc(nBands * sizeof(float));
for(iBand=0;iBand<nBands;iBand++)
{
pabHasNoData[iBand] = FALSE;
pafNoDataValue[iBand] = (float) papoSrcBands[iBand]->GetNoDataValue(&pabHasNoData[iBand]);
if (!pabHasNoData[iBand])
{
if (!EQUALN(pszResampling,"NEAR",4) && papoSrcBands[iBand]->GetDataset() != NULL)
{
const char* pszNoDataValues = papoSrcBands[iBand]->GetDataset()->GetMetadataItem("NODATA_VALUES");
if (pszNoDataValues)
{
char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE);
/* Make sure we have as many values as bands */
if (CSLCount(papszNoDataValues) == papoSrcBands[iBand]->GetDataset()->GetRasterCount() &&
papoSrcBands[iBand]->GetDataset()->GetRasterCount() != 0)
{
pafNoDataValue[iBand] = (float)atof(papszNoDataValues[papoSrcBands[iBand]->GetBand() - 1]);
pabHasNoData[iBand] = TRUE;
}
CSLDestroy(papszNoDataValues);
}
}
}
}
/* Second pass to do the real job ! */
double dfCurPixelCount = 0;
for(iOverview=0;iOverview<nOverviews && eErr == CE_None;iOverview++)
{
int iSrcOverview = -1; /* -1 means the source bands */
int nDstBlockXSize, nDstBlockYSize;
int nDstWidth, nDstHeight;
papapoOverviewBands[0][iOverview]->GetBlockSize(&nDstBlockXSize, &nDstBlockYSize);
nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize();
nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize();
/* Try to use previous level of overview as the source to compute */
/* the next level */
if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth)
{
nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize();
nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize();
iSrcOverview = iOverview - 1;
}
/* Compute the chunck size of the source such as it will match the size of */
/* a block of the overview */
int nFullResXChunk = (nDstBlockXSize * nSrcWidth) / nDstWidth;
int nFullResYChunk = (nDstBlockYSize * nSrcHeight) / nDstHeight;
float** papafChunk = (float**) CPLMalloc(nBands * sizeof(void*));
GByte* pabyChunkNoDataMask = NULL;
for(iBand=0;iBand<nBands;iBand++)
{
papafChunk[iBand] = (float*) VSIMalloc3(nFullResXChunk, nFullResYChunk, sizeof(float));
if( papafChunk[iBand] == NULL )
{
while ( --iBand >= 0)
CPLFree(papafChunk[iBand]);
CPLFree(papafChunk);
CPLFree(pabHasNoData);
CPLFree(pafNoDataValue);
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALRegenerateOverviewsMultiBand: Out of memory." );
return CE_Failure;
}
}
if (bUseNoDataMask)
{
pabyChunkNoDataMask = (GByte*) VSIMalloc2(nFullResXChunk, nFullResYChunk);
if( pabyChunkNoDataMask == NULL )
{
for(iBand=0;iBand<nBands;iBand++)
{
CPLFree(papafChunk[iBand]);
}
CPLFree(papafChunk);
CPLFree(pabHasNoData);
CPLFree(pafNoDataValue);
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALRegenerateOverviewsMultiBand: Out of memory." );
return CE_Failure;
}
}
int nChunkYOff;
/* Iterate on destination overview, block by block */
for( nChunkYOff = 0; nChunkYOff < nSrcHeight && eErr == CE_None; nChunkYOff += nFullResYChunk )
{
int nYCount;
if (nChunkYOff + nFullResYChunk <= nSrcHeight)
nYCount = nFullResYChunk;
else
nYCount = nSrcHeight - nChunkYOff;
if( !pfnProgress( dfCurPixelCount / dfTotalPixelCount,
NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
return CE_Failure;
}
int nChunkXOff;
for( nChunkXOff = 0; nChunkXOff < nSrcWidth && eErr == CE_None; nChunkXOff += nFullResXChunk )
{
int nXCount;
if (nChunkXOff + nFullResXChunk <= nSrcWidth)
nXCount = nFullResXChunk;
else
nXCount = nSrcWidth - nChunkXOff;
/* Read the source buffers for all the bands */
for(iBand=0;iBand<nBands && eErr == CE_None;iBand++)
{
GDALRasterBand* poSrcBand;
if (iSrcOverview == -1)
poSrcBand = papoSrcBands[iBand];
else
poSrcBand = papapoOverviewBands[iBand][iSrcOverview];
eErr = poSrcBand->RasterIO( GF_Read,
nChunkXOff, nChunkYOff,
nXCount, nYCount,
papafChunk[iBand],
nXCount, nYCount,
GDT_Float32, 0, 0 );
}
if (bUseNoDataMask && eErr == CE_None)
{
GDALRasterBand* poSrcBand;
if (iSrcOverview == -1)
poSrcBand = papoSrcBands[0];
else
poSrcBand = papapoOverviewBands[0][iSrcOverview];
eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read,
nChunkXOff, nChunkYOff,
nXCount, nYCount,
pabyChunkNoDataMask,
nXCount, nYCount,
GDT_Byte, 0, 0 );
}
/* Compute the resulting overview block */
for(iBand=0;iBand<nBands && eErr == CE_None;iBand++)
{
eErr = GDALDownsampleChunk32R(nSrcWidth, nSrcHeight,
papafChunk[iBand],
pabyChunkNoDataMask,
nChunkXOff, nXCount,
nChunkYOff, nYCount,
papapoOverviewBands[iBand][iOverview],
pszResampling,
pabHasNoData[iBand],
pafNoDataValue[iBand],
/*poColorTable*/ NULL,
eDataType);
}
}
dfCurPixelCount += (double)nYCount * nSrcWidth;
}
/* Flush the data to overviews */
for(iBand=0;iBand<nBands;iBand++)
{
CPLFree(papafChunk[iBand]);
papapoOverviewBands[iBand][iOverview]->FlushCache();
}
CPLFree(papafChunk);
CPLFree(pabyChunkNoDataMask);
}
CPLFree(pabHasNoData);
CPLFree(pafNoDataValue);
if (eErr == CE_None)
pfnProgress( 1.0, NULL, pProgressData );
return eErr;
}
/************************************************************************/
/* GDALComputeBandStats() */
/************************************************************************/
CPLErr CPL_STDCALL
GDALComputeBandStats( GDALRasterBandH hSrcBand,
int nSampleStep,
double *pdfMean, double *pdfStdDev,
GDALProgressFunc pfnProgress,
void *pProgressData )
{
VALIDATE_POINTER1( hSrcBand, "GDALComputeBandStats", CE_Failure );
GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand;
int iLine, nWidth, nHeight;
GDALDataType eType = poSrcBand->GetRasterDataType();
GDALDataType eWrkType;
int bComplex;
float *pafData;
double dfSum=0.0, dfSum2=0.0;
int nSamples = 0;
if( pfnProgress == NULL )
pfnProgress = GDALDummyProgress;
nWidth = poSrcBand->GetXSize();
nHeight = poSrcBand->GetYSize();
if( nSampleStep >= nHeight || nSampleStep < 1 )
nSampleStep = 1;
bComplex = GDALDataTypeIsComplex(eType);
if( bComplex )
{
pafData = (float *) VSIMalloc(nWidth * 2 * sizeof(float));
eWrkType = GDT_CFloat32;
}
else
{
pafData = (float *) VSIMalloc(nWidth * sizeof(float));
eWrkType = GDT_Float32;
}
if( pafData == NULL )
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALComputeBandStats: Out of memory for buffer." );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Loop over all sample lines. */
/* -------------------------------------------------------------------- */
for( iLine = 0; iLine < nHeight; iLine += nSampleStep )
{
int iPixel;
if( !pfnProgress( iLine / (double) nHeight,
NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
CPLFree( pafData );
return CE_Failure;
}
poSrcBand->RasterIO( GF_Read, 0, iLine, nWidth, 1,
pafData, nWidth, 1, eWrkType,
0, 0 );
for( iPixel = 0; iPixel < nWidth; iPixel++ )
{
float fValue;
if( bComplex )
{
// Compute the magnitude of the complex value.
fValue = (float)
sqrt(pafData[iPixel*2 ] * pafData[iPixel*2 ]
+ pafData[iPixel*2+1] * pafData[iPixel*2+1]);
}
else
{
fValue = pafData[iPixel];
}
dfSum += fValue;
dfSum2 += fValue * fValue;
}
nSamples += nWidth;
}
if( !pfnProgress( 1.0, NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
CPLFree( pafData );
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Produce the result values. */
/* -------------------------------------------------------------------- */
if( pdfMean != NULL )
*pdfMean = dfSum / nSamples;
if( pdfStdDev != NULL )
{
double dfMean = dfSum / nSamples;
*pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean));
}
CPLFree( pafData );
return CE_None;
}
/************************************************************************/
/* GDALOverviewMagnitudeCorrection() */
/* */
/* Correct the mean and standard deviation of the overviews of */
/* the given band to match the base layer approximately. */
/************************************************************************/
CPLErr
GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand,
int nOverviewCount,
GDALRasterBandH *pahOverviews,
GDALProgressFunc pfnProgress,
void *pProgressData )
{
VALIDATE_POINTER1( hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure );
CPLErr eErr;
double dfOrigMean, dfOrigStdDev;
/* -------------------------------------------------------------------- */
/* Compute mean/stddev for source raster. */
/* -------------------------------------------------------------------- */
eErr = GDALComputeBandStats( hBaseBand, 2, &dfOrigMean, &dfOrigStdDev,
pfnProgress, pProgressData );
if( eErr != CE_None )
return eErr;
/* -------------------------------------------------------------------- */
/* Loop on overview bands. */
/* -------------------------------------------------------------------- */
int iOverview;
for( iOverview = 0; iOverview < nOverviewCount; iOverview++ )
{
GDALRasterBand *poOverview = (GDALRasterBand *)pahOverviews[iOverview];
double dfOverviewMean, dfOverviewStdDev;
double dfGain;
eErr = GDALComputeBandStats( pahOverviews[iOverview], 1,
&dfOverviewMean, &dfOverviewStdDev,
pfnProgress, pProgressData );
if( eErr != CE_None )
return eErr;
if( dfOrigStdDev < 0.0001 )
dfGain = 1.0;
else
dfGain = dfOrigStdDev / dfOverviewStdDev;
/* -------------------------------------------------------------------- */
/* Apply gain and offset. */
/* -------------------------------------------------------------------- */
GDALDataType eWrkType, eType = poOverview->GetRasterDataType();
int iLine, nWidth, nHeight, bComplex;
float *pafData;
nWidth = poOverview->GetXSize();
nHeight = poOverview->GetYSize();
bComplex = GDALDataTypeIsComplex(eType);
if( bComplex )
{
pafData = (float *) VSIMalloc2(nWidth, 2 * sizeof(float));
eWrkType = GDT_CFloat32;
}
else
{
pafData = (float *) VSIMalloc2(nWidth, sizeof(float));
eWrkType = GDT_Float32;
}
if( pafData == NULL )
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"GDALOverviewMagnitudeCorrection: Out of memory for buffer." );
return CE_Failure;
}
for( iLine = 0; iLine < nHeight; iLine++ )
{
int iPixel;
if( !pfnProgress( iLine / (double) nHeight,
NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
CPLFree( pafData );
return CE_Failure;
}
poOverview->RasterIO( GF_Read, 0, iLine, nWidth, 1,
pafData, nWidth, 1, eWrkType,
0, 0 );
for( iPixel = 0; iPixel < nWidth; iPixel++ )
{
if( bComplex )
{
pafData[iPixel*2] *= (float) dfGain;
pafData[iPixel*2+1] *= (float) dfGain;
}
else
{
pafData[iPixel] = (float)
((pafData[iPixel]-dfOverviewMean)*dfGain + dfOrigMean);
}
}
poOverview->RasterIO( GF_Write, 0, iLine, nWidth, 1,
pafData, nWidth, 1, eWrkType,
0, 0 );
}
if( !pfnProgress( 1.0, NULL, pProgressData ) )
{
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
CPLFree( pafData );
return CE_Failure;
}
CPLFree( pafData );
}
return CE_None;
}