KeyPointDetector.cpp
Go to the documentation of this file.
00001 /*
00002 * This file is part of Parallel SURF, which implements the SURF algorithm
00003 * using multi-threading.
00004 *
00005 * Copyright (C) 2010 David Gossow
00006 *
00007 * It is based on the SURF implementation included in Pan-o-matic 0.9.4,
00008 * written by Anael Orlinski.
00009 *
00010 * Parallel SURF is free software; you can redistribute it and/or modify
00011 * it under the terms of the GNU General Public License as published by
00012 * the Free Software Foundation; either version 3 of the License, or
00013 * (at your option) any later version.
00014 *
00015 * Parallel SURF is distributed in the hope that it will be useful,
00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 * GNU General Public License for more details.
00019 *
00020 * You should have received a copy of the GNU General Public License
00021 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 */
00023 
00024 #include <iostream>
00025 #include <cfloat>
00026 
00027 #include <boost/thread/thread.hpp>
00028 
00029 #include <stdlib.h>
00030 
00031 #include "KeyPoint.h"
00032 #include "KeyPointDetector.h"
00033 #include "BoxFilter.h"
00034 #include "MathStuff.h"
00035 
00036 #include "threadpool.hpp"
00037 
00038 using namespace parallelsurf;
00039 
00040 const double parallelsurf::KeyPointDetector::kBaseSigma = 1.2;
00041 
00042 KeyPointDetector::KeyPointDetector ( Image& iImage, boost::threadpool::pool &iThreadPool ) : _image( iImage ), _threadPool ( iThreadPool )
00043 {
00044   // initialize default values
00045   _maxScales = 5;  // number of scales per octave (9x9, 15x15, 21x21, 27x27, ...)
00046   _maxOctaves = 4; // number of octaves
00047 
00048   _scoreThreshold = 0.2;
00049 
00050   _initialBoxFilterSize = 3;
00051   _scaleOverlap = 3;
00052 }
00053 
00054 
00055 void KeyPointDetector::detectKeyPoints ( KeyPointInsertor& iInsertor )
00056 {
00057   // allocate lots of memory for the scales
00058   double *** aSH = new double**[_maxScales];
00059 
00060   for ( int s = 0; s < _maxScales; ++s )
00061     aSH[s] = Image::AllocateImage ( _image.getWidth(), _image.getHeight() );
00062 
00063   // init the border size
00064   int * aBorderSize = new int[_maxScales];
00065 
00066   boost::mutex aInsertorMutex;
00067 
00068   // go through all the octaves
00069   for ( int o = 0; o < _maxOctaves; ++o )
00070   {
00071     // calculate the pixel step on the image, and the image size
00072     int aPixelStep = 1 << o; // 2^aOctaveIt
00073     int aOctaveWidth = _image.getWidth() / aPixelStep; // integer division
00074     int aOctaveHeight = _image.getHeight() / aPixelStep; // integer division
00075 
00076     // calculate the border sizes
00077     for ( int s = 0; s < _maxScales; ++s )
00078     {
00079       aBorderSize[s] = getBorderSize ( o, s );
00080     }
00081 
00082     ComputeHelper helper = { _image, aSH, o, aOctaveWidth, aOctaveHeight, aPixelStep, aBorderSize, _scoreThreshold, _initialBoxFilterSize, _maxScales, aInsertorMutex };
00083 
00084     // fill scale matrices
00085     for ( int s = 0; s < _maxScales; ++s )
00086     {
00087       _threadPool.schedule ( boost::bind ( &ComputeHelper::calcDet, boost::ref ( helper ), s, getFilterSize ( o, s ) ) );
00088     }
00089     _threadPool.wait();
00090 
00091     // detect the feature points with a 3x3x3 neighborhood non-maxima suppression
00092     for ( int s = 1; s < ( _maxScales - 1 ); s += 2 )
00093     {
00094       _threadPool.schedule ( boost::bind ( &ComputeHelper::detect, boost::ref ( helper ), s, boost::ref ( iInsertor ) ) );
00095     }
00096     _threadPool.wait();
00097   }
00098   
00099   // deallocate memory of the scale images
00100   for ( int s = 0; s < _maxScales; ++s )
00101     Image::DeallocateImage ( aSH[s], _image.getHeight() );
00102 }
00103 
00104 bool KeyPointDetector::ComputeHelper::fineTuneExtrema ( double *** iSH, int iX, int iY, int iS,
00105     double& oX, double& oY, double& oS, double& oScore,
00106     int iOctaveWidth, int iOctaveHeight, int iBorder )
00107 {
00108   // maximum fine tune iterations
00109   const int kMaxFineTuneIters = 6;
00110 
00111   // shift from the initial position for X and Y (only -1 or + 1 during the iterations).
00112   int aX = iX;
00113   int aY = iY;
00114   int aS = iS;
00115 
00116   int aShiftX = 0;
00117   int aShiftY = 0;
00118 
00119   // current deviations
00120   double aDx = 0, aDy = 0, aDs = 0;
00121 
00122   //result vector
00123   double aV[3]; //(x,y,s)
00124 
00125   for ( int aIter = 0; aIter < kMaxFineTuneIters; ++aIter )
00126   {
00127     // update the extrema position
00128     aX += aShiftX;
00129     aY += aShiftY;
00130 
00131     // create the problem matrix
00132     double aM[3][3]; //symetric, no ordering problem.
00133 
00134     // fill the result vector with gradient from pixels differences (negate to prepare system solve)
00135     aDx = ( iSH[aS  ][aY  ][aX+1] - iSH[aS  ][aY  ][aX-1] ) * 0.5;
00136     aDy = ( iSH[aS  ][aY+1][aX  ] - iSH[aS  ][aY-1][aX  ] ) * 0.5;
00137     aDs = ( iSH[aS+1][aY  ][aX  ] - iSH[aS-1][aY  ][aX  ] ) * 0.5;
00138 
00139     aV[0] = - aDx;
00140     aV[1] = - aDy;
00141     aV[2] = - aDs;
00142 
00143     // fill the matrix with values of the hessian from pixel differences
00144     aM[0][0] = iSH[aS  ][aY  ][aX-1] - 2.0 * iSH[aS][aY][aX] + iSH[aS  ][aY  ][aX+1];
00145     aM[1][1] = iSH[aS  ][aY-1][aX  ] - 2.0 * iSH[aS][aY][aX] + iSH[aS  ][aY+1][aX  ];
00146     aM[2][2] = iSH[aS-1][aY  ][aX  ] - 2.0 * iSH[aS][aY][aX] + iSH[aS+1][aY  ][aX  ];
00147 
00148     aM[0][1] = aM[1][0] = ( iSH[aS  ][aY+1][aX+1] + iSH[aS  ][aY-1][aX-1] - iSH[aS  ][aY+1][aX-1] - iSH[aS  ][aY-1][aX+1] ) * 0.25;
00149     aM[0][2] = aM[2][0] = ( iSH[aS+1][aY  ][aX+1] + iSH[aS-1][aY  ][aX-1] - iSH[aS+1][aY  ][aX-1] - iSH[aS-1][aY  ][aX+1] ) * 0.25;
00150     aM[1][2] = aM[2][1] = ( iSH[aS+1][aY+1][aX  ] + iSH[aS-1][aY-1][aX  ] - iSH[aS+1][aY-1][aX  ] - iSH[aS-1][aY+1][aX  ] ) * 0.25;
00151 
00152     // solve the linear system. results are in aV. exit with error if a problem happened
00153     if ( !Math::SolveLinearSystem33 ( aV, aM ) )
00154     {
00155       return false;
00156     }
00157 
00158     // ajust the shifts with the results and stop if no significant change
00159     if ( aIter < kMaxFineTuneIters - 1 )
00160     {
00161       aShiftX = 0;
00162       aShiftY = 0;
00163 
00164       if ( aV[0] > 0.6 && aX < ( int ) ( iOctaveWidth - iBorder - 2 ) )
00165         aShiftX++;
00166       else if ( aV[0] < -0.6 && aX > ( int ) iBorder + 1 )
00167         aShiftX--;
00168 
00169       if ( aV[1] > 0.6 && aY < ( int ) ( iOctaveHeight - iBorder - 2 ) )
00170         aShiftY++;
00171       else if ( aV[1] < -0.6 && aY > ( int ) iBorder + 1 )
00172         aShiftY--;
00173 
00174       if ( aShiftX == 0 && aShiftY == 0 )
00175         break;
00176     }
00177   }
00178 
00179   // update the score
00180   oScore = iSH[aS][aY][aX] + 0.5 * ( aDx * aV[0] + aDy * aV[1] + aDs * aV[2] );
00181 
00182   // reject too big deviation in last step (unfinished job).
00183   if ( Math::Abs ( aV[0] ) > 1.5 || Math::Abs ( aV[1] ) > 1.5  || Math::Abs ( aV[2] ) > 1.5 )
00184     return false;
00185 
00186   // put the last deviation (not integer :) to the output
00187   oX = aX + aV[0];
00188 
00189   oY = aY + aV[1];
00190 
00191   oS = iS + aV[2];
00192 
00193   return true;
00194 }
00195 
00196 int  KeyPointDetector::getFilterSize ( int iOctave, int iScale )
00197 {
00198   // base size + 3 times first increment for step back
00199   // for the first octave 9x9, 15x15, 21x21, 27x27, 33x33
00200   // for the second 21x21, 33x33, 45x45 ...
00201   int aScaleShift = 2 << iOctave;
00202   return _initialBoxFilterSize + ( aScaleShift - 2 ) * ( _maxScales - _scaleOverlap ) + aScaleShift * iScale;
00203 }
00204 
00205 int  KeyPointDetector::getBorderSize ( int iOctave, int iScale )
00206 {
00207   int aScaleShift = 2 << iOctave;
00208 
00209   if ( iScale <= 2 )
00210   {
00211     int aMult = ( iOctave == 0 ? 1 : 2 );
00212     return ( getFilterSize ( iOctave, 1 ) + aMult * aScaleShift ) * 3 / aScaleShift + 1;
00213   }
00214 
00215   return getFilterSize ( iOctave, iScale ) * 3 / aScaleShift + 1;
00216 }
00217 
00218 bool KeyPointDetector::ComputeHelper::calcTrace ( Image& iImage,
00219     double iX,
00220     double iY,
00221     double iScale,
00222     int& oTrace )
00223 {
00224   int aRX = Math::Round ( iX );
00225   int aRY = Math::Round ( iY );
00226 
00227   BoxFilter aBox ( 3*iScale, iImage );
00228 
00229   if ( !aBox.checkBounds ( aRX, aRY ) )
00230     return false;
00231 
00232   aBox.setY ( aRY );
00233 
00234   double aTrace = aBox.getDxxWithX ( aRX ) + aBox.getDyyWithX ( aRX );
00235 
00236   oTrace = ( aTrace <= 0.0 ? -1 : 1 );
00237 
00238   return true;
00239 }
00240 
00241 
00242 void KeyPointDetector::ComputeHelper::calcDet ( int s, int filterSize )
00243 {
00244   // create a box filter of the correct size.
00245   BoxFilter aBoxFilter ( filterSize, _image );
00246 
00247   // calculate the border for this scale
00248   const int aBS = _borderSize[ s ];
00249 
00250   // fill the hessians
00251   int aEy = _octaveHeight - aBS;
00252   int aEx = _octaveWidth - aBS;
00253 
00254   int aYPS = aBS * _pixelStep;
00255 
00256   for ( int y = aBS; y < aEy; ++y )
00257   {
00258     aBoxFilter.setY ( aYPS );
00259     int aXPS = aBS * _pixelStep;
00260 
00261     for ( int x = aBS; x < aEx; ++x )
00262     {
00263       _scaleHessian[s][y][x] = aBoxFilter.getDetWithX ( aXPS );
00264       aXPS += _pixelStep;
00265     }
00266 
00267     aYPS += _pixelStep;
00268   }
00269 }
00270 
00271 void KeyPointDetector::ComputeHelper::detect ( int s, KeyPointInsertor& iInsertor )
00272 {
00273   int aMaxReachableScale = s + 2;
00274   if ( aMaxReachableScale >= _maxScales )
00275   {
00276     aMaxReachableScale = _maxScales - 1;
00277   }
00278   const int aBS = _borderSize[ aMaxReachableScale ];
00279 
00280   double aTab[8];
00281   
00282 //   if ( ( _octaveHeight - aBS - 2 < 0 )
00283     
00284 
00285   for ( int aYIt = aBS + 1; aYIt < _octaveHeight - aBS - 2; aYIt += 2 )
00286   {
00287     for ( int aXIt = aBS + 1; aXIt < _octaveWidth - aBS - 2; aXIt += 2 )
00288     {
00289       // find the maximum in the 2x2x2 cube
00290 
00291       // get the values in a
00292       aTab[0] = _scaleHessian[s]  [aYIt]  [aXIt];
00293       aTab[1] = _scaleHessian[s]  [aYIt]  [aXIt+1];
00294       aTab[2] = _scaleHessian[s]  [aYIt+1][aXIt];
00295       aTab[3] = _scaleHessian[s]  [aYIt+1][aXIt+1];
00296       aTab[4] = _scaleHessian[s+1][aYIt]  [aXIt];
00297       aTab[5] = _scaleHessian[s+1][aYIt]  [aXIt+1];
00298       aTab[6] = _scaleHessian[s+1][aYIt+1][aXIt];
00299       aTab[7] = _scaleHessian[s+1][aYIt+1][aXIt+1];
00300       
00301       // find the max index without using a loop.
00302       int a04 = ( aTab[0] > aTab[4] ? 0 : 4 );
00303       int a15 = ( aTab[1] > aTab[5] ? 1 : 5 );
00304       int a26 = ( aTab[2] > aTab[6] ? 2 : 6 );
00305       int a37 = ( aTab[3] > aTab[7] ? 3 : 7 );
00306       int a0426 = ( aTab[a04] > aTab[a26] ? a04 : a26 );
00307       int a1537 = ( aTab[a15] > aTab[a37] ? a15 : a37 );
00308       int aMaxIdx = ( aTab[a0426] > aTab[a1537] ? a0426 : a1537 );
00309       
00310       // calculate approximate threshold
00311       double aApproxThres = _scoreThreshold * 0.8;
00312 
00313       double aScore = aTab[aMaxIdx];
00314 
00315       // check found point against threshold
00316       if ( aScore < aApproxThres )
00317         continue;
00318 
00319       // verify that other missing points in the 3x3x3 cube are also below treshold
00320 
00321       // aXShift: 2*0-1 = -1; 2*1-1=1
00322       // aXAdj = aXIt (no adjustment)      -->  aXShift = -1
00323       // aXAdj = aXIt+1 (with adjustment)  -->  aXShift =  1
00324       int aXShift = 2 * ( aMaxIdx & 1 ) - 1;
00325       int aXAdj = aXIt + ( aMaxIdx & 1 );
00326       
00327       aMaxIdx >>= 1;
00328 
00329       int aYShift = 2 * ( aMaxIdx & 1 ) - 1;
00330       int aYAdj = aYIt + ( aMaxIdx & 1 ); 
00331       
00332       aMaxIdx >>= 1;
00333 
00334       int aSShift = 2 * ( aMaxIdx & 1 ) - 1;
00335       int aSAdj = s + ( aMaxIdx & 1 );
00336 
00337       // skip too high scale adjusting
00338       if ( aSAdj == ( int ) _maxScales - 1 )
00339         continue;
00340 
00341       if ( ( _scaleHessian[aSAdj + aSShift][aYAdj - aYShift][aXAdj - 1] > aScore ) ||
00342            ( _scaleHessian[aSAdj + aSShift][aYAdj - aYShift][aXAdj    ] > aScore ) ||
00343            ( _scaleHessian[aSAdj + aSShift][aYAdj - aYShift][aXAdj + 1] > aScore ) ||
00344            ( _scaleHessian[aSAdj + aSShift][aYAdj   ][aXAdj - 1] > aScore ) ||
00345            ( _scaleHessian[aSAdj + aSShift][aYAdj   ][aXAdj    ] > aScore ) ||
00346            ( _scaleHessian[aSAdj + aSShift][aYAdj   ][aXAdj + 1] > aScore ) ||
00347            ( _scaleHessian[aSAdj + aSShift][aYAdj + aYShift][aXAdj - 1] > aScore ) ||
00348            ( _scaleHessian[aSAdj + aSShift][aYAdj + aYShift][aXAdj    ] > aScore ) ||
00349            ( _scaleHessian[aSAdj + aSShift][aYAdj + aYShift][aXAdj + 1] > aScore ) ||
00350 
00351            ( _scaleHessian[aSAdj][  aYAdj + aYShift ][aXAdj - 1] > aScore ) ||
00352            ( _scaleHessian[aSAdj][   aYAdj + aYShift ][aXAdj] > aScore ) ||
00353            ( _scaleHessian[aSAdj][  aYAdj + aYShift ][aXAdj + 1] > aScore ) ||
00354            ( _scaleHessian[aSAdj][ aYAdj   ][aXAdj + aXShift] > aScore ) ||
00355            ( _scaleHessian[aSAdj][ aYAdj - aYShift ][aXAdj + aXShift] > aScore ) ||
00356 
00357            ( _scaleHessian[aSAdj - aSShift][  aYAdj + aYShift ][aXAdj - 1] > aScore ) ||
00358            ( _scaleHessian[aSAdj - aSShift][   aYAdj + aYShift ][aXAdj] > aScore ) ||
00359            ( _scaleHessian[aSAdj - aSShift][  aYAdj + aYShift ][aXAdj + 1] > aScore ) ||
00360            ( _scaleHessian[aSAdj - aSShift][ aYAdj   ][aXAdj + aXShift] > aScore ) ||
00361            ( _scaleHessian[aSAdj - aSShift][ aYAdj - aYShift ][aXAdj + aXShift] > aScore )
00362          )
00363         continue;
00364 
00365       // fine tune the location
00366       double aX = aXAdj;
00367       double aY = aYAdj;
00368       double aS = aSAdj;
00369 
00370       // try to fine tune, restore the values if it failed
00371       // if the returned value is true,  keep the point, else drop it.
00372       if ( !fineTuneExtrema ( _scaleHessian, aXAdj, aYAdj, aSAdj, aX, aY, aS, aScore, _octaveWidth, _octaveHeight, _borderSize[aSAdj] ) )
00373       {
00374         continue;
00375       }
00376 
00377       // recheck the updated score
00378       if ( aScore < _scoreThreshold )
00379       {
00380         continue;
00381       }
00382 
00383       // adjust the values
00384       aX *= _pixelStep;
00385 
00386       aY *= _pixelStep;
00387 
00388       aS = ( ( 2 * aS * _pixelStep ) + _initialBoxFilterSize + ( _pixelStep - 1 ) * _maxScales ) / 3.0; // this one was hard to guess...
00389 
00390       // store the point
00391       int aTrace;
00392 
00393       if ( !calcTrace ( _image, aX, aY, aS, aTrace ) )
00394       {
00395         continue;
00396       }
00397 
00398       // do something with the keypoint depending on the insertor
00399       _insertorMutex.lock();
00400       iInsertor ( KeyPoint ( aX, aY, aS * kBaseSigma, aScore, aTrace ) );
00401       _insertorMutex.unlock();
00402     }
00403   }
00404 }


or_libs
Author(s): raphael
autogenerated on Mon Oct 6 2014 02:53:18