Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
KeyPointDetector.cpp
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2007-2008 Anael Orlinski
3 *
4 * This file is part of Panomatic.
5 *
6 * Panomatic is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Panomatic is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Panomatic; if not, write to the Free Software
18 * <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <iostream>
22 
23 #include "KeyPoint.h"
24 #include "KeyPointDetector.h"
25 #include "BoxFilter.h"
26 #include "MathStuff.h"
27 
28 namespace lfeat
29 {
30 const double KeyPointDetector::kBaseSigma = 1.2;
31 
33 {
34  // initialize default values
35  _maxScales = 5; // number of scales : 9x9, 15x15, 21x21, 27x27, ...
36  _maxOctaves = 4; // number of octaves
37 
38  _scoreThreshold = 1000;
39 
41  _scaleOverlap = 3;
42 
43 }
44 
46 {
47  // allocate lots of memory for the scales
48  double** * aSH = new double**[_maxScales];
49  for (unsigned int s = 0; s < _maxScales; ++s)
50  {
51  aSH[s] = Image::AllocateImage(iImage.getWidth(), iImage.getHeight());
52  }
53 
54  // init the border size
55  unsigned int* aBorderSize = new unsigned int[_maxScales];
56 
57  unsigned int aMaxima = 0;
58 
59  // base size + 3 times first increment for step back
60  // for the first octave 9x9, 15x15, 21x21, 27x27, 33x33
61  // for the second 21x21, 33x33, 45x45 ...
62 
63  // go through all the octaves
64  for (unsigned int o = 0; o < _maxOctaves; ++o)
65  {
66  // calculate the pixel step on the image, and the image size
67  unsigned int aPixelStep = 1 << o; // 2^aOctaveIt
68  int aOctaveWidth = iImage.getWidth() / aPixelStep; // integer division
69  int aOctaveHeight = iImage.getHeight() / aPixelStep; // integer division
70 
71  // fill each scale matrices
72  for (unsigned int s = 0; s < _maxScales; ++s)
73  {
74  // create a box filter of the correct size.
75  BoxFilter aBoxFilter(getFilterSize(o, s), iImage);
76 
77  // calculate the border for this scale
78  aBorderSize[s] = getBorderSize(o, s);
79 
80  // fill the hessians
81  int aEy = aOctaveHeight - aBorderSize[s];
82  int aEx = aOctaveWidth - aBorderSize[s];
83 
84  int aYPS = aBorderSize[s] * aPixelStep;
85  for (int y = aBorderSize[s]; y < aEy; ++y)
86  {
87  aBoxFilter.setY(aYPS);
88  int aXPS = aBorderSize[s] * aPixelStep;
89  for (int x = aBorderSize[s]; x < aEx; ++x)
90  {
91  aSH[s][y][x] = aBoxFilter.getDetWithX(aXPS);
92  aXPS += aPixelStep;
93  }
94  aYPS += aPixelStep;
95  }
96  }
97 
98  // detect the feature points with a 3x3x3 neighborhood non-maxima suppression
99  for (unsigned int aSIt = 1; aSIt < (_maxScales - 1); aSIt += 2)
100  {
101  const int aBS = aBorderSize[aSIt + 1];
102  for (int aYIt = aBS + 1; aYIt < aOctaveHeight - aBS - 1; aYIt += 2)
103  {
104  for (int aXIt = aBS + 1; aXIt < aOctaveWidth - aBS - 1; aXIt += 2)
105  {
106  // find the maximum in the 2x2x2 cube
107  double aTab[8];
108 
109  // get the values in a
110  aTab[0] = aSH[aSIt][aYIt][aXIt];
111  aTab[1] = aSH[aSIt][aYIt][aXIt + 1];
112  aTab[2] = aSH[aSIt][aYIt + 1][aXIt];
113  aTab[3] = aSH[aSIt][aYIt + 1][aXIt + 1];
114  aTab[4] = aSH[aSIt + 1][aYIt][aXIt];
115  aTab[5] = aSH[aSIt + 1][aYIt][aXIt + 1];
116  aTab[6] = aSH[aSIt + 1][aYIt + 1][aXIt];
117  aTab[7] = aSH[aSIt + 1][aYIt + 1][aXIt + 1];
118 
119  // find the max index without using a loop.
120  unsigned int a04 = (aTab[0] > aTab[4] ? 0 : 4);
121  unsigned int a15 = (aTab[1] > aTab[5] ? 1 : 5);
122  unsigned int a26 = (aTab[2] > aTab[6] ? 2 : 6);
123  unsigned int a37 = (aTab[3] > aTab[7] ? 3 : 7);
124  unsigned int a0426 = (aTab[a04] > aTab[a26] ? a04 : a26);
125  unsigned int a1537 = (aTab[a15] > aTab[a37] ? a15 : a37);
126  unsigned int aMaxIdx = (aTab[a0426] > aTab[a1537] ? a0426 : a1537);
127 
128  // calculate approximate threshold
129  double aApproxThres = _scoreThreshold * 0.8;
130 
131  double aScore = aTab[aMaxIdx];
132 
133  // check found point against threshold
134  if (aScore < aApproxThres)
135  {
136  continue;
137  }
138 
139  // verify that other missing points in the 3x3x3 cube are also below treshold
140  int aXShift = 2 * (aMaxIdx & 1) - 1;
141  int aXAdj = aXIt + (aMaxIdx & 1);
142  aMaxIdx >>= 1;
143 
144  int aYShift = 2 * (aMaxIdx & 1) - 1;
145  int aYAdj = aYIt + (aMaxIdx & 1);
146  aMaxIdx >>= 1;
147 
148  int aSShift = 2 * (aMaxIdx & 1) - 1;
149  int aSAdj = aSIt + (aMaxIdx & 1);
150 
151  // skip too high scale ajusting
152  if (aSAdj == (int)_maxScales - 1)
153  {
154  continue;
155  }
156 
157  if ((aSH[aSAdj + aSShift][aYAdj - aYShift][aXAdj - 1] > aScore) ||
158  (aSH[aSAdj + aSShift][aYAdj - aYShift][aXAdj] > aScore) ||
159  (aSH[aSAdj + aSShift][aYAdj - aYShift][aXAdj + 1] > aScore) ||
160  (aSH[aSAdj + aSShift][aYAdj][aXAdj - 1] > aScore) ||
161  (aSH[aSAdj + aSShift][aYAdj][aXAdj] > aScore) ||
162  (aSH[aSAdj + aSShift][aYAdj][aXAdj + 1] > aScore) ||
163  (aSH[aSAdj + aSShift][aYAdj + aYShift][aXAdj - 1] > aScore) ||
164  (aSH[aSAdj + aSShift][aYAdj + aYShift][aXAdj] > aScore) ||
165  (aSH[aSAdj + aSShift][aYAdj + aYShift][aXAdj + 1] > aScore) ||
166 
167  (aSH[aSAdj][aYAdj + aYShift][aXAdj - 1] > aScore) ||
168  (aSH[aSAdj][aYAdj + aYShift][aXAdj] > aScore) ||
169  (aSH[aSAdj][aYAdj + aYShift][aXAdj + 1] > aScore) ||
170  (aSH[aSAdj][aYAdj][aXAdj + aXShift] > aScore) ||
171  (aSH[aSAdj][aYAdj - aYShift][aXAdj + aXShift] > aScore) ||
172 
173  (aSH[aSAdj - aSShift][aYAdj + aYShift][aXAdj - 1] > aScore) ||
174  (aSH[aSAdj - aSShift][aYAdj + aYShift][aXAdj] > aScore) ||
175  (aSH[aSAdj - aSShift][aYAdj + aYShift][aXAdj + 1] > aScore) ||
176  (aSH[aSAdj - aSShift][aYAdj][aXAdj + aXShift] > aScore) ||
177  (aSH[aSAdj - aSShift][aYAdj - aYShift][aXAdj + aXShift] > aScore)
178  )
179  {
180  continue;
181  }
182 
183  // fine tune the location
184  double aX = aXAdj;
185  double aY = aYAdj;
186  double aS = aSAdj;
187 
188  if (aBorderSize[aSAdj + 1] > aBorderSize[aSAdj])
189  {
190  if (aX<aBorderSize[aSAdj + 1] || aX>aOctaveWidth - aBorderSize[aSAdj + 1] - 1)
191  {
192  continue;
193  };
194  if (aY<aBorderSize[aSAdj + 1] || aY>aOctaveHeight - aBorderSize[aSAdj + 1] - 1)
195  {
196  continue;
197  };
198  };
199  // try to fine tune, restore the values if it failed
200  // if the returned value is true, keep the point, else drop it.
201  if (!fineTuneExtrema(aSH, aXAdj, aYAdj, aSAdj, aX, aY, aS, aScore, aOctaveWidth, aOctaveHeight, aBorderSize[aSAdj + 1]))
202  {
203  continue;
204  }
205 
206  // recheck the updated score
207  if (aScore < _scoreThreshold)
208  {
209  continue;
210  }
211 
212  //if (aScore > 1e10)
213  //{
214  // //continue;
215  // std::cout << "big big score" << std::endl;
216  //}
217 
218  // adjust the values
219  aX *= aPixelStep;
220  aY *= aPixelStep;
221  aS = ((2 * aS * aPixelStep) + _initialBoxFilterSize + (aPixelStep - 1) * _maxScales) / 3.0; // this one was hard to guess...
222 
223  // store the point
224  int aTrace;
225  if (!calcTrace(iImage, aX, aY, aS, aTrace))
226  {
227  continue;
228  }
229 
230  aMaxima++;
231 
232  // do something with the keypoint depending on the insertor
233  iInsertor(KeyPoint(aX, aY, aS * kBaseSigma, aScore, aTrace));
234 
235  }
236  }
237  }
238  }
239 
240  // deallocate memory of the scale images
241  for (unsigned int s = 0; s < _maxScales; ++s)
242  {
243  Image::DeallocateImage(aSH[s], iImage.getHeight());
244  }
245  delete[]aSH;
246  delete[]aBorderSize;
247 }
248 
249 bool KeyPointDetector::fineTuneExtrema(double** * iSH, unsigned int iX, unsigned int iY, unsigned int iS,
250  double& oX, double& oY, double& oS, double& oScore,
251  unsigned int iOctaveWidth, unsigned int iOctaveHeight, unsigned int iBorder)
252 {
253  // maximum fine tune iterations
254  const unsigned int kMaxFineTuneIters = 6;
255 
256  // shift from the initial position for X and Y (only -1 or + 1 during the iterations).
257  int aX = iX;
258  int aY = iY;
259  int aS = iS;
260 
261  int aShiftX = 0;
262  int aShiftY = 0;
263 
264  // current deviations
265  double aDx = 0, aDy = 0, aDs = 0;
266 
267  //result vector
268  double aV[3]; //(x,y,s)
269 
270  for (unsigned int aIter = 0; aIter < kMaxFineTuneIters; ++aIter)
271  {
272  // update the extrema position
273  aX += aShiftX;
274  aY += aShiftY;
275 
276  // create the problem matrix
277  double aM[3][3]; //symetric, no ordering problem.
278 
279  // fill the result vector with gradient from pixels differences (negate to prepare system solve)
280  aDx = (iSH[aS][aY][aX + 1] - iSH[aS][aY][aX - 1]) * 0.5;
281  aDy = (iSH[aS][aY + 1][aX] - iSH[aS][aY - 1][aX]) * 0.5;
282  aDs = (iSH[aS + 1][aY][aX] - iSH[aS - 1][aY][aX]) * 0.5;
283 
284  aV[0] = -aDx;
285  aV[1] = -aDy;
286  aV[2] = -aDs;
287 
288  // fill the matrix with values of the hessian from pixel differences
289  aM[0][0] = iSH[aS][aY][aX - 1] - 2.0 * iSH[aS][aY][aX] + iSH[aS][aY][aX + 1];
290  aM[1][1] = iSH[aS][aY - 1][aX] - 2.0 * iSH[aS][aY][aX] + iSH[aS][aY + 1][aX];
291  aM[2][2] = iSH[aS - 1][aY][aX] - 2.0 * iSH[aS][aY][aX] + iSH[aS + 1][aY][aX];
292 
293  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;
294  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;
295  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;
296 
297  // solve the linear system. results are in aV. exit with error if a problem happened
298  if (!Math::SolveLinearSystem33(aV, aM))
299  {
300  return false;
301  }
302 
303 
304  // ajust the shifts with the results and stop if no significant change
305 
306  if (aIter < kMaxFineTuneIters - 1)
307  {
308  aShiftX = 0;
309  aShiftY = 0;
310 
311  if (aV[0] > 0.6 && aX < (int)(iOctaveWidth - iBorder - 2))
312  {
313  aShiftX++;
314  }
315  else if (aV[0] < -0.6 && aX > (int)iBorder + 1)
316  {
317  aShiftX--;
318  }
319 
320  if (aV[1] > 0.6 && aY < (int)(iOctaveHeight - iBorder - 2))
321  {
322  aShiftY++;
323  }
324  else if (aV[1] < -0.6 && aY > (int)iBorder + 1)
325  {
326  aShiftY--;
327  }
328 
329  if (aShiftX == 0 && aShiftY == 0)
330  {
331  break;
332  }
333  }
334  }
335 
336  // update the score
337  oScore = iSH[aS][aY][aX] + 0.5 * (aDx * aV[0] + aDy * aV[1] + aDs * aV[2]);
338 
339  // reject too big deviation in last step (unfinished job).
340  if (std::abs(aV[0]) > 1.5 || std::abs(aV[1]) > 1.5 || std::abs(aV[2]) > 1.5)
341  {
342  return false;
343  }
344 
345  // put the last deviation (not integer :) to the output
346  oX = aX + aV[0];
347  oY = aY + aV[1];
348  oS = iS + aV[2];
349 
350  return true;
351 }
352 
353 unsigned int KeyPointDetector::getFilterSize(unsigned int iOctave, unsigned int iScale)
354 {
355  unsigned int aScaleShift = 2 << iOctave;
356  return _initialBoxFilterSize + (aScaleShift - 2)*(_maxScales - _scaleOverlap) + aScaleShift * iScale;
357 }
358 
359 unsigned int KeyPointDetector::getBorderSize(unsigned int iOctave, unsigned int iScale)
360 {
361  unsigned int aScaleShift = 2 << iOctave;
362  if (iScale <= 2)
363  {
364  unsigned int aMult = (iOctave == 0 ? 1 : 2);
365  return (getFilterSize(iOctave, 1) + aMult * aScaleShift) * 3 / aScaleShift + 1;
366  }
367  return getFilterSize(iOctave, iScale) * 3 / aScaleShift + 1;
368 }
369 
371  double iX,
372  double iY,
373  double iScale,
374  int& oTrace)
375 {
376  unsigned int aRX = hugin_utils::roundi(iX);
377  unsigned int aRY = hugin_utils::roundi(iY);
378 
379  BoxFilter aBox(3 * iScale, iImage);
380 
381  if (!aBox.checkBounds(aRX, aRY))
382  {
383  return false;
384  }
385 
386  aBox.setY(aRY);
387  double aTrace = aBox.getDxxWithX(aRX) + aBox.getDyyWithX(aRX);
388  oTrace = (aTrace <= 0.0 ? -1 : 1);
389 
390  return true;
391 }
392 
393 } // namespace lfeat
static bool SolveLinearSystem33(double *solution, double sq[3][3])
Definition: MathStuff.cpp:24
unsigned int getBorderSize(unsigned int iOctave, unsigned int iScale)
int roundi(T x)
Definition: hugin_math.h:73
bool calcTrace(Image &iImage, double iX, double iY, double iScale, int &oTrace)
static double ** AllocateImage(unsigned int iWidth, unsigned int iHeight)
Definition: Image.cpp:88
unsigned int getWidth()
Definition: Image.h:54
bool fineTuneExtrema(double ***iSH, unsigned int iX, unsigned int iY, unsigned int iS, double &oX, double &oY, double &oS, double &oScore, unsigned int iOctaveWidth, unsigned int iOctaveHeight, unsigned int iBorder)
double getDyyWithX(unsigned int x) const
Definition: BoxFilter.h:111
unsigned int getHeight()
Definition: Image.h:58
unsigned int getFilterSize(unsigned int iOctave, unsigned int iScale)
double getDetWithX(unsigned int x) const
Definition: BoxFilter.h:132
bool checkBounds(int x, int y) const
Definition: BoxFilter.h:155
void setY(unsigned int y)
Definition: BoxFilter.h:141
static const double kBaseSigma
double getDxxWithX(unsigned int x) const
Definition: BoxFilter.h:105
static void DeallocateImage(double **iImagePtr, unsigned int iHeight)
Definition: Image.cpp:102
unsigned int _initialBoxFilterSize
void detectKeypoints(Image &iImage, KeyPointInsertor &iInsertor)