Hugin trunk 0.1
Loading...
Searching...
No Matches
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"
40
41//#define DEBUG_DESC
42//#define DEBUG_ROT
43
44namespace lfeat
45{
46LUT<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);
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;
129
130 _ori_hist = new double[_ori_nbins + 2];
131
132}
133
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
153
154 // normalize
156}
157
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
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.
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
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
337
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
358
360
361 if (i == 0)
362 {
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
#define M_PI
int assignOrientation(KeyPoint &ioKeyPoint, double angles[4]) const
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)
void createDescriptor(KeyPoint &ioKeyPoint) const
void makeDescriptor(KeyPoint &ioKeyPoint) const
misc math function & classes used by other parts of the program
#define PI
Header file for Khan's deghosting algorithm Copyright (C) 2009 Lukáš Jirkovský l.jirkovsky@gmail....
Definition khan.h:43
int roundi(T x)
Definition hugin_math.h:73
LUT< 0, 83 > Exp1_2(exp, 0.5, -0.08)
static bool Normalize(double *iVec, int iLen)
Definition MathStuff.cpp:84
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41