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