00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
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   
00045   _maxScales = 5;  
00046   _maxOctaves = 4; 
00047 
00048   _scoreThreshold = 0.2;
00049 
00050   _initialBoxFilterSize = 3;
00051   _scaleOverlap = 3;
00052 }
00053 
00054 
00055 void KeyPointDetector::detectKeyPoints ( KeyPointInsertor& iInsertor )
00056 {
00057   
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   
00064   int * aBorderSize = new int[_maxScales];
00065 
00066   boost::mutex aInsertorMutex;
00067 
00068   
00069   for ( int o = 0; o < _maxOctaves; ++o )
00070   {
00071     
00072     int aPixelStep = 1 << o; 
00073     int aOctaveWidth = _image.getWidth() / aPixelStep; 
00074     int aOctaveHeight = _image.getHeight() / aPixelStep; 
00075 
00076     
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     
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     
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   
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   
00109   const int kMaxFineTuneIters = 6;
00110 
00111   
00112   int aX = iX;
00113   int aY = iY;
00114   int aS = iS;
00115 
00116   int aShiftX = 0;
00117   int aShiftY = 0;
00118 
00119   
00120   double aDx = 0, aDy = 0, aDs = 0;
00121 
00122   
00123   double aV[3]; 
00124 
00125   for ( int aIter = 0; aIter < kMaxFineTuneIters; ++aIter )
00126   {
00127     
00128     aX += aShiftX;
00129     aY += aShiftY;
00130 
00131     
00132     double aM[3][3]; 
00133 
00134     
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     
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     
00153     if ( !Math::SolveLinearSystem33 ( aV, aM ) )
00154     {
00155       return false;
00156     }
00157 
00158     
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   
00180   oScore = iSH[aS][aY][aX] + 0.5 * ( aDx * aV[0] + aDy * aV[1] + aDs * aV[2] );
00181 
00182   
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   
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   
00199   
00200   
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   
00245   BoxFilter aBoxFilter ( filterSize, _image );
00246 
00247   
00248   const int aBS = _borderSize[ s ];
00249 
00250   
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 
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       
00290 
00291       
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       
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       
00311       double aApproxThres = _scoreThreshold * 0.8;
00312 
00313       double aScore = aTab[aMaxIdx];
00314 
00315       
00316       if ( aScore < aApproxThres )
00317         continue;
00318 
00319       
00320 
00321       
00322       
00323       
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       
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       
00366       double aX = aXAdj;
00367       double aY = aYAdj;
00368       double aS = aSAdj;
00369 
00370       
00371       
00372       if ( !fineTuneExtrema ( _scaleHessian, aXAdj, aYAdj, aSAdj, aX, aY, aS, aScore, _octaveWidth, _octaveHeight, _borderSize[aSAdj] ) )
00373       {
00374         continue;
00375       }
00376 
00377       
00378       if ( aScore < _scoreThreshold )
00379       {
00380         continue;
00381       }
00382 
00383       
00384       aX *= _pixelStep;
00385 
00386       aY *= _pixelStep;
00387 
00388       aS = ( ( 2 * aS * _pixelStep ) + _initialBoxFilterSize + ( _pixelStep - 1 ) * _maxScales ) / 3.0; 
00389 
00390       
00391       int aTrace;
00392 
00393       if ( !calcTrace ( _image, aX, aY, aS, aTrace ) )
00394       {
00395         continue;
00396       }
00397 
00398       
00399       _insertorMutex.lock();
00400       iInsertor ( KeyPoint ( aX, aY, aS * kBaseSigma, aScore, aTrace ) );
00401       _insertorMutex.unlock();
00402     }
00403   }
00404 }