ultimatepp/bazaar/plugin/gdal/alg/gdal_tps.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

403 lines
15 KiB
C++

/******************************************************************************
* $Id: gdal_tps.cpp 29207 2015-05-18 17:23:45Z mloskot $
*
* Project: High Performance Image Reprojector
* Purpose: Thin Plate Spline transformer (GDAL wrapper portion)
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
* Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* 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 "thinplatespline.h"
#include "gdal_alg.h"
#include "gdal_alg_priv.h"
#include "gdal_priv.h"
#include "cpl_conv.h"
#include "cpl_string.h"
#include "cpl_atomic_ops.h"
#include "cpl_multiproc.h"
CPL_CVSID("$Id: gdal_tps.cpp 29207 2015-05-18 17:23:45Z mloskot $");
CPL_C_START
CPLXMLNode *GDALSerializeTPSTransformer( void *pTransformArg );
void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree );
CPL_C_END
typedef struct
{
GDALTransformerInfo sTI;
VizGeorefSpline2D *poForward;
VizGeorefSpline2D *poReverse;
int bForwardSolved;
int bReverseSolved;
int bReversed;
int nGCPCount;
GDAL_GCP *pasGCPList;
volatile int nRefCount;
} TPSTransformInfo;
/************************************************************************/
/* GDALCreateSimilarTPSTransformer() */
/************************************************************************/
static
void* GDALCreateSimilarTPSTransformer( void *hTransformArg, double dfRatioX, double dfRatioY )
{
VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarTPSTransformer", NULL );
TPSTransformInfo *psInfo = (TPSTransformInfo *) hTransformArg;
if( dfRatioX == 1.0 && dfRatioY == 1.0 )
{
/* We can just use a ref count, since using the source transformation */
/* is thread-safe */
CPLAtomicInc(&(psInfo->nRefCount));
}
else
{
GDAL_GCP *pasGCPList = GDALDuplicateGCPs( psInfo->nGCPCount,
psInfo->pasGCPList );
for(int i=0;i<psInfo->nGCPCount;i++)
{
pasGCPList[i].dfGCPPixel /= dfRatioX;
pasGCPList[i].dfGCPLine /= dfRatioY;
}
psInfo = (TPSTransformInfo *) GDALCreateTPSTransformer( psInfo->nGCPCount, pasGCPList,
psInfo->bReversed );
GDALDeinitGCPs( psInfo->nGCPCount, pasGCPList );
CPLFree( pasGCPList );
}
return psInfo;
}
/************************************************************************/
/* GDALCreateTPSTransformer() */
/************************************************************************/
/**
* Create Thin Plate Spline transformer from GCPs.
*
* The thin plate spline transformer produces exact transformation
* at all control points and smoothly varying transformations between
* control points with greatest influence from local control points.
* It is suitable for for many applications not well modelled by polynomial
* transformations.
*
* Creating the TPS transformer involves solving systems of linear equations
* related to the number of control points involved. This solution is
* computed within this function call. It can be quite an expensive operation
* for large numbers of GCPs. For instance, for reference, it takes on the
* order of 10s for 400 GCPs on a 2GHz Athlon processor.
*
* TPS Transformers are serializable.
*
* The GDAL Thin Plate Spline transformer is based on code provided by
* Gilad Ronnen on behalf of VIZRT Inc (http://www.visrt.com). Incorporation
* of the algorithm into GDAL was supported by the Centro di Ecologia Alpina
* (http://www.cealp.it).
*
* @param nGCPCount the number of GCPs in pasGCPList.
* @param pasGCPList an array of GCPs to be used as input.
* @param bReversed set it to TRUE to compute the reversed transformation.
*
* @return the transform argument or NULL if creation fails.
*/
void *GDALCreateTPSTransformer( int nGCPCount, const GDAL_GCP *pasGCPList,
int bReversed )
{
return GDALCreateTPSTransformerInt(nGCPCount, pasGCPList, bReversed, NULL);
}
static void GDALTPSComputeForwardInThread(void* pData)
{
TPSTransformInfo *psInfo = (TPSTransformInfo *)pData;
psInfo->bForwardSolved = psInfo->poForward->solve() != 0;
}
void *GDALCreateTPSTransformerInt( int nGCPCount, const GDAL_GCP *pasGCPList,
int bReversed, char** papszOptions )
{
TPSTransformInfo *psInfo;
int iGCP;
/* -------------------------------------------------------------------- */
/* Allocate transform info. */
/* -------------------------------------------------------------------- */
psInfo = (TPSTransformInfo *) CPLCalloc(sizeof(TPSTransformInfo),1);
psInfo->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList );
psInfo->nGCPCount = nGCPCount;
psInfo->bReversed = bReversed;
psInfo->poForward = new VizGeorefSpline2D( 2 );
psInfo->poReverse = new VizGeorefSpline2D( 2 );
memcpy( psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) );
psInfo->sTI.pszClassName = "GDALTPSTransformer";
psInfo->sTI.pfnTransform = GDALTPSTransform;
psInfo->sTI.pfnCleanup = GDALDestroyTPSTransformer;
psInfo->sTI.pfnSerialize = GDALSerializeTPSTransformer;
psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarTPSTransformer;
/* -------------------------------------------------------------------- */
/* Attach all the points to the transformation. */
/* -------------------------------------------------------------------- */
for( iGCP = 0; iGCP < nGCPCount; iGCP++ )
{
double afPL[2], afXY[2];
afPL[0] = pasGCPList[iGCP].dfGCPPixel;
afPL[1] = pasGCPList[iGCP].dfGCPLine;
afXY[0] = pasGCPList[iGCP].dfGCPX;
afXY[1] = pasGCPList[iGCP].dfGCPY;
if( bReversed )
{
psInfo->poReverse->add_point( afPL[0], afPL[1], afXY );
psInfo->poForward->add_point( afXY[0], afXY[1], afPL );
}
else
{
psInfo->poForward->add_point( afPL[0], afPL[1], afXY );
psInfo->poReverse->add_point( afXY[0], afXY[1], afPL );
}
}
psInfo->nRefCount = 1;
int nThreads = 1;
if( nGCPCount > 100 )
{
const char* pszWarpThreads = CSLFetchNameValue(papszOptions, "NUM_THREADS");
if (pszWarpThreads == NULL)
pszWarpThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "1");
if (EQUAL(pszWarpThreads, "ALL_CPUS"))
nThreads = CPLGetNumCPUs();
else
nThreads = atoi(pszWarpThreads);
}
if( nThreads > 1 )
{
/* Compute direct and reverse transforms in parallel */
CPLJoinableThread* hThread = CPLCreateJoinableThread(GDALTPSComputeForwardInThread, psInfo);
psInfo->bReverseSolved = psInfo->poReverse->solve() != 0;
if( hThread != NULL )
CPLJoinThread(hThread);
else
psInfo->bForwardSolved = psInfo->poForward->solve() != 0;
}
else
{
psInfo->bForwardSolved = psInfo->poForward->solve() != 0;
psInfo->bReverseSolved = psInfo->poReverse->solve() != 0;
}
if( !psInfo->bForwardSolved || !psInfo->bReverseSolved )
{
GDALDestroyTPSTransformer(psInfo);
return NULL;
}
return psInfo;
}
/************************************************************************/
/* GDALDestroyTPSTransformer() */
/************************************************************************/
/**
* Destroy TPS transformer.
*
* This function is used to destroy information about a GCP based
* polynomial transformation created with GDALCreateTPSTransformer().
*
* @param pTransformArg the transform arg previously returned by
* GDALCreateTPSTransformer().
*/
void GDALDestroyTPSTransformer( void *pTransformArg )
{
if( pTransformArg == NULL )
return;
TPSTransformInfo *psInfo = (TPSTransformInfo *) pTransformArg;
if( CPLAtomicDec(&(psInfo->nRefCount)) == 0 )
{
delete psInfo->poForward;
delete psInfo->poReverse;
GDALDeinitGCPs( psInfo->nGCPCount, psInfo->pasGCPList );
CPLFree( psInfo->pasGCPList );
CPLFree( pTransformArg );
}
}
/************************************************************************/
/* GDALTPSTransform() */
/************************************************************************/
/**
* Transforms point based on GCP derived polynomial model.
*
* This function matches the GDALTransformerFunc signature, and can be
* used to transform one or more points from pixel/line coordinates to
* georeferenced coordinates (SrcToDst) or vice versa (DstToSrc).
*
* @param pTransformArg return value from GDALCreateTPSTransformer().
* @param bDstToSrc TRUE if transformation is from the destination
* (georeferenced) coordinates to pixel/line or FALSE when transforming
* from pixel/line to georeferenced coordinates.
* @param nPointCount the number of values in the x, y and z arrays.
* @param x array containing the X values to be transformed.
* @param y array containing the Y values to be transformed.
* @param z array containing the Z values to be transformed.
* @param panSuccess array in which a flag indicating success (TRUE) or
* failure (FALSE) of the transformation are placed.
*
* @return TRUE.
*/
int GDALTPSTransform( void *pTransformArg, int bDstToSrc,
int nPointCount,
double *x, double *y,
CPL_UNUSED double *z,
int *panSuccess )
{
VALIDATE_POINTER1( pTransformArg, "GDALTPSTransform", 0 );
int i;
TPSTransformInfo *psInfo = (TPSTransformInfo *) pTransformArg;
for( i = 0; i < nPointCount; i++ )
{
double xy_out[2];
if( bDstToSrc )
{
psInfo->poReverse->get_point( x[i], y[i], xy_out );
x[i] = xy_out[0];
y[i] = xy_out[1];
}
else
{
psInfo->poForward->get_point( x[i], y[i], xy_out );
x[i] = xy_out[0];
y[i] = xy_out[1];
}
panSuccess[i] = TRUE;
}
return TRUE;
}
/************************************************************************/
/* GDALSerializeTPSTransformer() */
/************************************************************************/
CPLXMLNode *GDALSerializeTPSTransformer( void *pTransformArg )
{
VALIDATE_POINTER1( pTransformArg, "GDALSerializeTPSTransformer", NULL );
CPLXMLNode *psTree;
TPSTransformInfo *psInfo = static_cast<TPSTransformInfo *>(pTransformArg);
psTree = CPLCreateXMLNode( NULL, CXT_Element, "TPSTransformer" );
/* -------------------------------------------------------------------- */
/* Serialize bReversed. */
/* -------------------------------------------------------------------- */
CPLCreateXMLElementAndValue(
psTree, "Reversed",
CPLString().Printf( "%d", psInfo->bReversed ) );
/* -------------------------------------------------------------------- */
/* Attach GCP List. */
/* -------------------------------------------------------------------- */
if( psInfo->nGCPCount > 0 )
{
GDALSerializeGCPListToXML( psTree,
psInfo->pasGCPList,
psInfo->nGCPCount,
NULL );
}
return psTree;
}
/************************************************************************/
/* GDALDeserializeTPSTransformer() */
/************************************************************************/
void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree )
{
GDAL_GCP *pasGCPList = 0;
int nGCPCount = 0;
void *pResult;
int bReversed;
/* -------------------------------------------------------------------- */
/* Check for GCPs. */
/* -------------------------------------------------------------------- */
CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
if( psGCPList != NULL )
{
GDALDeserializeGCPListFromXML( psGCPList,
&pasGCPList,
&nGCPCount,
NULL );
}
/* -------------------------------------------------------------------- */
/* Get other flags. */
/* -------------------------------------------------------------------- */
bReversed = atoi(CPLGetXMLValue(psTree,"Reversed","0"));
/* -------------------------------------------------------------------- */
/* Generate transformation. */
/* -------------------------------------------------------------------- */
pResult = GDALCreateTPSTransformer( nGCPCount, pasGCPList, bReversed );
/* -------------------------------------------------------------------- */
/* Cleanup GCP copy. */
/* -------------------------------------------------------------------- */
GDALDeinitGCPs( nGCPCount, pasGCPList );
CPLFree( pasGCPList );
return pResult;
}