1 /** @file scalespace.c
2 ** @brief Scale Space - Definition
3 ** @author Karel Lenc
4 ** @author Andrea Vedaldi
5 ** @author Michal Perdoch
6 **/
7
8 /*
9 Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
10 All rights reserved.
11
12 This file is part of the VLFeat library and is made available under
13 the terms of the BSD license (see the COPYING file).
14 */
15
16 /**
17 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
18 @page scalespace Gaussian Scale Space (GSS)
19 @author Karel Lenc
20 @author Andrea Vedaldi
21 @author Michal Perdoch
22 @tableofcontents
23 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
24
25 @ref scalespace.h implements a Gaussian scale space, a data structure
26 representing an image at multiple resolutions
27 @cite{witkin83scale-space} @cite{koenderink84the-structure}
28 @cite{lindeberg94scale-space}. Scale spaces have many use, including
29 the detection of co-variant local features
30 @cite{lindeberg98principles} such as SIFT, Hessian-Affine,
31 Harris-Affine, Harris-Laplace, etc. @ref scalespace-starting
32 demonstreates how to use the C API to compute the scalespace of an
33 image. For further details refer to:
34
35 - @subpage scalespace-fundamentals
36
37 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
38 @section scalespace-starting Getting started
39 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
40
41 Given an input image `image`, the following example uses the
42 ::VlScaleSpace object to compute its Gaussian scale space and return
43 the image `level` at scale `(o,s)`, where `o` is the octave and `s` is
44 the octave subdivision or sublevel:
45
46 @code
47 float* level ;
48 VlScaleSpace ss = vl_scalespace_new(imageWidth, imageHeight) ;
49 vl_scalespace_put_image(ss, image) ;
50 level = vl_scalespace_get_level(ss, o, s) ;
51 @endcode
52
53 The image `level` is obtained by convolving `image` by a Gaussian
54 filter of isotropic standard deviation given by
55
56 @code
57 double sigma = vl_scalespace_get_sigma(ss, o, s) ;
58 @endcode
59
60 The resolution of `level` is in general different from the resolution
61 of `image` and is determined by the octave `o`. It can be obtained as
62 follows:
63
64 @code
65 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(ss, o) ;
66 ogeom.width // width of level (in number of pixels)
67 ogeom.height // height of level (in number of pixels)
68 ogeom.step // spatial sampling step
69 @endcode
70
71 The parameter `ogeom.step` is the sampling step relatively to the
72 sampling of the input image `image`. The ranges of valid octaves and
73 scale sublevels can be obtained as
74
75 @code
76 VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(ss) ;
77 geom.firstOctave // Index of the fisrt octave
78 geom.lastOctave // Index of the last octave
79 geom.octaveResolution ; // Number of octave subdivisions
80 geom.octaveFirstSubdivision // Index of the first octave subdivision
81 geom.octaveLastSubdivision // Index of the last octave subdivision
82 @endcode
83
84 So for example `o` minimum value is `geom.firstOctave` and maximum
85 value is `geom.lastOctave`. The subdivision index `s` naturally spans
86 the range 0 to `geom.octaveResolution-1`. However, the scale space
87 object is flexible in that it allows different ranges of subdivisions
88 to be computed and `s` varies in the range
89 `geom.octaveFirstSubdivision` to `geom.octaveLastSubdivision`. See
90 @ref scalespace-fundamentals for further details.
91
92 The geometry of the scale space can be customized upon creation, as
93 follows:
94
95 @code
96 VlScaleSpaceGeometry geom = vl_scalespace_get_default_geometry(imageWidth, imageHeight) ;
97 geom.firstOctave = -1 ;
98 geom.octaveFirstSubdivision = -1 ;
99 geom.octaveLastSubdivision = geom.octaveResolution ;
100 VlScaleSpacae ss = vl_scalespace_new_with_geometry (geom) ;
101 @endcode
102
103 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
104 @page scalespace-fundamentals Gaussian scale space fundamentals
105 @tableofcontents
106 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
107
108 This page discusses the notion of *Gaussian scale space* and the
109 relative data structure. For the C API see @ref scalespace.h and @ref
110 scalespace-starting.
111
112 A *scale space* is representation of an image at multiple resolution
113 levels. An image is a function $\ell(x,y)$ of two coordinates $x$,
114 $y$; the scale space $\ell(x,y,\sigma)$ adds a third coordinate
115 $\sigma$ indexing the *scale*. Here the focus is the Gaussian scale
116 space, where the image $\ell(x,y,\sigma)$ is obtained by smoothing
117 $\ell(x,y)$ by a Gaussian kernel of isotropic standard deviation
118 $\sigma$.
119
120 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
121 @section scalespace-definition Scale space definition
122 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
123
124 Formally, the *Gaussian scale space* of an image $\ell(x,y)$ is
125 defined as
126
127 \[
128 \ell(x,y,\sigma) =
129 [g_{\sigma} * \ell](x,y,\sigma)
130 \]
131
132 where $g_\sigma$ denotes a 2D Gaussian kernel of isotropic standard
133 deviation $\sigma$:
134
135 \[
136 g_{\sigma}(x,y) = \frac{1}{2\pi\sigma^2}
137 \exp\left(
138 - \frac{x^2 + y^2}{2\sigma^2}
139 \right).
140 \]
141
142 An important detail is that the algorithm computing the scale space
143 assumes that the input image $\ell(x,y)$ is pre-smoothed, roughly
144 capturing the effect of the finite pixel size in a CCD. This is
145 modelled by assuming that the input is not $\ell(x,y)$, but
146 $\ell(x,y,\sigma_n)$, where $\sigma_n$ is a *nominal smoothing*,
147 usually taken to be 0.5 (half a pixel standard deviation). This also
148 means that $\sigma = \sigma_n = 0.5$ is the *finest scale* that can
149 actually be computed.
150
151 The scale space structure stores samples of the function
152 $\ell(x,y,\sigma)$. The density of the sampling of the spatial
153 coordinates $x$ and $y$ is adjusted as a function of the scale
154 $\sigma$, corresponding to the intuition that images at a coarse
155 resolution can be sampled more coarsely without loss of
156 information. Thus, the scale space has the structure of a *pyramid*: a
157 collection of digital images sampled at progressively coarser spatial
158 resolution and hence of progressively smaller size (in pixels).
159
160 The following figure illustrates the scale space pyramid structure:
161
162 @image html scalespace-basic.png "A scalespace structure with 2 octaves and S=3 subdivisions per octave"
163
164 The pyramid is organised in a number of *octaves*, indexed by a
165 parameter `o`. Each octave is further subdivided into *sublevels*,
166 indexed by a parameter `s`. These are related to the scale $\sigma$ by
167 the equation
168
169 \[
170 \sigma(s,o) = \sigma_o 2^{\displaystyle o + \frac{s}{\mathtt{octaveResolution}}}
171 \]
172
173 where `octaveResolution` is the resolution of the octave subsampling
174 $\sigma_0$ is the *base smoothing*.
175
176 At each octave the spatial resolution is doubled, in the sense that
177 samples are take with a step of
178 \[
179 \mathtt{step} = 2^o.
180 \]
181 Hence, denoting as `level[i,j]` the corresponding samples, one has
182 $\ell(x,y,\sigma) = \mathtt{level}[i,j]$, where
183 \[
184 (x,y) = (i,j) \times \mathtt{step},
185 \quad
186 \sigma = \sigma(o,s),
187 \quad
188 0 \leq i < \mathtt{lwidth},
189 \quad
190 0 \leq j < \mathtt{lheight},
191 \]
192 where
193 \[
194 \mathtt{lwidth} = \lfloor \frac{\mathtt{width}}{2^\mathtt{o}}\rfloor, \quad
195 \mathtt{lheight} = \lfloor \frac{\mathtt{height}}{2^\mathtt{o}}\rfloor.
196 \]
197
198 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
199 @section scalespace-geometry Scale space geometry
200 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
201
202 In addition to the parameters discussed above, the geometry of the
203 data stored in a scale space structure depends on the range of
204 allowable octaves `o` and scale sublevels `s`.
205
206 While `o` may range in any reasonable value given the size of the
207 input image `image`, usually its minimum value is either 0 or -1. The
208 latter corresponds to doubling the resolution of the image in the
209 first octave of the scale space and it is often used in feature
210 extraction. While there is no information added to the image by
211 upsampling in this manner, fine scale filters, including derivative
212 filters, are much easier to compute by upsalmpling first. The maximum
213 practical value is dictated by the image resolution, as it should be
214 $2^o\leq\min\{\mathtt{width},\mathtt{height}\}$. VLFeat has the
215 flexibility of specifying the range of `o` using the `firstOctave` and
216 `lastOctave` parameters of the ::VlScaleSpaceGeometry structure.
217
218 The sublevel `s` varies naturally in the range
219 $\{0,\dots,\mathtt{octaveResolution}-1\}$. However, it is often
220 convenient to store a few extra levels per octave (e.g. to compute the
221 local maxima of a function in scale or the Difference of Gaussian
222 cornerness measure). Thus VLFeat scale space structure allows this
223 parameter to vary in an arbitrary range, specified by the parameters
224 `octaveFirstSubdivision` and `octaveLastSubdivision` of
225 ::VlScaleSpaceGeometry.
226
227 Overall the possible values of the indexes `o` and `s` are:
228
229 \[
230 \mathtt{firstOctave} \leq o \leq \mathtt{lastOctave},
231 \qquad
232 \mathtt{octaveFirstSubdivision} \leq s \leq \mathtt{octaveLastSubdivision}.
233 \]
234
235 Note that, depending on these ranges, there could be *redundant pairs*
236 of indexes `o` and `s` that represent the *same* pyramid level at more
237 than one sampling resolution. In practice, the ability to generate
238 such redundant information is very useful in algorithms using
239 scalespaces, as coding multiscale operations using a fixed sampling
240 resolution is far easier. For example, the DoG feature detector
241 computes the scalespace with three redundant levels per octave, as
242 follows:
243
244 @image html scalespace.png "A scalespace containing redundant representation of certain scale levels."
245
246 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
247 @section scalespace-algorithm Algorithm and limitations
248 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
249
250 Given $\ell(x,y,\sigma_n)$, any of a vast number digitial filtering
251 techniques can be used to compute the scale levels. Presently, VLFeat
252 uses a basic FIR implementation of the Gaussian filters.
253
254 The FIR implementation is obtained by sampling the Gaussian function
255 and re-normalizing it to have unit norm. This simple construction does
256 not account properly for sampling effect, which may be a problem for
257 very small Gausisan kernels. As a rule of thumb, such filters work
258 sufficiently well for, say, standard deviation $\sigma$ at least 1.6
259 times the sampling step. A work around to apply this basic FIR
260 implementation to very small Gaussian filters is to upsample the image
261 first.
262
263 The limitations on the FIR filters have relatively important for the
264 pyramid construction, as the latter is obtained by *incremental
265 smoothing*: each successive level is obtained from the previous one by
266 adding the needed amount of smoothing. In this manner, the size of the
267 FIR filters remains small, which makes them efficient; at the same
268 time, for what discussed, excessively small filters are not
269 represented properly.
270
271 */
272
273 #include "scalespace.h"
274 #include "mathop.h"
275
276 #include <assert.h>
277 #include <stdlib.h>
278 #include <string.h>
279 #include <math.h>
280 #include <stdio.h>
281
282 /** @file scalespace.h
283 ** @struct VlScaleSpace
284 ** @brief Scale space class
285 **
286 ** This is an opaque class used to compute the scale space of an
287 ** image.
288 **/
289
290 struct _VlScaleSpace
291 {
292 VlScaleSpaceGeometry geom ; /**< Geometry of the scale space */
293 float **octaves ; /**< Data */
294 } ;
295
296 /* ---------------------------------------------------------------- */
297 /** @brief Get the default geometry for a given image size.
298 ** @param width image width.
299 ** @param height image height.
300 ** @return the default scale space geometry.
301 **
302 ** Both @a width and @a height must be at least one pixel wide.
303 **/
304
305 VlScaleSpaceGeometry
vl_scalespace_get_default_geometry(vl_size width,vl_size height)306 vl_scalespace_get_default_geometry (vl_size width, vl_size height)
307 {
308 VlScaleSpaceGeometry geom ;
309 assert(width >= 1) ;
310 assert(height >= 1) ;
311 geom.width = width ;
312 geom.height = height ;
313 geom.firstOctave = 0 ;
314 geom.lastOctave = VL_MAX(floor(vl_log2_d(VL_MIN(width, height))) - 3, 0) ;
315 geom.octaveResolution= 3 ;
316 geom.octaveFirstSubdivision = 0 ;
317 geom.octaveLastSubdivision = geom.octaveResolution - 1 ;
318 geom.baseScale = 1.6 * pow(2.0, 1.0 / geom.octaveResolution) ;
319 geom.nominalScale = 0.5 ;
320 return geom ;
321 }
322
323 #define is_valid_geometry(geom) (\
324 geom.firstOctave <= geom.lastOctave && \
325 geom.octaveResolution >= 1 && \
326 geom.octaveFirstSubdivision <= geom.octaveLastSubdivision && \
327 geom.baseScale >= 0.0 && \
328 geom.nominalScale >= 0.0)
329
330 /** @brief Check scale space geometries for equality
331 ** @param a first geometry.
332 ** @param b second geometry.
333 ** @return true if equal.
334 **/
335
336 vl_bool
vl_scalespacegeometry_is_equal(VlScaleSpaceGeometry a,VlScaleSpaceGeometry b)337 vl_scalespacegeometry_is_equal (VlScaleSpaceGeometry a,
338 VlScaleSpaceGeometry b)
339 {
340 return
341 a.width == b.width &&
342 a.height == b.height &&
343 a.firstOctave == b.firstOctave &&
344 a.lastOctave == b.lastOctave &&
345 a.octaveResolution == b.octaveResolution &&
346 a.octaveFirstSubdivision == b.octaveLastSubdivision &&
347 a.baseScale == b.baseScale &&
348 a.nominalScale == b.nominalScale ;
349 }
350
351 /** @brief Get the geometry of the scale space.
352 ** @param self object.
353 ** @return the scale space geometry.
354 **/
355
356 VlScaleSpaceGeometry
vl_scalespace_get_geometry(VlScaleSpace const * self)357 vl_scalespace_get_geometry (VlScaleSpace const * self)
358 {
359 return self->geom ;
360 }
361
362 /** @brief Get the geometry of an octave of the scalespace.
363 ** @param self object.
364 ** @param o octave index.
365 ** @return the geometry of octave @a o.
366 **/
367
368 VlScaleSpaceOctaveGeometry
vl_scalespace_get_octave_geometry(VlScaleSpace const * self,vl_index o)369 vl_scalespace_get_octave_geometry (VlScaleSpace const * self, vl_index o)
370 {
371 VlScaleSpaceOctaveGeometry ogeom ;
372 ogeom.width = VL_SHIFT_LEFT(self->geom.width, -o) ;
373 ogeom.height = VL_SHIFT_LEFT(self->geom.height, -o) ;
374 ogeom.step = pow(2.0, o) ;
375 return ogeom ;
376 }
377
378 /** @brief Get the data of a scale space level
379 ** @param self object.
380 ** @param o octave index.
381 ** @param s level index.
382 ** @return pointer to the data for octave @a o, level @a s.
383 **
384 ** The octave index @a o must be in the range @c firstOctave
385 ** to @c lastOctave and the scale index @a s must be in the
386 ** range @c octaveFirstSubdivision to @c octaveLastSubdivision.
387 **/
388
389 float *
vl_scalespace_get_level(VlScaleSpace * self,vl_index o,vl_index s)390 vl_scalespace_get_level (VlScaleSpace *self, vl_index o, vl_index s)
391 {
392 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self,o) ;
393 float * octave ;
394 assert(self) ;
395 assert(o >= self->geom.firstOctave) ;
396 assert(o <= self->geom.lastOctave) ;
397 assert(s >= self->geom.octaveFirstSubdivision) ;
398 assert(s <= self->geom.octaveLastSubdivision) ;
399
400 octave = self->octaves[o - self->geom.firstOctave] ;
401 return octave + ogeom.width * ogeom.height * (s - self->geom.octaveFirstSubdivision) ;
402 }
403
404 /** @brief Get the data of a scale space level (const)
405 ** @param self object.
406 ** @param o octave index.
407 ** @param s level index.
408 ** @return pointer to the data for octave @a o, level @a s.
409 **
410 ** This function is the same as ::vl_scalespace_get_level but reutrns
411 ** a @c const pointer to the data.
412 **/
413
414 float const *
vl_scalespace_get_level_const(VlScaleSpace const * self,vl_index o,vl_index s)415 vl_scalespace_get_level_const (VlScaleSpace const * self, vl_index o, vl_index s)
416 {
417 return vl_scalespace_get_level((VlScaleSpace*)self, o, s) ;
418 }
419
420 /** ------------------------------------------------------------------
421 ** @brief Get the scale of a given octave and sublevel
422 ** @param self object.
423 ** @param o octave index.
424 ** @param s sublevel index.
425 **
426 ** The function returns the scale $\sigma(o,s)$ as a function of the
427 ** octave index @a o and sublevel @a s.
428 **/
429
430 double
vl_scalespace_get_level_sigma(VlScaleSpace const * self,vl_index o,vl_index s)431 vl_scalespace_get_level_sigma (VlScaleSpace const *self, vl_index o, vl_index s)
432 {
433 return self->geom.baseScale * pow(2.0, o + (double) s / self->geom.octaveResolution) ;
434 }
435
436 /** ------------------------------------------------------------------
437 ** @internal @brief Upsample the rows and take the transpose
438 ** @param destination output image.
439 ** @param source input image.
440 ** @param width input image width.
441 ** @param height input image height.
442 **
443 ** The output image has dimensions @a height by 2 @a width (so the
444 ** destination buffer must be at least as big as two times the
445 ** input buffer).
446 **
447 ** Upsampling is performed by linear interpolation.
448 **/
449
450 static void
copy_and_upsample(float * destination,float const * source,vl_size width,vl_size height)451 copy_and_upsample
452 (float *destination,
453 float const *source, vl_size width, vl_size height)
454 {
455 vl_index x, y, ox, oy ;
456 float v00, v10, v01, v11 ;
457
458 assert(destination) ;
459 assert(source) ;
460
461 for(y = 0 ; y < (signed)height ; ++y) {
462 oy = (y < ((signed)height - 1)) * width ;
463 v10 = source[0] ;
464 v11 = source[oy] ;
465 for(x = 0 ; x < (signed)width ; ++x) {
466 ox = x < ((signed)width - 1) ;
467 v00 = v10 ;
468 v01 = v11 ;
469 v10 = source[ox] ;
470 v11 = source[ox + oy] ;
471 destination[0] = v00 ;
472 destination[1] = 0.5f * (v00 + v10) ;
473 destination[2*width] = 0.5f * (v00 + v01) ;
474 destination[2*width+1] = 0.25f * (v00 + v01 + v10 + v11) ;
475 destination += 2 ;
476 source ++;
477 }
478 destination += 2*width ;
479 }
480 }
481
482 /** ------------------------------------------------------------------
483 ** @internal @brief Downsample
484 ** @param destination output imgae buffer.
485 ** @param source input image buffer.
486 ** @param width input image width.
487 ** @param height input image height.
488 ** @param numOctaves octaves (non negative).
489 **
490 ** The function downsamples the image @a d times, reducing it to @c
491 ** 1/2^d of its original size. The parameters @a width and @a height
492 ** are the size of the input image. The destination image @a dst is
493 ** assumed to be <code>floor(width/2^d)</code> pixels wide and
494 ** <code>floor(height/2^d)</code> pixels high.
495 **/
496
497 static void
copy_and_downsample(float * destination,float const * source,vl_size width,vl_size height,vl_size numOctaves)498 copy_and_downsample
499 (float *destination,
500 float const *source,
501 vl_size width, vl_size height, vl_size numOctaves)
502 {
503 vl_index x, y ;
504 vl_size step = 1 << numOctaves ; /* step = 2^numOctaves */
505
506 assert(destination) ;
507 assert(source) ;
508
509 if (numOctaves == 0) {
510 memcpy(destination, source, sizeof(float) * width * height) ;
511 } else {
512 for(y = 0 ; y < (signed)height ; y += step) {
513 float const *p = source + y * width ;
514 for(x = 0 ; x < (signed)width - ((signed)step - 1) ; x += step) {
515 *destination++ = *p ;
516 p += step ;
517 }
518 }
519 }
520 }
521
522 /* ---------------------------------------------------------------- */
523 /** @brief Create a new scale space object
524 ** @param width image width.
525 ** @param height image height.
526 ** @return new scale space object.
527 **
528 ** This function is the same as ::vl_scalespace_new_with_geometry()
529 ** but it uses ::vl_scalespace_get_default_geometry to initialise
530 ** the geometry of the scale space from the image size.
531 **
532 ** @sa ::vl_scalespace_new_with_geometry(), ::vl_scalespace_delete().
533 **/
534
535 VlScaleSpace *
vl_scalespace_new(vl_size width,vl_size height)536 vl_scalespace_new (vl_size width, vl_size height)
537 {
538 VlScaleSpaceGeometry geom ;
539 geom = vl_scalespace_get_default_geometry(width, height) ;
540 return vl_scalespace_new_with_geometry(geom) ;
541 }
542
543 /** ------------------------------------------------------------------
544 ** @brief Create a new scale space with the specified geometry
545 ** @param geom scale space geomerty.
546 ** @return new scale space object.
547 **
548 ** If the geometry is not valid (see ::VlScaleSpaceGeometry), the
549 ** result is unpredictable.
550 **
551 ** The function returns `NULL` if it was not possible to allocate the
552 ** object because of an out-of-memory condition.
553 **
554 ** @sa ::VlScaleSpaceGeometry, ::vl_scalespace_delete().
555 **/
556
557 VlScaleSpace *
vl_scalespace_new_with_geometry(VlScaleSpaceGeometry geom)558 vl_scalespace_new_with_geometry (VlScaleSpaceGeometry geom)
559 {
560
561 vl_index o ;
562 vl_size numSublevels = geom.octaveLastSubdivision - geom.octaveFirstSubdivision + 1 ;
563 vl_size numOctaves = geom.lastOctave - geom.firstOctave + 1 ;
564 VlScaleSpace *self ;
565
566 assert(is_valid_geometry(geom)) ;
567 numOctaves = geom.lastOctave - geom.firstOctave + 1 ;
568 numSublevels = geom.octaveLastSubdivision - geom.octaveFirstSubdivision + 1 ;
569
570 self = vl_calloc(1, sizeof(VlScaleSpace)) ;
571 if (self == NULL) goto err_alloc_self ;
572 self->geom = geom ;
573 self->octaves = vl_calloc(numOctaves, sizeof(float*)) ;
574 if (self->octaves == NULL) goto err_alloc_octave_list ;
575 for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) {
576 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self,o) ;
577 vl_size octaveSize = ogeom.width * ogeom.height * numSublevels ;
578 self->octaves[o - self->geom.firstOctave] = vl_malloc(octaveSize * sizeof(float)) ;
579 if (self->octaves[o - self->geom.firstOctave] == NULL) goto err_alloc_octaves;
580 }
581 return self ;
582
583 err_alloc_octaves:
584 for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) {
585 if (self->octaves[o - self->geom.firstOctave]) {
586 vl_free(self->octaves[o - self->geom.firstOctave]) ;
587 }
588 }
589 err_alloc_octave_list:
590 vl_free(self) ;
591 err_alloc_self:
592 return NULL ;
593 }
594
595 /* ---------------------------------------------------------------- */
596 /** @brief Create a new copy of the object
597 ** @param self object to copy from.
598 **
599 ** The function returns `NULL` if the copy cannot be made due to an
600 ** out-of-memory condition.
601 **/
602
603 VlScaleSpace *
vl_scalespace_new_copy(VlScaleSpace * self)604 vl_scalespace_new_copy (VlScaleSpace* self)
605 {
606 vl_index o ;
607 VlScaleSpace * copy = vl_scalespace_new_shallow_copy(self) ;
608 if (copy == NULL) return NULL ;
609
610 for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) {
611 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self,o) ;
612 vl_size numSubevels = self->geom.octaveLastSubdivision - self->geom.octaveFirstSubdivision + 1;
613 memcpy(copy->octaves[o - self->geom.firstOctave],
614 self->octaves[o - self->geom.firstOctave],
615 ogeom.width * ogeom.height * numSubevels * sizeof(float)) ;
616 }
617 return copy ;
618 }
619
620 /* ---------------------------------------------------------------- */
621 /** @brief Create a new shallow copy of the object
622 ** @param self object to copy from.
623 **
624 ** The function works like ::vl_scalespace_new_copy() but only allocates
625 ** the scale space, without actually copying the data.
626 **/
627
628 VlScaleSpace *
vl_scalespace_new_shallow_copy(VlScaleSpace * self)629 vl_scalespace_new_shallow_copy (VlScaleSpace* self)
630 {
631 return vl_scalespace_new_with_geometry (self->geom) ;
632 }
633
634 /* ---------------------------------------------------------------- */
635 /** @brief Delete object
636 ** @param self object to delete.
637 ** @sa ::vl_scalespace_new()
638 **/
639
640 void
vl_scalespace_delete(VlScaleSpace * self)641 vl_scalespace_delete (VlScaleSpace * self)
642 {
643 if (self) {
644 if (self->octaves) {
645 vl_index o ;
646 for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) {
647 if (self->octaves[o - self->geom.firstOctave]) {
648 vl_free(self->octaves[o - self->geom.firstOctave]) ;
649 }
650 }
651 vl_free(self->octaves) ;
652 }
653 vl_free(self) ;
654 }
655 }
656
657 /* ---------------------------------------------------------------- */
658
659 /** @internal @brief Fill octave starting from the first level
660 ** @param self object instance.
661 ** @param o octave to process.
662 **
663 ** The function takes the first sublevel of octave @a o (the one at
664 ** sublevel `octaveFirstLevel` and iteratively
665 ** smoothes it to obtain the other octave levels.
666 **/
667
668 void
_vl_scalespace_fill_octave(VlScaleSpace * self,vl_index o)669 _vl_scalespace_fill_octave (VlScaleSpace *self, vl_index o)
670 {
671 vl_index s ;
672 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self, o) ;
673
674 for(s = self->geom.octaveFirstSubdivision + 1 ;
675 s <= self->geom.octaveLastSubdivision ; ++s) {
676 double sigma = vl_scalespace_get_level_sigma(self, o, s) ;
677 double previousSigma = vl_scalespace_get_level_sigma(self, o, s - 1) ;
678 double deltaSigma = sqrtf(sigma*sigma - previousSigma*previousSigma) ;
679
680 float* level = vl_scalespace_get_level (self, o, s) ;
681 float* previous = vl_scalespace_get_level (self, o, s-1) ;
682 vl_imsmooth_f (level, ogeom.width,
683 previous, ogeom.width, ogeom.height, ogeom.width,
684 deltaSigma / ogeom.step, deltaSigma / ogeom.step) ;
685 }
686 }
687
688 /** ------------------------------------------------------------------
689 ** @internal @brief Initialize the first level of an octave from an image
690 ** @param self ::VlScaleSpace object instance.
691 ** @param image image data.
692 ** @param o octave to start.
693 **
694 ** The function initializes the first level of octave @a o from
695 ** image @a image. The dimensions of the image are the ones set
696 ** during the creation of the ::VlScaleSpace object instance.
697 **/
698
699 static void
_vl_scalespace_start_octave_from_image(VlScaleSpace * self,float const * image,vl_index o)700 _vl_scalespace_start_octave_from_image (VlScaleSpace *self,
701 float const *image,
702 vl_index o)
703 {
704 float *level ;
705 double sigma, imageSigma ;
706 vl_index op ;
707
708 assert(self) ;
709 assert(image) ;
710 assert(o >= self->geom.firstOctave) ;
711 assert(o <= self->geom.lastOctave) ;
712
713 /*
714 * Copy the image to self->geom.octaveFirstSubdivision of octave o, upscaling or
715 * downscaling as needed.
716 */
717
718 level = vl_scalespace_get_level(self, VL_MAX(0, o), self->geom.octaveFirstSubdivision) ;
719 copy_and_downsample(level, image, self->geom.width, self->geom.height, VL_MAX(0, o)) ;
720
721 for (op = -1 ; op >= o ; --op) {
722 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self, op + 1) ;
723 float *succLevel = vl_scalespace_get_level(self, op + 1, self->geom.octaveFirstSubdivision) ;
724 level = vl_scalespace_get_level(self, op, self->geom.octaveFirstSubdivision) ;
725 copy_and_upsample(level, succLevel, ogeom.width, ogeom.height) ;
726 }
727
728 /*
729 * Adjust the smoothing of the first level just initialised, accounting
730 * for the fact that the input image is assumed to be a nominal scale
731 * level.
732 */
733
734 sigma = vl_scalespace_get_level_sigma(self, o, self->geom.octaveFirstSubdivision) ;
735 imageSigma = self->geom.nominalScale ;
736
737 if (sigma > imageSigma) {
738 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self, o) ;
739 double deltaSigma = sqrt (sigma*sigma - imageSigma*imageSigma) ;
740 level = vl_scalespace_get_level (self, o, self->geom.octaveFirstSubdivision) ;
741 vl_imsmooth_f (level, ogeom.width,
742 level, ogeom.width, ogeom.height, ogeom.width,
743 deltaSigma / ogeom.step, deltaSigma / ogeom.step) ;
744 }
745 }
746
747 /** @internal @brief Initialize the first level of an octave from the previous octave
748 ** @param self object.
749 ** @param o octave to initialize.
750 **
751 ** The function initializes the first level of octave @a o from the
752 ** content of octave <code>o - 1</code>.
753 **/
754
755 static void
_vl_scalespace_start_octave_from_previous_octave(VlScaleSpace * self,vl_index o)756 _vl_scalespace_start_octave_from_previous_octave (VlScaleSpace *self, vl_index o)
757 {
758 double sigma, prevSigma ;
759 float *level, *prevLevel ;
760 vl_index prevLevelIndex ;
761 VlScaleSpaceOctaveGeometry ogeom ;
762
763 assert(self) ;
764 assert(o > self->geom.firstOctave) ; /* must not be the first octave */
765 assert(o <= self->geom.lastOctave) ;
766
767 /*
768 * From the previous octave pick the level which is closer to
769 * self->geom.octaveFirstSubdivision in this octave.
770 * The is self->geom.octaveFirstSubdivision + self->numLevels since there are
771 * self->geom.octaveResolution levels in an octave, provided that
772 * this value does not exceed self->geom.octaveLastSubdivision.
773 */
774
775 prevLevelIndex = VL_MIN(self->geom.octaveFirstSubdivision
776 + (signed)self->geom.octaveResolution,
777 self->geom.octaveLastSubdivision) ;
778 prevLevel = vl_scalespace_get_level (self, o - 1, prevLevelIndex) ;
779 level = vl_scalespace_get_level (self, o, self->geom.octaveFirstSubdivision) ;
780 ogeom = vl_scalespace_get_octave_geometry(self, o - 1) ;
781
782 copy_and_downsample (level, prevLevel, ogeom.width, ogeom.height, 1) ;
783
784 /*
785 * Add remaining smoothing, if any.
786 */
787
788 sigma = vl_scalespace_get_level_sigma(self, o, self->geom.octaveFirstSubdivision) ;
789 prevSigma = vl_scalespace_get_level_sigma(self, o - 1, prevLevelIndex) ;
790
791 if (sigma > prevSigma) {
792 VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self, o) ;
793 double deltaSigma = sqrt (sigma*sigma - prevSigma*prevSigma) ;
794 level = vl_scalespace_get_level (self, o, self->geom.octaveFirstSubdivision) ;
795
796 /* todo: this may fail due to an out-of-memory condition */
797 vl_imsmooth_f (level, ogeom.width,
798 level, ogeom.width, ogeom.height, ogeom.width,
799 deltaSigma / ogeom.step, deltaSigma / ogeom.step) ;
800 }
801 }
802
803 /** @brief Initialise Scale space with new image
804 ** @param self ::VlScaleSpace object instance.
805 ** @param image image to process.
806 **
807 ** Compute the data of all the defined octaves and scales of the scale
808 ** space @a self.
809 **/
810
811 void
vl_scalespace_put_image(VlScaleSpace * self,float const * image)812 vl_scalespace_put_image (VlScaleSpace *self, float const *image)
813 {
814 vl_index o ;
815 _vl_scalespace_start_octave_from_image(self, image, self->geom.firstOctave) ;
816 _vl_scalespace_fill_octave(self, self->geom.firstOctave) ;
817 for (o = self->geom.firstOctave + 1 ; o <= self->geom.lastOctave ; ++o) {
818 _vl_scalespace_start_octave_from_previous_octave(self, o) ;
819 _vl_scalespace_fill_octave(self, o) ;
820 }
821 }
822