mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
299 lines
11 KiB
C++
299 lines
11 KiB
C++
/******************************************************************************
|
|
* $Id$
|
|
*
|
|
* Project: GDAL
|
|
* Purpose: GDAL Wrapper for image matching via corellation algorithm.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
* Author: Andrew Migal, migal.drew@gmail.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2012, 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_alg.h"
|
|
#include "gdal_simplesurf.h"
|
|
|
|
CPL_CVSID("$Id");
|
|
|
|
/**
|
|
* @file
|
|
* @author Andrew Migal migal.drew@gmail.com
|
|
* @brief Algorithms for searching corresponding points on images.
|
|
* @details This implementation is based on an simplified version
|
|
* of SURF algorithm (Speeded Up Robust Features).
|
|
* Provides capability for detection feature points
|
|
* and finding equal points on different images.
|
|
* As original, this realization is scale invariant, but sensitive to rotation.
|
|
* Images should have similar rotation angles (maximum difference is up to 10-15 degrees),
|
|
* otherwise algorithm produces incorrect and very unstable results.
|
|
*/
|
|
|
|
/**
|
|
* Detect feature points on provided image. Please carefully read documentation below.
|
|
*
|
|
* @param poDataset Image on which feature points will be detected
|
|
* @param panBands Array of 3 raster bands numbers, for Red, Green, Blue bands (in that order)
|
|
* @param nOctaveStart Number of bottom octave. Octave numbers starts from one.
|
|
* This value directly and strongly affects to amount of recognized points
|
|
* @param nOctaveEnd Number of top octave. Should be equal or greater than octaveStart
|
|
* @param dfThreshold Value from 0 to 1. Threshold for feature point recognition.
|
|
* Number of detected points is larger if threshold is lower
|
|
*
|
|
* @see GDALFeaturePoint, GDALSimpleSURF class for detailes.
|
|
*
|
|
* @note Every octave finds points in specific size. For small images
|
|
* use small octave numbers, for high resolution - large.
|
|
* For 1024x1024 images it's normal to use any octave numbers from range 1-6.
|
|
* (for example, octave start - 1, octave end - 3, or octave start - 2, octave end - 2.)
|
|
* For larger images, try 1-10 range or even higher.
|
|
* Pay attention that number of detected point decreases quickly per octave
|
|
* for particular image. Algorithm finds more points in case of small octave number.
|
|
* If method detects nothing, reduce octave start value.
|
|
* In addition, if many feature points are required (the largest possible amount),
|
|
* use the lowest octave start value (1) and wide octave range.
|
|
*
|
|
* @note Typical threshold's value is 0,001. It's pretty good for all images.
|
|
* But this value depends on image's nature and may be various in each particular case.
|
|
* For example, value can be 0,002 or 0,005.
|
|
* Notice that number of detected points is larger if threshold is lower.
|
|
* But with high threshold feature points will be better - "stronger", more "unique" and distinctive.
|
|
*
|
|
* Feel free to experiment with parameters, because character, robustness and number of points
|
|
* entirely depend on provided range of octaves and threshold.
|
|
*
|
|
* NOTICE that every octave requires time to compute. Use a little range
|
|
* or only one octave, if execution time is significant.
|
|
*
|
|
* @return CE_None or CE_Failure if error occurs.
|
|
*/
|
|
|
|
static std::vector<GDALFeaturePoint> *
|
|
GatherFeaturePoints(GDALDataset* poDataset, int* panBands,
|
|
int nOctaveStart, int nOctaveEnd, double dfThreshold)
|
|
{
|
|
if (poDataset == NULL)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "GDALDataset isn't specified");
|
|
return NULL;
|
|
}
|
|
|
|
if (panBands == NULL)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Raster bands are not specified");
|
|
return NULL;
|
|
}
|
|
|
|
if (nOctaveStart <= 0 || nOctaveEnd < 0 ||
|
|
nOctaveStart > nOctaveEnd)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Octave numbers are invalid");
|
|
return NULL;
|
|
}
|
|
|
|
if (dfThreshold < 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Threshold have to be greater than zero");
|
|
return NULL;
|
|
}
|
|
|
|
GDALRasterBand *poRstRedBand = poDataset->GetRasterBand(panBands[0]);
|
|
GDALRasterBand *poRstGreenBand = poDataset->GetRasterBand(panBands[1]);
|
|
GDALRasterBand *poRstBlueBand = poDataset->GetRasterBand(panBands[2]);
|
|
|
|
int nWidth = poRstRedBand->GetXSize();
|
|
int nHeight = poRstRedBand->GetYSize();
|
|
|
|
// Allocate memory for grayscale image
|
|
double **padfImg = NULL;
|
|
padfImg = new double*[nHeight];
|
|
for (int i = 0; i < nHeight; i++)
|
|
padfImg[i] = new double[nWidth];
|
|
|
|
// Create grayscale image
|
|
GDALSimpleSURF::ConvertRGBToLuminosity(
|
|
poRstRedBand, poRstGreenBand, poRstBlueBand, nWidth, nHeight,
|
|
padfImg, nHeight, nWidth);
|
|
|
|
// Prepare integral image
|
|
GDALIntegralImage *poImg = new GDALIntegralImage();
|
|
poImg->Initialize((const double**)padfImg, nHeight, nWidth);
|
|
|
|
// Get feature points
|
|
GDALSimpleSURF *poSurf = new GDALSimpleSURF(nOctaveStart, nOctaveEnd);
|
|
|
|
std::vector<GDALFeaturePoint> *poCollection =
|
|
poSurf->ExtractFeaturePoints(poImg, dfThreshold);
|
|
|
|
// Clean up
|
|
delete poImg;
|
|
delete poSurf;
|
|
|
|
for (int i = 0; i < nHeight; i++)
|
|
delete[] padfImg[i];
|
|
|
|
delete[] padfImg;
|
|
|
|
return poCollection;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALComputeMatchingPoints() */
|
|
/************************************************************************/
|
|
|
|
GDAL_GCP CPL_DLL *
|
|
GDALComputeMatchingPoints( GDALDatasetH hFirstImage,
|
|
GDALDatasetH hSecondImage,
|
|
char **papszOptions,
|
|
int *pnGCPCount )
|
|
{
|
|
*pnGCPCount = 0;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Override default algorithm parameters. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nOctaveStart, nOctaveEnd;
|
|
double dfSURFThreshold;
|
|
double dfMatchingThreshold = 0.015;
|
|
|
|
nOctaveStart =atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_START", "2"));
|
|
nOctaveEnd = atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_END", "2"));
|
|
|
|
dfSURFThreshold = CPLAtof(
|
|
CSLFetchNameValueDef(papszOptions, "SURF_THRESHOLD", "0.001"));
|
|
dfMatchingThreshold = CPLAtof(
|
|
CSLFetchNameValueDef(papszOptions, "MATCHING_THRESHOLD", "0.015"));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Identify the bands to use. For now we are effectively */
|
|
/* limited to using RGB input so if we have one band only treat */
|
|
/* it as red=green=blue=band 1. Disallow non eightbit imagery. */
|
|
/* -------------------------------------------------------------------- */
|
|
int anBandMap1[3], anBandMap2[3];
|
|
|
|
if( GDALGetRasterCount(hFirstImage) >= 3 )
|
|
{
|
|
anBandMap1[0] = 1;
|
|
anBandMap1[1] = 2;
|
|
anBandMap1[2] = 3;
|
|
}
|
|
else
|
|
{
|
|
anBandMap1[0] = anBandMap1[1] = anBandMap1[2] = 1;
|
|
}
|
|
|
|
if( GDALGetRasterCount(hSecondImage) >= 3 )
|
|
{
|
|
anBandMap2[0] = 1;
|
|
anBandMap2[1] = 2;
|
|
anBandMap2[2] = 3;
|
|
}
|
|
else
|
|
{
|
|
anBandMap2[0] = anBandMap2[1] = anBandMap2[2] = 1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect reference points on each image. */
|
|
/* -------------------------------------------------------------------- */
|
|
std::vector<GDALFeaturePoint> *poFPCollection1 =
|
|
GatherFeaturePoints((GDALDataset *) hFirstImage, anBandMap1,
|
|
nOctaveStart, nOctaveEnd, dfSURFThreshold);
|
|
if( poFPCollection1 == NULL )
|
|
return NULL;
|
|
|
|
std::vector<GDALFeaturePoint> *poFPCollection2 =
|
|
GatherFeaturePoints((GDALDataset *) hSecondImage, anBandMap2,
|
|
nOctaveStart, nOctaveEnd,
|
|
dfSURFThreshold);
|
|
|
|
if( poFPCollection2 == NULL )
|
|
return NULL;
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to find corresponding locations. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr;
|
|
std::vector<GDALFeaturePoint *> oMatchPairs;
|
|
|
|
eErr = GDALSimpleSURF::MatchFeaturePoints(
|
|
&oMatchPairs, poFPCollection1, poFPCollection2,
|
|
dfMatchingThreshold );
|
|
|
|
if( eErr != CE_None )
|
|
return NULL;
|
|
|
|
|
|
*pnGCPCount = oMatchPairs.size() / 2;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Translate these into GCPs - but with the output coordinate */
|
|
/* system being pixel/line on the second image. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDAL_GCP *pasGCPList = (GDAL_GCP*) CPLCalloc(*pnGCPCount, sizeof(GDAL_GCP));
|
|
|
|
GDALInitGCPs(*pnGCPCount, pasGCPList);
|
|
|
|
for (int i=0; i < *pnGCPCount; i++)
|
|
{
|
|
GDALFeaturePoint *poPoint1 = oMatchPairs[i*2 ];
|
|
GDALFeaturePoint *poPoint2 = oMatchPairs[i*2+1];
|
|
|
|
pasGCPList[i].dfGCPPixel = poPoint1->GetX() + 0.5;
|
|
pasGCPList[i].dfGCPLine = poPoint1->GetY() + 0.5;
|
|
|
|
pasGCPList[i].dfGCPX = poPoint2->GetX() + 0.5;
|
|
pasGCPList[i].dfGCPY = poPoint2->GetY() + 0.5;
|
|
pasGCPList[i].dfGCPZ = 0.0;
|
|
}
|
|
|
|
// Cleanup the feature point lists.
|
|
delete poFPCollection1;
|
|
delete poFPCollection2;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Optionally transform into the georef coordinates of the */
|
|
/* output image. */
|
|
/* -------------------------------------------------------------------- */
|
|
int bGeorefOutput =
|
|
CSLTestBoolean(CSLFetchNameValueDef(papszOptions,"OUTPUT_GEOREF","NO"));
|
|
|
|
if( bGeorefOutput )
|
|
{
|
|
double adfGeoTransform[6];
|
|
|
|
GDALGetGeoTransform( hSecondImage, adfGeoTransform );
|
|
|
|
for (int i=0; i < *pnGCPCount; i++)
|
|
{
|
|
GDALApplyGeoTransform(adfGeoTransform,
|
|
pasGCPList[i].dfGCPX,
|
|
pasGCPList[i].dfGCPY,
|
|
&(pasGCPList[i].dfGCPX),
|
|
&(pasGCPList[i].dfGCPY));
|
|
}
|
|
}
|
|
|
|
return pasGCPList;
|
|
}
|