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

522 lines
17 KiB
C++

/******************************************************************************
* Project: GDAL
* Purpose: Correlator - GDALSimpleSURF and GDALFeaturePoint classes.
* Author: Andrew Migal, migal.drew@gmail.com
*
******************************************************************************
* Copyright (c) 2012, Andrew Migal
*
* 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_simplesurf.h"
CPL_CVSID("$Id");
/************************************************************************/
/* ==================================================================== */
/* GDALFeaturePoint */
/* ==================================================================== */
/************************************************************************/
GDALFeaturePoint::GDALFeaturePoint()
{
nX = -1;
nY = -1;
nScale = -1;
nRadius = -1;
nSign = -1;
padfDescriptor = new double[DESC_SIZE];
}
GDALFeaturePoint::GDALFeaturePoint(const GDALFeaturePoint& fp)
{
nX = fp.nX;
nY = fp.nY;
nScale = fp.nScale;
nRadius = fp.nRadius;
nSign = fp.nSign;
padfDescriptor = new double[DESC_SIZE];
for (int i = 0; i < DESC_SIZE; i++)
padfDescriptor[i] = fp.padfDescriptor[i];
}
GDALFeaturePoint::GDALFeaturePoint(int nX, int nY,
int nScale, int nRadius, int nSign)
{
this->nX = nX;
this->nY = nY;
this->nScale = nScale;
this->nRadius = nRadius;
this->nSign = nSign;
this->padfDescriptor = new double[DESC_SIZE];
}
GDALFeaturePoint& GDALFeaturePoint::operator=(const GDALFeaturePoint& point)
{
if (this != &point)
{
nX = point.nX;
nY = point.nY;
nScale = point.nScale;
nRadius = point.nRadius;
nSign = point.nSign;
//Free memory
delete[] padfDescriptor;
//Copy descriptor values
padfDescriptor = new double[DESC_SIZE];
for (int i = 0; i < DESC_SIZE; i++)
padfDescriptor[i] = point.padfDescriptor[i];
}
return *this;
}
int GDALFeaturePoint::GetX() { return nX; }
void GDALFeaturePoint::SetX(int nX) { this->nX = nX; }
int GDALFeaturePoint::GetY() { return nY; }
void GDALFeaturePoint::SetY(int nY) { this->nY = nY; }
int GDALFeaturePoint::GetScale() { return nScale; }
void GDALFeaturePoint::SetScale(int nScale) { this->nScale = nScale; }
int GDALFeaturePoint::GetRadius() { return nRadius; }
void GDALFeaturePoint::SetRadius(int nRadius) { this->nRadius = nRadius; }
int GDALFeaturePoint::GetSign() { return nSign; }
void GDALFeaturePoint::SetSign(int nSign) { this->nSign = nSign; }
double& GDALFeaturePoint::operator [] (int nIndex)
{
if (nIndex < 0 || nIndex >= DESC_SIZE)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Descriptor index is out of range");
}
return padfDescriptor[nIndex];
}
GDALFeaturePoint::~GDALFeaturePoint() {
delete[] padfDescriptor;
}
/************************************************************************/
/* ==================================================================== */
/* GDALSimpleSurf */
/* ==================================================================== */
/************************************************************************/
GDALSimpleSURF::GDALSimpleSURF(int nOctaveStart, int nOctaveEnd)
{
this->octaveStart = nOctaveStart;
this->octaveEnd = nOctaveEnd;
// Initialize Octave map with custom range
poOctMap = new GDALOctaveMap(octaveStart, octaveEnd);
}
CPLErr GDALSimpleSURF::ConvertRGBToLuminosity(
GDALRasterBand *red, GDALRasterBand *green, GDALRasterBand *blue,
int nXSize, int nYSize, double **padfImg, int nHeight, int nWidth)
{
const double forRed = 0.21;
const double forGreen = 0.72;
const double forBlue = 0.07;
if (red == NULL || green == NULL || blue == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Raster bands are not specified");
return CE_Failure;
}
if (nXSize > red->GetXSize() || nYSize > red->GetYSize())
{
CPLError(CE_Failure, CPLE_AppDefined,
"Red band has less size than has been requested");
return CE_Failure;
}
if (padfImg == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined, "Buffer isn't specified");
return CE_Failure;
}
GDALDataType eRedType = red->GetRasterDataType();
GDALDataType eGreenType = green->GetRasterDataType();
GDALDataType eBlueType = blue->GetRasterDataType();
int dataRedSize = GDALGetDataTypeSize(eRedType) / 8;
int dataGreenSize = GDALGetDataTypeSize(eGreenType) / 8;
int dataBlueSize = GDALGetDataTypeSize(eBlueType) / 8;
void *paRedLayer = CPLMalloc(dataRedSize * nWidth * nHeight);
void *paGreenLayer = CPLMalloc(dataGreenSize * nWidth * nHeight);
void *paBlueLayer = CPLMalloc(dataBlueSize * nWidth * nHeight);
red->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paRedLayer, nWidth, nHeight, eRedType, 0, 0, NULL);
green->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paGreenLayer, nWidth, nHeight, eGreenType, 0, 0, NULL);
blue->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paBlueLayer, nWidth, nHeight, eBlueType, 0, 0, NULL);
double maxValue = 255.0;
for (int row = 0; row < nHeight; row++)
for (int col = 0; col < nWidth; col++)
{
// Get RGB values
double dfRedVal = SRCVAL(paRedLayer, eRedType,
nWidth * row + col * dataRedSize);
double dfGreenVal = SRCVAL(paGreenLayer, eGreenType,
nWidth * row + col * dataGreenSize);
double dfBlueVal = SRCVAL(paBlueLayer, eBlueType,
nWidth * row + col * dataBlueSize);
// Compute luminosity value
padfImg[row][col] = (
dfRedVal * forRed +
dfGreenVal * forGreen +
dfBlueVal * forBlue) / maxValue;
}
CPLFree(paRedLayer);
CPLFree(paGreenLayer);
CPLFree(paBlueLayer);
return CE_None;
}
std::vector<GDALFeaturePoint>*
GDALSimpleSURF::ExtractFeaturePoints(GDALIntegralImage *poImg,
double dfThreshold)
{
std::vector<GDALFeaturePoint>* poCollection =
new std::vector<GDALFeaturePoint>();
//Calc Hessian values for layers
poOctMap->ComputeMap(poImg);
//Search for exremum points
for (int oct = octaveStart; oct <= octaveEnd; oct++)
{
for (int k = 0; k < GDALOctaveMap::INTERVALS - 2; k++)
{
GDALOctaveLayer *bot = poOctMap->pMap[oct - 1][k];
GDALOctaveLayer *mid = poOctMap->pMap[oct - 1][k + 1];
GDALOctaveLayer *top = poOctMap->pMap[oct - 1][k + 2];
for (int i = 0; i < mid->height; i++)
{
for (int j = 0; j < mid->width; j++)
{
if (poOctMap->PointIsExtremum(i, j, bot, mid, top, dfThreshold))
{
GDALFeaturePoint oFP(j, i, mid->scale,
mid->radius, mid->signs[i][j]);
SetDescriptor(&oFP, poImg);
poCollection->push_back(oFP);
}
}
}
}
}
return poCollection;
}
double GDALSimpleSURF::GetEuclideanDistance(
GDALFeaturePoint &firstPoint, GDALFeaturePoint &secondPoint)
{
double sum = 0;
for (int i = 0; i < GDALFeaturePoint::DESC_SIZE; i++)
sum += (firstPoint[i] - secondPoint[i]) * (firstPoint[i] - secondPoint[i]);
return sqrt(sum);
}
void GDALSimpleSURF::NormalizeDistances(std::list<MatchedPointPairInfo> *poList)
{
double max = 0;
std::list<MatchedPointPairInfo>::iterator i;
for (i = poList->begin(); i != poList->end(); i++)
if ((*i).euclideanDist > max)
max = (*i).euclideanDist;
if (max != 0)
{
for (i = poList->begin(); i != poList->end(); i++)
(*i).euclideanDist /= max;
}
}
void GDALSimpleSURF::SetDescriptor(
GDALFeaturePoint *poPoint, GDALIntegralImage *poImg)
{
// Affects to the descriptor area
const int haarScale = 20;
// Side of the Haar wavelet
int haarFilterSize = 2 * poPoint->GetScale();
// Length of the side of the descriptor area
int descSide = haarScale * poPoint->GetScale();
// Side of the quadrant in 4x4 grid
int quadStep = descSide / 4;
// Side of the sub-quadrant in 5x5 regular grid of quadrant
int subQuadStep = quadStep / 5;
int leftTop_row = poPoint->GetY() - (descSide / 2);
int leftTop_col = poPoint->GetX() - (descSide / 2);
int count = 0;
for (int r = leftTop_row; r < leftTop_row + descSide; r += quadStep)
for (int c = leftTop_col; c < leftTop_col + descSide; c += quadStep)
{
double dx = 0;
double dy = 0;
double abs_dx = 0;
double abs_dy = 0;
for (int sub_r = r; sub_r < r + quadStep; sub_r += subQuadStep)
for (int sub_c = c; sub_c < c + quadStep; sub_c += subQuadStep)
{
// Approximate center of sub quadrant
int cntr_r = sub_r + subQuadStep / 2;
int cntr_c = sub_c + subQuadStep / 2;
// Left top point for Haar wavelet computation
int cur_r = cntr_r - haarFilterSize / 2;
int cur_c = cntr_c - haarFilterSize / 2;
// Gradients
double cur_dx = poImg->HaarWavelet_X(cur_r, cur_c, haarFilterSize);
double cur_dy = poImg->HaarWavelet_Y(cur_r, cur_c, haarFilterSize);
dx += cur_dx;
dy += cur_dy;
abs_dx += fabs(cur_dx);
abs_dy += fabs(cur_dy);
}
// Fills point's descriptor
(*poPoint)[count++] = dx;
(*poPoint)[count++] = dy;
(*poPoint)[count++] = abs_dx;
(*poPoint)[count++] = abs_dy;
}
}
/**
* Find corresponding points (equal points in two collections).
*
* @param poMatched Resulting collection for matched points
* @param poFirstCollection Points on the first image
* @param poSecondCollection Points on the second image
* @param dfThreshold Value from 0 to 1. Threshold affects to number of
* matched points. If threshold is higher, amount of corresponding
* points is larger, and vice versa
*
* @note Typical threshold's value is 0,1. BUT it's a very approximate guide.
* It can be 0,001 or even 1. This threshold provides direct adjustment
* of point matching.
* NOTICE that if threshold is lower, matches are more robust and correct, but number of
* matched points is smaller. Therefore if algorithm performs many false
* detections and produces bad results, reduce threshold.
* Otherwise, if algorithm finds nothing, increase threshold.
*
* @return CE_None or CE_Failure if error occurs.
*/
CPLErr GDALSimpleSURF::MatchFeaturePoints(
std::vector<GDALFeaturePoint*> *poMatchPairs,
std::vector<GDALFeaturePoint> *poFirstCollect,
std::vector<GDALFeaturePoint> *poSecondCollect,
double dfThreshold)
{
/* -------------------------------------------------------------------- */
/* Validate parameters. */
/* -------------------------------------------------------------------- */
if (poMatchPairs == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Matched points colection isn't specified");
return CE_Failure;
}
if (poFirstCollect == NULL || poSecondCollect == NULL)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Feature point collections are not specified");
return CE_Failure;
}
/* ==================================================================== */
/* Matching algorithm. */
/* ==================================================================== */
// Affects to false matching pruning
const double ratioThreshold = 0.8;
int len_1 = poFirstCollect->size();
int len_2 = poSecondCollect->size();
int minLength = (len_1 < len_2) ? len_1 : len_2;
// Temporary pointers. Used to swap collections
std::vector<GDALFeaturePoint> *p_1;
std::vector<GDALFeaturePoint> *p_2;
bool isSwap = false;
// Assign p_1 - collection with minimal number of points
if (minLength == len_2)
{
p_1 = poSecondCollect;
p_2 = poFirstCollect;
int tmp = 0;
tmp = len_1;
len_1 = len_2;
len_2 = tmp;
isSwap = true;
}
else
{
// Assignment 'as is'
p_1 = poFirstCollect;
p_2 = poSecondCollect;
isSwap = false;
}
// Stores matched point indexes and
// their euclidean distances
std::list<MatchedPointPairInfo> *poPairInfoList =
new std::list<MatchedPointPairInfo>();
// Flags that points in the 2nd collection are matched or not
bool *alreadyMatched = new bool[len_2];
for (int i = 0; i < len_2; i++)
alreadyMatched[i] = false;
for (int i = 0; i < len_1; i++)
{
// Distance to the nearest point
double bestDist = -1;
// Index of the nearest point in p_2 collection
int bestIndex = -1;
// Distance to the 2nd nearest point
double bestDist_2 = -1;
// Find the nearest and 2nd nearest points
for (int j = 0; j < len_2; j++)
if (!alreadyMatched[j])
if (p_1->at(i).GetSign() ==
p_2->at(j).GetSign())
{
// Get distance between two feature points
double curDist = GetEuclideanDistance(
p_1->at(i), p_2->at(j));
if (bestDist == -1)
{
bestDist = curDist;
bestIndex = j;
}
else
{
if (curDist < bestDist)
{
bestDist = curDist;
bestIndex = j;
}
}
// Findes the 2nd nearest point
if (bestDist_2 < 0)
bestDist_2 = curDist;
else
if (curDist > bestDist && curDist < bestDist_2)
bestDist_2 = curDist;
}
/* -------------------------------------------------------------------- */
/* False matching pruning. */
/* If ratio bestDist to bestDist_2 greater than 0.8 => */
/* consider as false detection. */
/* Otherwise, add points as matched pair. */
/*----------------------------------------------------------------------*/
if (bestDist_2 > 0 && bestDist >= 0)
if (bestDist / bestDist_2 < ratioThreshold)
{
MatchedPointPairInfo info(i, bestIndex, bestDist);
poPairInfoList->push_back(info);
alreadyMatched[bestIndex] = true;
}
}
/* -------------------------------------------------------------------- */
/* Pruning based on the provided threshold */
/* -------------------------------------------------------------------- */
NormalizeDistances(poPairInfoList);
std::list<MatchedPointPairInfo>::const_iterator iter;
for (iter = poPairInfoList->begin(); iter != poPairInfoList->end(); iter++)
{
if ((*iter).euclideanDist <= dfThreshold)
{
int i_1 = (*iter).ind_1;
int i_2 = (*iter).ind_2;
// Add copies into MatchedCollection
if(!isSwap)
{
poMatchPairs->push_back( &(p_1->at(i_1)) );
poMatchPairs->push_back( &(p_2->at(i_2)) );
}
else
{
poMatchPairs->push_back( &(p_2->at(i_2)) );
poMatchPairs->push_back( &(p_1->at(i_1)) );
}
}
}
// Clean up
delete[] alreadyMatched;
return CE_None;
}
GDALSimpleSURF::~GDALSimpleSURF()
{
delete poOctMap;
}