Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CircularKeyPointDescriptor.cpp
Go to the documentation of this file.
1 /* *-* tab-width: 4; c-basic-offset: 4; -*- */
2 /*
3 * Copyright (C) 2007-2008 Anael Orlinski
4 *
5 * This file is part of Panomatic.
6 *
7 * Panomatic is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * Panomatic is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Panomatic; if not, write to the Free Software
19 * <http://www.gnu.org/licenses/>.
20 */
21 
22 #include <iostream>
23 
24 #ifdef _MSC_VER
25 #define _USE_MATH_DEFINES
26 #endif
27 #include <math.h>
28 #include <vector>
29 #include <map>
30 #include <fstream>
31 #include <cassert>
32 
33 #include <string.h>
34 
35 #include "KeyPoint.h"
37 #include "MathStuff.h"
38 #include "WaveFilter.h"
39 #include "hugin_math/hugin_math.h"
40 
41 //#define DEBUG_DESC
42 //#define DEBUG_ROT
43 
44 namespace lfeat
45 {
46 LUT<0, 83> Exp1_2(exp, 0.5, -0.08);
47 
49  std::vector<int> rings, std::vector<double>ring_radius,
50  std::vector<double>ring_gradient_width,
51  int ori_nbins, double ori_sample_scale, int ori_gridsize) :
52  _image(iImage), _ori_nbins(ori_nbins), _ori_sample_scale(ori_sample_scale),
53  _ori_gridsize(ori_gridsize)
54 {
55  // default parameters
56  if (rings.empty())
57  {
58  rings.push_back(1);
59  ring_radius.push_back(0);
60  ring_gradient_width.push_back(2);
61  rings.push_back(6);
62  ring_radius.push_back(4.2);
63  ring_gradient_width.push_back(1.9);
64  /*
65  rings.push_back(6);
66  ring_radius.push_back(5);
67  ring_gradient_width.push_back(3);
68  */
69  rings.push_back(6);
70  ring_radius.push_back(8);
71  ring_gradient_width.push_back(3.2);
72  }
73 
74  assert(rings.size() == ring_radius.size());
75  assert(rings.size() == ring_gradient_width.size());
76 
77  /*
78  // radius of the rings (ring 0 is the center point)
79  double ring_radius[3];
80  ring_radius[0] = 0;
81  ring_radius[1] = 2;
82  ring_radius[2] = 5;
83 
84  // width of the boxfilter used during the computation (ring 0 is the center point)
85  double ring_gradwidth[3];
86  ring_gradwidth[0] = 0.5;
87  ring_gradwidth[1] = 1.5;
88  ring_gradwidth[2] = 2.5;
89 
90  // number of entries for each ring
91  int nrings[3];
92  nrings[0] = 1;
93  nrings[1] = 6;
94  nrings[2] = 6;
95  */
96  // compute number of sampling points
97  _subRegions = 0;
98  for (unsigned int i = 0; i < rings.size(); i++)
99  {
100  _subRegions += rings[i];
101  }
102 
103  // create a list of sample parameters
105 
106  // precompute positions of the sampling points
107  int j = 0;
108  for (unsigned int i = 0; i < rings.size(); i++)
109  {
110  // alternate the phi offset of the rings,
111  // so that the circles don't overlap too much with the
112  // next ring
113  double phioffset = i % 2 == 0 ? 0 : M_PI / rings[i];
114  for (int ri = 0; ri < rings[i]; ri++)
115  {
116  double phi = ri * 2 * M_PI / rings[i] + phioffset;
117  _samples[j].x = ring_radius[i] * cos(phi);
118  _samples[j].y = ring_radius[i] * sin(phi);
119  _samples[j].size = ring_gradient_width[i];
120  j++;
121  }
122  }
123 
124  // compute 4 gradient entries (pos(dx), pos(-dx), pos(dy), pos(-dy))
125  // maybe use something else here, for example, just the gradient entries...
126  // also use LBP as a 5th feature
127  _vecLen = 3;
128  _descrLen = _vecLen * _subRegions - 1;
129 
130  _ori_hist = new double[_ori_nbins + 2];
131 
132 }
133 
135 {
136  delete[] _ori_hist;
137  delete[] _samples;
138 }
139 
141 {
142  // create a descriptor context
143  //KeyPointDescriptorContext aCtx(_subRegions, _vecLen, ioKeyPoint._ori);
144 
145  // create the storage in the keypoint
146  if (!ioKeyPoint._vec)
147  {
148  ioKeyPoint.allocVector(getDescriptorLength());
149  }
150 
151  // create a vector
152  createDescriptor(ioKeyPoint);
153 
154  // normalize
156 }
157 
158 int CircularKeyPointDescriptor::assignOrientation(lfeat::KeyPoint& ioKeyPoint, double angles[4]) const
159 {
160  double* hist = _ori_hist + 1;
161  unsigned int aRX = hugin_utils::roundi(ioKeyPoint._x);
162  unsigned int aRY = hugin_utils::roundi(ioKeyPoint._y);
163  int aStep = (int)(ioKeyPoint._scale + 0.8);
164 
165  WaveFilter aWaveFilter(_ori_sample_scale * ioKeyPoint._scale + 1.5, _image);
166 
167 #ifdef DEBUG_ROT_2
168  std::cerr << "ori_scale = " << 2.5 * ioKeyPoint._scale + 1.5 << std::endl;
169  std::cerr << "ori= [ ";
170 #endif
171 
172 #ifdef _MSC_VER
173 #pragma message("use LUT after parameter tuning")
174 #else
175  #warning use LUT after parameter tuning
176 #endif
177  double coeffadd = 0.5;
178  double coeffmul = (0.5 + 6) / -(_ori_nbins*_ori_nbins);
179 
180  memset(_ori_hist, 0, sizeof(double)*(_ori_nbins + 2));
181  // compute haar wavelet responses in a circular neighborhood of _ori_gridsize s
182  for (int aYIt = -_ori_gridsize; aYIt <= _ori_gridsize; aYIt++)
183  {
184  int aSY = aRY + aYIt * aStep;
185  for (int aXIt = -_ori_gridsize; aXIt <= _ori_gridsize; aXIt++)
186  {
187  int aSX = aRX + aXIt * aStep;
188  // keep points in a circular region of diameter 6s
189  const int aSqDist = aXIt * aXIt + aYIt * aYIt;
190  if (aSqDist <= _ori_nbins*_ori_nbins && aWaveFilter.checkBounds(aSX, aSY))
191  {
192  double aWavX = aWaveFilter.getWx(aSX, aSY);
193  // y axis for derivate seems is from bottom to top (typical math coordinate system),
194  // not from top to bottom (as in the typical image coordinate system).
195  double aWavY = -aWaveFilter.getWy(aSX, aSY);
196  double aWavResp = sqrt(aWavX * aWavX + aWavY * aWavY);
197  if (aWavResp > 0)
198  {
199  // add PI -> 0 .. 2*PI interval
200  double angle = atan2(aWavY, aWavX) + PI;
201  int bin = angle / (2 * PI) * _ori_nbins;
202  // deal with possible rounding problems.
203  bin = (bin + _ori_nbins) % _ori_nbins;
204  // center of bin 0 equals -PI + 16°deg, etc.
205  double weight = exp(coeffmul * (aSqDist + coeffadd));
206  hist[bin] += aWavResp * weight;
207  //hist[bin] += aWavResp * Exp1_2(aSqDist);
208 #ifdef DEBUG_ROT_2
209  std::cerr << "[ " << aSX << ", " << aSY << ", "
210  << aWavX << ", " << aWavY << ", "
211  << aWavResp << ", " << angle << ", " << bin << ", "
212  << aSqDist << ", " << aWavResp* Exp1_2(aSqDist) << "], " << std::endl;
213 #endif
214  }
215  }
216  }
217  }
218 #ifdef DEBUG_ROT_2
219  std::cerr << "]" << endl;
220 #endif
221 
222 #if 0
223  // smoothing doesn't seem to work very well with the default grid + scale values for orientation histogram building
224  // smooth histogram
225  for (int it=0; it < 1; it++)
226  {
227  double prev = hist[_ori_nbins-1];
228  double first = hist[0];
229  for (int i=0; i < _ori_nbins-2; i++)
230  {
231  double hs = (prev + hist[i] + hist[i+1]) / 3.0;
232  prev = hist[i];
233  hist[i] = hs;
234  }
235  hist[_ori_nbins-1] = (prev + hist[_ori_nbins-1] + first) / 3.0;
236  }
237 #endif
238 
239  // avoid boundary problems, wrap around histogram
240  hist[-1] = hist[_ori_nbins - 1];
241  hist[_ori_nbins] = hist[0];
242 
243  // find bin with the maximum response.
244  double aMax = hist[0];
245  int iMax = 0;
246 #ifdef DEBUG_ROT
247  std::cerr << "rot_hist: [ " << aMax;
248 #endif
249  for (int i = 1; i < _ori_nbins; i++)
250  {
251 #ifdef DEBUG_ROT
252  std::cerr << ", " << hist[i];
253 #endif
254  if (hist[i] > aMax)
255  {
256  aMax = hist[i];
257  iMax = i;
258  }
259  }
260 #ifdef DEBUG_ROT
261  std::cerr << " ] " << std::endl;
262 #endif
263 
264  double prev = hist[iMax - 1];
265  double curr = hist[iMax];
266  double next = hist[iMax + 1];
267  double dsub = -0.5*(next - prev) / (prev + next - 2 * curr);
268  ioKeyPoint._ori = (iMax + 0.5 + dsub) / _ori_nbins * 2 * PI - PI;
269 
270  // add more keypoints that are within 0.8 of the maximum strength.
271  aMax *= 0.8;
272  int nNewOri = 0;
273  for (int i = 0; i < _ori_nbins; i++)
274  {
275  double prev = hist[i - 1];
276  double curr = hist[i];
277  double next = hist[i + 1];
278  if (curr > aMax && prev < curr && next < curr && i != iMax)
279  {
280  dsub = -0.5*(next - prev) / (prev + next - 2 * curr);
281  angles[nNewOri] = (i + 0.5 + dsub) / _ori_nbins * 2 * PI - PI;
282  nNewOri++;
283  if (nNewOri == 4)
284  {
285  break;
286  }
287  }
288  }
289  return nNewOri;
290 }
291 
292 // gradient and intensity difference
294 {
295 #ifdef DEBUG_DESC
296  std::ofstream dlog("descriptor_details.txt", std::ios_base::app);
297 #endif
298 
299  // create the vector of features by analyzing a square patch around the point.
300  // for this the current patch (x,y) will be translated in rotated coordinates (u,v)
301 
302  double aX = ioKeyPoint._x;
303  double aY = ioKeyPoint._y;
304  int aS = (int)ioKeyPoint._scale;
305 
306  // get the sin/cos of the orientation
307  double ori_sin = sin(ioKeyPoint._ori);
308  double ori_cos = cos(ioKeyPoint._ori);
309 
310  if (aS < 1)
311  {
312  aS = 1;
313  }
314 
315  // compute the gradients in x and y for all regions of interest
316 
317  // we override the wave filter size later
318  WaveFilter aWaveFilter(10, _image);
319 
320  // compute features at each position and store in feature vector
321  int j = 0;
322  double middleMean = 0;
323 
324  for (int i = 0; i < _subRegions; i++)
325  {
326  // scale radius with aS.
327  double xS = _samples[i].x * aS;
328  double yS = _samples[i].y * aS;
329  // rotate sample point with the orientation
330  double aXSample = aX + xS * ori_cos - yS * ori_sin;
331  double aYSample = aY + xS * ori_sin + yS * ori_cos;
332  // make integer values from double ones
333  int aIntXSample = hugin_utils::roundi(aXSample);
334  int aIntYSample = hugin_utils::roundi(aYSample);
335  int aIntSampleSize = hugin_utils::roundi(_samples[i].size* aS);
336  int sampleArea = aIntSampleSize * aIntSampleSize;
337 
338  if (!aWaveFilter.checkBounds(aIntXSample, aIntYSample, aIntSampleSize))
339  {
340  ioKeyPoint._vec[j++] = 0;
341  ioKeyPoint._vec[j++] = 0;
342  //ioKeyPoint._vec[j++] = 0;
343  //ioKeyPoint._vec[j++] = 0;
344  if (i > 0)
345  {
346  ioKeyPoint._vec[j++] = 0;
347  }
348 #ifdef DEBUG_DESC
349  dlog << xS << " " << yS << " "
350  << aIntXSample << " " << aIntYSample << " " << aIntSampleSize << " "
351  << 0 << " " << 0 << " " << 0 << " " << 0 << " " << 0 << std::endl;
352 #endif
353  continue;
354  }
355 
356  double aWavX = aWaveFilter.getWx(aIntXSample, aIntYSample, aIntSampleSize) / sampleArea;
357  double aWavY = -aWaveFilter.getWy(aIntXSample, aIntYSample, aIntSampleSize) / sampleArea;
358 
359  double meanGray = aWaveFilter.getSum(aIntXSample, aIntYSample, aIntSampleSize) / sampleArea;
360 
361  if (i == 0)
362  {
363  middleMean = meanGray;
364  }
365 
366  // rotate extracted gradients
367 
368  // need to rotate in the other direction?
369 
370  double aWavXR = aWavX * ori_cos + aWavY * ori_sin;
371  double aWavYR = -aWavX * ori_sin + aWavY * ori_cos;
372  /*
373  double aWavXR = aWavX * ori_cos - aWavY * ori_sin;
374  double aWavYR = aWavX * ori_sin + aWavY * ori_cos;
375  */
376 
377 #ifdef DEBUG_DESC
378  dlog << xS << " " << yS << " "
379  << aIntXSample << " " << aIntYSample << " " << aIntSampleSize << " "
380  << aWavX << " " << aWavY << " " << meanGray << " " << aWavXR << " " << aWavYR << std::endl;
381 #endif
382 
383  // store descriptor
384  ioKeyPoint._vec[j++] = aWavXR;
385  ioKeyPoint._vec[j++] = aWavYR;
386  /*
387  if (aWavXR > 0) {
388  ioKeyPoint._vec[j++] = aWavXR;
389  ioKeyPoint._vec[j++] = 0;
390  } else {
391  ioKeyPoint._vec[j++] = 0;
392  ioKeyPoint._vec[j++] = -aWavXR;
393  }
394  if (aWavYR > 0) {
395  ioKeyPoint._vec[j++] = aWavYR;
396  ioKeyPoint._vec[j++] = 0;
397  } else {
398  ioKeyPoint._vec[j++] = 0;
399  ioKeyPoint._vec[j++] = -aWavYR;
400  }
401  */
402  if (i != 0)
403  {
404  ioKeyPoint._vec[j++] = meanGray - middleMean;
405  }
406  }
407 #ifdef DEBUG_DESC
408  dlog.close();
409 #endif
410 
411 }
412 
413 } // namespace lfeat
double _scale
Definition: KeyPoint.h:45
int roundi(T x)
Definition: hugin_math.h:73
bool checkBounds(int x, int y) const
Definition: WaveFilter.h:81
misc math function &amp; classes used by other parts of the program
void makeDescriptor(KeyPoint &ioKeyPoint) const
void createDescriptor(KeyPoint &ioKeyPoint) const
#define PI
Header file for Khan&#39;s deghosting algorithm Copyright (C) 2009 Lukáš Jirkovský l...
Definition: khan.h:43
LUT< 0, 83 > Exp1_2(exp, 0.5,-0.08)
double _ori
Definition: KeyPoint.h:48
double getWy(unsigned int x, unsigned int y)
Definition: WaveFilter.h:75
double getWx(unsigned int x, unsigned int y)
Definition: WaveFilter.h:69
CircularKeyPointDescriptor(Image &iImage, std::vector< int > rings=std::vector< int >(), std::vector< double > ring_radius=std::vector< double >(), std::vector< double > ring_gradient_width=std::vector< double >(), int ori_bins=18, double ori_sample_scale=4, int ori_gridsize=11)
double getSum(unsigned int x, unsigned int y, int scale)
Definition: WaveFilter.h:88
void allocVector(int iSize)
Definition: KeyPoint.h:96
int assignOrientation(KeyPoint &ioKeyPoint, double angles[4]) const
static bool Normalize(double *iVec, int iLen)
Definition: MathStuff.cpp:84
#define M_PI
Definition: GaborFilter.cpp:34
double * _vec
Definition: KeyPoint.h:50