hatdesc.cpp
4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// hatdesc.cpp: Model for HAT descriptors.
// This does a grid search using the descriptors defined hat.cpp.
// It also caches the descriptors for fast reuse.
//
// Copyright (C) 2005-2013, Stephen Milborrow
#include "stasm.h"
#include "stasmhash.h"
#define CACHE 1 // define to 0 if your compiler doesn't support hash_map
// Stasm runs faster if 1
namespace stasm
{
// hat_g is global because we initialize the HAT internal data
// (grads and orients etc.) once for the entire pyramid level.
// Initialized in InitHatLevData.
//static const Hat &hat_g;
//-----------------------------------------------------------------------------
#if CACHE
// For speed, we cache the HAT descriptors, so we have the descriptor at
// hand if we revisit an xy position in the image which is very common in ASMs.
// (Note: an implementation with cache_g as a vector<vector VEC>> was slower.)
//static hash_map<unsigned, VEC> cache_g; // cached descriptors
static const bool TRACE_CACHE = 0; // for checking cache hit rate
//static int ncalls_g, nhits_g; // only used if TRACE_CACHE
static unsigned Key(int x, int y) // pack x,y into 32 bits for cache key
{
return ((y & 0xffff) << 16) | (x & 0xffff);
}
static double GetHatFit( // args same as non CACHE version, see below
int x, // in
int y, // in
const HatFit hatfit, // in
const Hat &hat,
StasmHash &hash)
{
const double* descbuf = NULL; // the HAT descriptor
const unsigned key(Key(x, y));
QHash<unsigned, VEC>::const_iterator it = hash.find(key);
if (it != hash.end()) {
descbuf = Buf(it.value());
} else {
const VEC desc(hat.Desc_(x, y));
hash[key] = desc; // remember descriptor for possible re-use
descbuf = Buf(desc);
}
return hatfit(descbuf);
}
#else // not CACHE
// Get the HAT descriptor at the given ipoint and x,y coords, and return
// how well the descriptor matches the model. High fit means good match.
static double GetHatFit(
int x, // in: image x coord (may be off image)
int y, // in: image y coord (may be off image)
const HatFit hatfit) // in: func to estimate descriptor match
{
return hatfit(Buf(hat_g.Desc_(x, y)));
}
#endif // not CACHE
static int round2(double x) // return closest int to x that is divisible by 2
{
return 2 * cvRound(x / 2);
}
static int PatchWidth( // patchwidth at the given pyramid level
int ilev) // in: pyramid level (0 is full size)
{
return HAT_PATCH_WIDTH + round2(ilev * HAT_PATCH_WIDTH_ADJ);
}
Hat InitHatLevData( // init the global HAT data needed for this pyr level
const Image& img, // in
int ilev) // in
{
Hat hat;
if (ilev <= HAT_START_LEV) // we use HATs only at upper pyr levs
{
hat.Init_(img, PatchWidth(ilev));
}
return hat;
}
// Note 1: The image is not passed directly to this function. Instead this
// function accesses the image gradient magnitude and orientation stored in
// fields of the global variable hat_g and previously initialized by the
// call to InitHatLevData.
//
// Note 2: If OpenMP is enabled, multiple instances of this function will be
// called concurrently (each call will have a different value of x and y). Thus
// this function and its callees do not modify any data that is not on the stack.
void HatDescSearch( // search in a grid around the current landmark
double& x, // io: (in: old position of landmark, out: new position)
double& y, // io:
const HatFit hatfit, // in: func to estimate descriptor match
const Hat &hat,
StasmHash &hash)
{
// If HAT_SEARCH_RESOL is 2, force x,y positions to be divisible
// by 2 to increase cache hit rate. This increases the mean hit rate
// from about 67% to 88% and barely affects landmark accuracy.
int ix = HAT_SEARCH_RESOL == 2? round2(x): cvRound(x);
int iy = HAT_SEARCH_RESOL == 2? round2(y): cvRound(y);
double fit_best = -FLT_MAX;
int xoffset_best = 0, yoffset_best = 0; // in pixels
for (int yoffset = -HAT_MAX_OFFSET;
yoffset <= HAT_MAX_OFFSET;
yoffset += HAT_SEARCH_RESOL)
{
for (int xoffset = -HAT_MAX_OFFSET;
xoffset <= HAT_MAX_OFFSET;
xoffset += HAT_SEARCH_RESOL)
{
const double fit = GetHatFit(ix + xoffset, iy + yoffset, hatfit, hat, hash);
if (fit > fit_best)
{
fit_best = fit;
xoffset_best = xoffset;
yoffset_best = yoffset;
}
}
}
x += xoffset_best;
y += yoffset_best;
}
} // namespace stasm