vigra - vigra/gaborfilter.hxx Source File

[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

gaborfilter.hxx |

1 /************************************************************************/
2 /* */
3 /* Copyright 2002-2004 by Ullrich Koethe and Hans Meine */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35
36
37 #ifndef VIGRA_GABORFILTER_HXX
38 #define VIGRA_GABORFILTER_HXX
39
40 #include "imagecontainer.hxx"
41 #include "config.hxx"
42 #include "stdimage.hxx"
43 #include "copyimage.hxx"
44 #include "transformimage.hxx"
45 #include "combineimages.hxx"
46 #include "utilities.hxx"
47 #include "multi_shape.hxx"
48
49 #include <functional>
50 #include <vector>
51 #include <cmath>
52
53 namespace vigra {
54
55 /** \addtogroup GaborFilter Gabor Filter
56 Functions to create or apply gabor filter (latter based on FFTW).
57 */
58 //@{
59
60 /********************************************************/
61 /* */
62 /* createGaborFilter */
63 /* */
64 /********************************************************/
65
66 /** \brief Create a gabor filter in frequency space.
67
68 The orientation is given in radians, the other parameters are the
69 center frequency (for example 0.375 or smaller) and the two
70 angular and radial sigmas of the gabor filter. (See \ref
71 angularGaborSigma() for an explanation of possible values.)
72
73 The energy of the filter is explicitly normalized to 1.0.
74
75 <b> Declarations:</b>
76
77 pass 2D array views:
78 \code
79 namespace vigra {
80 template <class T, class S>
81 void
82 createGaborFilter(MultiArrayView<2, T, S> dest,
83 double orientation, double centerFrequency,
84 double angularSigma, double radialSigma);
85 }
86 \endcode
87
88 \deprecatedAPI{createGaborFilter}
89 pass \ref ImageIterators and \ref DataAccessors :
90 \code
91 namespace vigra {
92 template <class DestImageIterator, class DestAccessor>
93 void createGaborFilter(DestImageIterator destUpperLeft,
94 DestImageIterator destLowerRight,
95 DestAccessor da,
96 double orientation, double centerFrequency,
97 double angularSigma, double radialSigma)
98 }
99 \endcode
100 use argument objects in conjunction with \ref ArgumentObjectFactories :
101 \code
102 namespace vigra {
103 template <class DestImageIterator, class DestAccessor>
104 void createGaborFilter(triple<DestImageIterator,
105 DestImageIterator,
106 DestAccessor> dest,
107 double orientation, double centerFrequency,
108 double angularSigma, double radialSigma)
109 }
110 \endcode
111 \deprecatedEnd
112
113 <b> Usage:</b>
114
115 <b>\#include</b> <vigra/gaborfilter.hxx><br/>
116 Namespace: vigra
117
118 \code
119 MultiArray<2, float> gabor(w,h);
120
121 createGaborFilter(gabor, orient, freq,
122 angularGaborSigma(directionCount, freq),
123 radialGaborSigma(freq));
124 \endcode
125
126 \deprecatedUsage{createGaborFilter}
127 \code
128 vigra::FImage gabor(w,h);
129
130 vigra::createGaborFilter(destImageRange(gabor), orient, freq,
131 angularGaborSigma(directionCount, freq),
132 radialGaborSigma(freq));
133 \endcode
134 \deprecatedEnd
135 */
136 doxygen_overloaded_function(template <...> void createGaborFilter)
137
138 template <class DestImageIterator, class DestAccessor>
139 void createGaborFilter(DestImageIterator destUpperLeft,
140 DestImageIterator destLowerRight, DestAccessor da,
141 double orientation, double centerFrequency,
142 double angularSigma, double radialSigma)
143 {
144 int w = int(destLowerRight.x - destUpperLeft.x);
145 int h = int(destLowerRight.y - destUpperLeft.y);
146
147 double squaredSum = 0.0;
148 double cosTheta= VIGRA_CSTD::cos(orientation);
149 double sinTheta= VIGRA_CSTD::sin(orientation);
150
151 double radialSigma2 = radialSigma*radialSigma;
152 double angularSigma2 = angularSigma*angularSigma;
153
154 double wscale = w % 1 ?
155 1.0f / (w-1) :
156 1.0f / w;
157 double hscale = h % 1 ?
158 1.0f / (h-1) :
159 1.0f / h;
160
161 int dcX= (w+1)/2, dcY= (h+1)/2;
162
163 double u, v;
164 for ( int y=0; y<h; y++, destUpperLeft.y++ )
165 {
166 typename DestImageIterator::row_iterator dix = destUpperLeft.rowIterator();
167
168 v = hscale * ((h - (y - dcY))%h - dcY);
169 for ( int x=0; x<w; x++, dix++ )
170 {
171 u= wscale*((x - dcX + w)%w - dcX);
172
173 double uu = cosTheta*u + sinTheta*v - centerFrequency;
174 double vv = -sinTheta*u + cosTheta*v;
175 double gabor;
176
177 gabor = VIGRA_CSTD::exp(-0.5*(uu*uu / radialSigma2 + vv*vv / angularSigma2));
178 squaredSum += gabor * gabor;
179 da.set( gabor, dix );
180 }
181 }
182 destUpperLeft.y -= h;
183
184 // clear out DC value and remove it from the squared sum
185 double dcValue = da(destUpperLeft);
186 squaredSum -= dcValue * dcValue;
187 da.set( 0.0, destUpperLeft );
188
189 // normalize energy to one
190 double factor = VIGRA_CSTD::sqrt(squaredSum);
191 for ( int y=0; y<h; y++, destUpperLeft.y++ )
192 {
193 typename DestImageIterator::row_iterator dix = destUpperLeft.rowIterator();
194
195 for ( int x=0; x<w; x++, dix++ )
196 {
197 da.set( da(dix) / factor, dix );
198 }
199 }
200 }
201
202 template <class DestImageIterator, class DestAccessor>
203 inline void
204 createGaborFilter(triple<DestImageIterator, DestImageIterator, DestAccessor> dest,
205 double orientation, double centerFrequency,
206 double angularSigma, double radialSigma)
207 {
208 createGaborFilter(dest.first, dest.second, dest.third,
209 orientation, centerFrequency,
210 angularSigma, radialSigma);
211 }
212
213 template <class T, class S>
214 inline void
215 createGaborFilter(MultiArrayView<2, T, S> dest,
216 double orientation, double centerFrequency,
217 double angularSigma, double radialSigma)
218 {
219 createGaborFilter(destImageRange(dest),
220 orientation, centerFrequency,
221 angularSigma, radialSigma);
222 }
223
224 /********************************************************/
225 /* */
226 /* radialGaborSigma */
227 /* */
228 /********************************************************/
229
230 /** \brief Calculate sensible radial sigma for given parameters.
231
232 For a brief introduction what is meant with "sensible" sigmas, see
233 \ref angularGaborSigma().
234
235 <b> Declaration:</b>
236
237 \code
238 namespace vigra {
239 double radialGaborSigma(double centerFrequency)
240 }
241 \endcode
242 */
243
244 inline double radialGaborSigma(double centerFrequency)
245 {
246 double sfactor = 3.0 * VIGRA_CSTD::sqrt(VIGRA_CSTD::log(4.0));
247 return centerFrequency / sfactor;
248 }
249
250 /********************************************************/
251 /* */
252 /* angularGaborSigma */
253 /* */
254 /********************************************************/
255
256 /** \brief Calculate sensible angular sigma for given parameters.
257
258 "Sensible" means: If you use a range of gabor filters for feature
259 detection, you are interested in minimal redundancy. This is hard
260 to define but one possible try is to arrange the filters in
261 frequency space, so that the half-peak-magnitude ellipses touch
262 each other.
263
264 To do so, you must know the number of directions (first parameter
265 for the angular sigma function) and the center frequency of the
266 filter you want to calculate the sigmas for.
267
268 The exact formulas are:
269 \code
270 sigma_radial= 1/sqrt(ln(4)) * centerFrequency/3
271 \endcode
272
273 \code
274 sigma_angular= 1/sqrt(ln(4)) * tan(pi/(directions*2))
275 * sqrt(8/9) * centerFrequency
276 \endcode
277
278 <b> Declaration:</b>
279
280 \code
281 namespace vigra {
282 double angularGaborSigma(int directionCount, double centerFrequency)
283 }
284 \endcode
285 */
286
287 inline double angularGaborSigma(int directionCount, double centerFrequency)
288 {
289 return VIGRA_CSTD::tan(M_PI/directionCount/2.0) * centerFrequency
290 * VIGRA_CSTD::sqrt(8.0 / (9 * VIGRA_CSTD::log(4.0)));
291 }
292
293 /********************************************************/
294 /* */
295 /* GaborFilterFamily */
296 /* */
297 /********************************************************/
298
299 /** \brief Family of gabor filters of different scale and direction.
300
301 A GaborFilterFamily can be used to quickly create a whole family
302 of gabor filters in frequency space. Especially useful in
303 conjunction with \ref applyFourierFilterFamily, since it's derived
304 from \ref ImageArray.
305
306 The filter parameters are chosen to make the center frequencies
307 decrease in octaves with increasing scale indices, and to make the
308 half-peak-magnitude ellipses touch each other to somewhat reduce
309 redundancy in the filter answers. This is done by using \ref
310 angularGaborSigma() and \ref radialGaborSigma(), you'll find more
311 information there.
312
313 The template parameter ImageType should be a scalar image type suitable for filling in
314
315 <b>\#include</b> <vigra/gaborfilter.hxx><br/>
316 Namespace: vigra
317 */
318 template <class ImageType,
319 class Alloc = typename ImageType::allocator_type::template rebind<ImageType>::other >
320 class GaborFilterFamily
321 : public ImageArray<ImageType, Alloc>
322 {
323 typedef ImageArray<ImageType, Alloc> ParentClass;
324 int scaleCount_, directionCount_;
325 double maxCenterFrequency_;
326
327 protected:
328 void initFilters()
329 {
330 for(int direction= 0; direction<directionCount_; direction++)
331 for(int scale= 0; scale<scaleCount_; scale++)
332 {
333 double angle = direction * M_PI / directionCount();
334 double centerFrequency =
335 maxCenterFrequency_ / VIGRA_CSTD::pow(2.0, (double)scale);
336 createGaborFilter(destImageRange(this->images_[filterIndex(direction, scale)]),
337 angle, centerFrequency,
338 angularGaborSigma(directionCount(), centerFrequency),
339 radialGaborSigma(centerFrequency));
340 }
341 }
342
343 public:
344 enum { stdFilterSize= 128, stdDirectionCount= 6, stdScaleCount= 4 };
345
346 /** Constructs a family of gabor filters in frequency
347 space. The filters will be calculated on construction, so
348 it makes sense to provide good parameters right now
349 although they can be changed later, too. If you leave them
350 out, the defaults are a \ref directionCount of 6, a \ref
351 scaleCount of 4 and a \ref maxCenterFrequency of
352 3/8(=0.375).
353 */
354 GaborFilterFamily(const Diff2D & size,
355 int directionCount = stdDirectionCount, int scaleCount = stdScaleCount,
356 double maxCenterFrequency = 3.0/8.0,
357 Alloc const & alloc = Alloc())
358 : ParentClass(directionCount*scaleCount, size, alloc),
359 scaleCount_(scaleCount),
360 directionCount_(directionCount),
361 maxCenterFrequency_(maxCenterFrequency)
362 {
363 initFilters();
364 }
365
366 /** Convenience variant of the above constructor taking width
367 and height separately. Also, this one serves as default
368 constructor constructing 128x128 pixel filters.
369 */
370 GaborFilterFamily(int width= stdFilterSize, int height= -1,
371 int directionCount = stdDirectionCount, int scaleCount = stdScaleCount,
372 double maxCenterFrequency = 3.0/8.0,
373 Alloc const & alloc = Alloc())
374 : ParentClass(directionCount*scaleCount,
375 Size2D(width, height > 0 ? height : width), alloc),
376 scaleCount_(scaleCount),
377 directionCount_(directionCount),
378 maxCenterFrequency_(maxCenterFrequency)
379 {
380 initFilters();
381 }
382
383 /** Return the index of the filter with the given direction and
384 scale in this ImageArray. direction must in the range
385 0..directionCount()-1 and scale in the range
386 0..rangeCount()-1. This is useful for example if you used
387 \ref applyFourierFilterFamily() and got a resulting
388 ImageArray which still has the same order of images, but no
389 \ref getFilter() method anymore.
390 */
391 int filterIndex(int direction, int scale) const
392 {
393 return scale*directionCount()+direction;
394 }
395
396 /** Return the filter with the given direction and
397 scale. direction must in the range 0..directionCount()-1
398 and scale in the range 0..rangeCount()-1.
399 <tt>filters.getFilter(direction, scale)</tt> is the same as
400 <tt>filters[filterIndex(direction, scale)]</tt>.
401 */
402 ImageType const & getFilter(int direction, int scale) const
403 {
404 return this->images_[filterIndex(direction, scale)];
405 }
406
407 /** Resize all filters (causing their recalculation).
408 */
409 virtual void resizeImages(const Diff2D &newSize)
410 {
411 ParentClass::resizeImages(newSize);
412 initFilters();
413 }
414
415 /** Query the number of filter scales available.
416 */
417 int scaleCount() const
418 { return scaleCount_; }
419
420 /** Query the number of filter directions available.
421 */
422 int directionCount() const
423 { return directionCount_; }
424
425 /** Change the number of directions / scales. This causes the
426 recalculation of all filters.
427 */
428 void setDirectionScaleCounts(int directionCount, int scaleCount)
429 {
430 this->resize(directionCount * scaleCount);
431 scaleCount_ = scaleCount;
432 directionCount_ = directionCount;
433 initFilters();
434 }
435
436 /** Return the center frequency of the filter(s) with
437 scale==0. Filters with scale>0 will have a center frequency
438 reduced in octaves:
439 <tt>centerFrequency= maxCenterFrequency / 2.0^scale</tt>
440 */
441 double maxCenterFrequency()
442 { return maxCenterFrequency_; }
443
444 /** Change the center frequency of the filter(s) with
445 scale==0. See \ref maxCenterFrequency().
446 */
447 void setMaxCenterFrequency(double maxCenterFrequency)
448 {
449 maxCenterFrequency_ = maxCenterFrequency;
450 initFilters();
451 }
452 };
453
454 //@}
455
456 } // namespace vigra
457
458 #endif // VIGRA_GABORFILTER_HXX

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de) Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany | html generated using doxygen and Python vigra 1.10.0 (Mon Nov 18 2013)