1 /** @file covdet.c
2  ** @brief Covariant feature detectors - Definition
3  ** @author Karel Lenc
4  ** @author Andrea Vedaldi
5  ** @author Michal Perdoch
6  **/
7 
8 /*
9 Copyright (C) 2013-14 Andrea Vedaldi.
10 Copyright (C) 2012 Karel Lenc, Andrea Vedaldi and Michal Perdoch.
11 All rights reserved.
12 
13 This file is part of the VLFeat library and is made available under
14 the terms of the BSD license (see the COPYING file).
15 */
16 
17 /**
18 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
19 @page covdet Covariant feature detectors
20 @author Karel Lenc
21 @author Andrea Vedaldi
22 @author Michal Perdoch
23 @tableofcontents
24 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
25 
26 @ref covdet.h implements a number of covariant feature detectors, based
27 on three cornerness measures (determinant of the Hessian, trace of the Hessian
28 (aka Difference of Gaussians, and Harris). It supprots affine adaptation,
29 orientation estimation, as well as Laplacian scale detection.
30 
31 - @subpage covdet-fundamentals
32 - @subpage covdet-principles
33 - @subpage covdet-differential
34 - @subpage covdet-corner-types
35 
36 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
37 @section covdet-starting Getting started
38 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
39 
40 The ::VlCovDet object implements a number of covariant feature
41 detectors: Difference of Gaussian, Harris, determinant of Hessian.
42 Variant of the basic detectors support scale selection by maximizing
43 the Laplacian measure as well as affine normalization.
44 
45 @code
46 // create a detector object
47 VlCovDet * covdet = vl_covdet_new(method) ;
48 
49 // set various parameters (optional)
50 vl_covdet_set_first_octave(covdet, -1) ; // start by doubling the image resolution
51 vl_covdet_set_octave_resolution(covdet, octaveResolution) ;
52 vl_covdet_set_peak_threshold(covdet, peakThreshold) ;
53 vl_covdet_set_edge_threshold(covdet, edgeThreshold) ;
54 
55 // process the image and run the detector
56 vl_covdet_put_image(covdet, image, numRows, numCols) ;
57 vl_covdet_detect(covdet) ;
58 
59 // drop features on the margin (optional)
60 vl_covdet_drop_features_outside (covdet, boundaryMargin) ;
61 
62 // compute the affine shape of the features (optional)
63 vl_covdet_extract_affine_shape(covdet) ;
64 
65 // compute the orientation of the features (optional)
66 vl_covdet_extract_orientations(covdet) ;
67 
68 // get feature frames back
69 vl_size numFeatures = vl_covdet_get_num_features(covdet) ;
70 VlCovDetFeature const * feature = vl_covdet_get_features(covdet) ;
71 
72 // get normalized feature appearance patches (optional)
73 vl_size w = 2*patchResolution + 1 ;
74 for (i = 0 ; i < numFeatures ; ++i) {
75   float * patch = malloc(w*w*sizeof(*desc)) ;
76   vl_covdet_extract_patch_for_frame(covdet,
77                                     patch,
78                                     patchResolution,
79                                     patchRelativeExtent,
80                                     patchRelativeSmoothing,
81                                     feature[i].frame) ;
82   // do something with patch
83 }
84 @endcode
85 
86 This example code:
87 
88 - Calls ::vl_covdet_new constructs a new detector object. @ref
89   covdet.h supports a variety of different detectors (see
90   ::VlCovDetMethod).
91 - Optionally calls various functions to set the detector parameters if
92   needed (e.g. ::vl_covdet_set_peak_threshold).
93 - Calls ::vl_covdet_put_image to start processing a new image. It
94   causes the detector to compute the scale space representation of the
95   image, but does not compute the features yet.
96 - Calls ::vl_covdet_detect runs the detector. At this point features are
97   ready to be extracted. However, one or all of the following steps
98   may be executed in order to process the features further.
99 - Optionally calls ::vl_covdet_drop_features_outside to drop features
100   outside the image boundary.
101 - Optionally calls ::vl_covdet_extract_affine_shape to compute the
102   affine shape of features using affine adaptation.
103 - Optionally calls ::vl_covdet_extract_orientations to compute the
104   dominant orientation of features looking for the dominant gradient
105   orientation in patches.
106 - Optionally calls ::vl_covdet_extract_patch_for_frame to extract a
107   normalized feature patch, for example to compute an invariant
108   feature descriptor.
109 
110 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
111 @page covdet-fundamentals Covariant detectors fundamentals
112 @tableofcontents
113 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
114 
115 This page describes the fundamental concepts required to understand a
116 covariant feature detector, the geometry of covariant features, and
117 the process of feature normalization.
118 
119 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
120 @section covdet-covariance Covariant detection
121 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
122 
123 The purpose of a *covariant detector* is to extract from an image a
124 set of local features in a manner which is consistent with spatial
125 transformations of the image itself. For instance, a covariant
126 detector that extracts interest points $\bx_1,\dots,\bx_n$ from image
127 $\ell$ extracts the translated points $\bx_1+T,\dots,\bx_n+T$ from the
128 translated image $\ell'(\bx) = \ell(\bx-T)$.
129 
130 More in general, consider a image $\ell$ and a transformed version
131 $\ell' = \ell \circ w^{-1}$ of it, as in the following figure:
132 
133 @image html covdet.png "Covariant detection of local features."
134 
135 The transformation or <em>warp</em> $w : \real^2 \mapsto \real^2$ is a
136 deformation of the image domain which may capture a change of camera
137 viewpoint or similar imaging factor. Examples of warps typically
138 considered are translations, scaling, rotations, and general affine
139 transformations; however, in $w$ could be another type of continuous
140 and invertible transformation.
141 
142 Given an image $\ell$, a **detector** selects features $R_1,\dots,R_n$
143 (one such features is shown in the example as a green circle). The
144 detector is said to be **covariant** with the warps $w$ if it extracts
145 the transformed features $w[R_1],\dots, w[R_n]$ from the transformed
146 image $w[\ell]$. Intuitively, this means that the &ldquo;same
147 features&rdquo; are extracted in both cases up to the transformation
148 $w$. This property is described more formally in @ref
149 covdet-principles.
150 
151 Covariance is a key property of local feature detectors as it allows
152 extracting corresponding features from two or more images, making it
153 possible to match them in a meaningful way.
154 
155 The @ref covdet.h module in VLFeat implements an array of feature
156 detection algorithm that have are covariant to different classes of
157 transformations.
158 
159 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
160 @section covdet-frame Feature geometry and feature frames
161 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
162 
163 As we have seen, local features are subject to image transformations,
164 and they apply a fundamental role in matching and normalizing
165 images. To operates effectively with local features is therefore
166 necessary to understand their geometry.
167 
168 The geometry of a local feature is captured by a <b>feature frame</b>
169 $R$. In VLFeat, depending on the specific detector, the frame can be
170 either a point, a disc, an ellipse, an oriented disc, or an oriented
171 ellipse.
172 
173 A frame captures both the extent of the local features, useful to know
174 which portions of two images are put in correspondence, as well their
175 shape.  The latter can be used to associate to diagnose the
176 transformation that affects a feature and remove it through the
177 process of **normalization**.
178 
179 More precisely, in covariant detection feature frames are constructed
180 to be compatible with a certain class of transformations. For example,
181 circles are compatible with similarity transformations as they are
182 closed under them. Likewise, ellipses are compatible with affine
183 transformations.
184 
185 Beyond this closure property, the key idea here is that all feature
186 occurrences can be seen as transformed versions of a base or
187 <b>canonical</b> feature. For example, all discs $R$ can be obtained
188 by applying a similarity transformation to the unit disc $\bar R$
189 centered at the origin. $\bar R$ is an example of canonical frame
190 as any other disc can be written as $R = w[\bar R]$ for a suitable
191 similarity $w$.
192 
193 @image html frame-canonical.png "The idea of canonical frame and normalization"
194 
195 The equation $R = w[\bar R_0]$ matching the canonical and detected
196 feature frames establishes a constraint on the warp $w$, very similar
197 to the way two reference frames in geometry establish a transformation
198 between spaces. The transformation $w$ can be thought as a the
199 **pose** of the detected feature, a generalization of its location.
200 
201 In the case of discs and similarity transformations, the equation $R =
202 w[\bar R_0]$ fixes $w$ up to a residual rotation. This can be
203 addressed by considering oriented discs instead. An **oriented disc**
204 is a disc with a radius highlighted to represent the feature
205 orientation.
206 
207 While discs are appropriate for similarity transformations, they are
208 not closed under general affine transformations. In this case, one
209 should consider the more general class of (oriented) ellipses. The
210 following image illustrates the five types of feature frames used in
211 VLFeat:
212 
213 @image html frame-types.png "Types of local feature frames: points, discs, oriented discs, ellipses, oriented ellipses."
214 
215 Note that these frames are described respectively by 2, 3, 4, 5 and 6
216 parameters. The most general type are the oriented ellipses, which can
217 be used to represent all the other frame types as well.
218 
219 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
220 @section covdet-frame-transformation Transforming feature frames
221 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
222 
223 Consider a warp $w$ mapping image $\ell$ into image $\ell'$ as in the
224 figure below. A feature $R$ in the first image co-variantly transform
225 into a feature $R'=w[R]$ in the second image:
226 
227 @image html covdet-normalization.png "Normalization removes the effect of an image deformation."
228 
229 The poses $u,u'$ of $R=u[R_0]$ and $R' = u'[R_0]$ are then related by
230 the simple expression:
231 
232 \[
233   u' = w \circ u.
234 \]
235 
236 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
237 @section covdet-frame-normalization Normalizing feature frames
238 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
239 
240 In the example above, the poses $u$ and $u'$ relate the two
241 occurrences $R$ and $R'$ of the feature to its canonical version
242 $R_0$. If the pose $u$ of the feature in image $\ell$ is known, the
243 canonical feature appearance can be computed by un-warping it:
244 
245 \[
246  \ell_0 = u^{-1}[\ell] = \ell \circ u.
247 \]
248 
249 This process is known as **normalization** and is the key in the
250 computation of invariant feature descriptors as well as in the
251 construction of most co-variant detectors.
252 
253 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
254 @page covdet-principles Principles of covariant detection
255 @tableofcontents
256 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
257 
258 The goals of a co-variant detector were discussed in @ref
259 covdet-fundamentals. This page introduces a few general principles
260 that are at the basis of most covariant detection algorithms. Consider
261 an input image $\ell$ and a two dimensional continuous and invertible
262 warp $w$. The *warped image* $w[\ell]$ is defined to be
263 
264 \[
265  w[\ell] = \ell \circ w^{-1},
266 \]
267 
268 or, equivalently,
269 
270 \[
271  w[\ell](x,y) =  \ell(w^{-1}(x,y)), \qquad \forall (x,y)\in\real^2.
272 \]
273 
274 Note that, while $w$ pushes pixels forward, from the original to the
275 transformed image domain, defining the transformed image $\ell'$
276 requires inverting the warp and composing $\ell$ with $w^{-1}$.
277 
278 The goal a covariant detector is to extract the same local features
279 irregardless of image transformations. The detector is said to be
280 <b>covariant</b> or <b>equivariant</b> with a class of warps
281 $w\in\mathcal{W}$ if, when the feature $R$ is detected in image
282 $\ell$, then the transformed feature $w[R]$ is detected in the
283 transformed image $w[\ell]$.
284 
285 The net effect is that a covariant feature detector appears to
286 &ldquo;track&rdquo; image transformations; however, it is important to
287 note that a detector *is not a tracker* because it processes images
288 individually rather than jointly as part of a sequence.
289 
290 An intuitive way to construct a covariant feature detector is to
291 extract features in correspondence of images structures that are
292 easily identifiable even after a transformation. Example of specific
293 structures include dots, corners, and blobs. These will be generically
294 indicated as **corners** in the followup.
295 
296 A covariant detector faces two challenges. First, corners have, in
297 practice, an infinite variety of individual appearances and the
298 detector must be able to capture them to be of general applicability.
299 Second, the way corners are identified and detected must remain stable
300 under transformations of the image. These two problems are addressed
301 in @ref covdet-cornerness-localmax and @ref
302 covdet-cornerness-normalization respectively.
303 
304 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
305 @section covdet-cornerness Detection using a cornerness measure
306 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
307 
308 One way to decide whether an image region $R$ contains a corner is to
309 compare the local appearance to a model or template of the corner; the
310 result of this comparisons produces a *cornerness score* at that
311 location. This page describe general theoretical properties of the
312 cornerness and the detection process. Concrete examples of cornerness
313 are given in @ref covdet-corner-types.
314 
315 A **cornerness measure** associate a score to all possible feature
316 locations in an image $\ell$. As described in @ref covdet-frame, the
317 location or, more in general, pose $u$ of a feature $R$ is the warp
318 $w$ that maps the canonical feature frame $R_0$ to $R$:
319 
320 \[
321     R = u[R_0].
322 \]
323 
324 The goal of a cornerness measure is to associate a score $F(u;\ell)$
325 to all possible feature poses $u$ and use this score to extract a
326 finite number of co-variant features from any image.
327 
328 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
329 @subsection covdet-cornerness-localmax Local maxima of a cornerness measure
330 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
331 
332 Given the cornerness of each candidate feature, the detector must
333 extract a finite number of them. However, the cornerness of features
334 with nearly identical pose must be similar (otherwise the cornerness
335 measure would be unstable). As such, simply thresholding $F(w;\ell)$
336 would detect an infinite number of nearly identical features rather
337 than a finite number.
338 
339 The solution is to detect features in correspondence of the local
340 maxima of the score measure:
341 
342 \[
343  \{w_1,\dots,w_n\} = \operatorname{localmax}_{w\in\mathcal{W}} F(w;\ell).
344 \]
345 
346 This also means that features are never detected in isolation, but by
347 comparing neighborhoods of them.
348 
349 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
350 @subsection covdet-cornerness-normalization Covariant detection by normalization
351 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
352 
353 The next difficulty is to guarantee that detection is co-variant with
354 image transformations. Hence, if $u$ is the pose of a feature
355 extracted from image $\ell$, then the transformed pose $u' = w[u]$
356 must be detected in the transformed image $\ell' = w[\ell]$.
357 
358 Since features are extracted in correspondence of the local maxima of
359 the cornerness score, a sufficient condition is that corresponding
360 features attain the same score in the two images:
361 
362 \[
363 \forall u\in\mathcal{W}: \quad F(u;\ell) = F(w[u];w[\ell]),
364 \qquad\text{or}\qquad
365 F(u;\ell) = F(w \circ u ;\ell \circ w^{-1}).
366 \]
367 
368 One simple way to satisfy this equation is to compute a cornerness
369 score *after normalizing the image* by the inverse of the candidate
370 feature pose warp $u$, as follows:
371 
372 \[
373   F(u;\ell) = F(1;u^{-1}[\ell]) = F(1;\ell \circ u) = \mathcal{F}(\ell \circ u),
374 \]
375 
376 where $1 = u^{-1} \circ u$ is the identity transformation and
377 $\mathcal{F}$ is an arbitrary functional. Intuitively, co-variant
378 detection is obtained by looking if the appearance of the feature
379 resembles a corner only *after normalization*. Formally:
380 
381 @f{align*}
382 F(w[u];w[\ell])
383 &=
384 F(w \circ u ;\ell \circ w^{-1})
385 \\
386 &=
387 F(1; \ell \circ w^{-1} \circ w \circ u)
388 \\
389 &=
390 \mathcal{F}(\ell\circ u)
391 \\
392 &=
393 F(u;\ell).
394 @f}
395 
396 Concrete examples of the functional $\mathcal{F}$ are given in @ref
397 covdet-corner-types.
398 
399 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
400 @subsection covdet-locality Locality of the detected features
401 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
402 
403 In the definition above, the cornenress functional $\mathcal{F}$ is an
404 arbitrary functional of the entire normalized image $u^{-1}[\ell]$.
405 In practice, one is always interested in detecting **local features**
406 (at the very least because the image extent is finite).
407 
408 This is easily obtained by considering a cornerness $\mathcal{F}$
409 which only looks in a small region of the normalized image, usually
410 corresponding to the extent of the canonical feature $R_0$ (e.g. a
411 unit disc centered at the origin).
412 
413 In this case the extent of the local feature in the original image is
414 simply given by $R = u[R_0]$.
415 
416 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
417 @section covdet-partial Partial and iterated normalization
418 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
419 
420 Practical detectors implement variants of the ideas above. Very often,
421 for instance, detection is an iterative process, in which successive
422 parameters of the pose of a feature are determined. For instance, it
423 is typical to first detect the location and scale of a feature using a
424 rotation-invariant cornerness score $\mathcal{F}$. Once these two
425 parameters are known, the rotation can be determined using a different
426 score, sensitive to the orientation of the local image structures.
427 
428 Certain detectors (such as Harris-Laplace and Hessian-Laplace) use
429 even more sophisticated schemes, in which different scores are used to
430 jointly (rather than in succession) different parameters of the pose
431 of a feature, such as its translation and scale. While a formal
432 treatment of these cases is possible as well, we point to the original
433 papers.
434 
435 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
436 @page covdet-differential Differential and integral image operations
437 @tableofcontents
438 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
439 
440 Dealing with covariant interest point detector requires working a good
441 deal with derivatives, convolutions, and transformations of images.
442 The notation and fundamental properties of interest here are discussed
443 next.
444 
445 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
446 @section covdet-derivatives Derivative operations: gradients
447 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
448 
449 For the derivatives, we borrow the notation of
450 @cite{kinghorn96integrals}. Let $f: \mathbb{R}^m \rightarrow
451 \mathbb{R}^n, \bx \mapsto f(\bx)$ be a vector function. The derivative
452 of the function with respect to $\bx$ is given by its *Jacobian
453 matrix* denoted by the symbol:
454 
455 \[
456 \frac{\partial f}{\partial \bx^\top}
457 =
458 \begin{bmatrix}
459   \frac{\partial f_1}{x_1} & \frac{\partial f_1}{x_2} & \dots \\
460   \frac{\partial f_2}{x_1} & \frac{\partial f_2}{x_2} & \dots \\
461   \vdots & \vdots & \ddots \\
462 \end{bmatrix}.
463 \]
464 
465 When the function $ f $ is scalar ($n=1$), the Jacobian is the same as
466 the gradient of the function (or, in fact, its transpose). More
467 precisely, the <b>gradient</b> $\nabla f $ of $ f $ denotes the column
468 vector of partial derivatives:
469 
470 \[
471 \nabla f
472  = \frac{\partial f}{\partial \bx}
473  =
474  \begin{bmatrix}
475   \frac{\partial f}{\partial x_1} \\
476   \frac{\partial f}{\partial x_2} \\
477   \vdots
478 \end{bmatrix}.
479 \]
480 
481 The second derivative $H_f $ of a scalar function $ f $, or
482 <b>Hessian</b>, is denoted as
483 
484 \[
485 H_f
486 = \frac{\partial f}{\partial \bx \partial \bx^\top}
487 = \frac{\partial \nabla f}{\partial \bx^\top}
488 =
489 \begin{bmatrix}
490   \frac{\partial f}{\partial x_1 \partial x_1} & \frac{\partial f}{\partial x_1 \partial x_2} & \dots \\
491   \frac{\partial f}{\partial x_2 \partial x_1} & \frac{\partial f}{\partial x_2 \partial x_2} & \dots \\
492   \vdots & \vdots & \ddots \\
493 \end{bmatrix}.
494 \]
495 
496 The determinant of the Hessian is also known as <b>Laplacian</b> and denoted as
497 
498 \[
499  \Delta f = \operatorname{det} H_f =
500 \frac{\partial f}{\partial x_1^2} +
501 \frac{\partial f}{\partial x_2^2} +
502 \dots
503 \]
504 
505 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
506 @subsection covdet-derivative-transformations Derivative and image warps
507 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
508 
509 In the following, we will often been interested in domain warpings $u:
510 \mathbb{R}^m \rightarrow \mathbb{R}^n, \bx \mapsto u(\bx)$ of a
511 function $f(\bar\bx) $ and its effect on the derivatives of the
512 function. The key transformation is the chain rule:
513 
514 \[
515 \frac{\partial f \circ u}{\partial \bx^\top}
516 =
517 \left(\frac{\partial f}{\partial \bar\bx^\top} \circ u\right)
518 \frac{\partial u}{\partial \bx^\top}
519 \]
520 
521 In particular, for an affine transformation $u = (A,T) : \bx \mapsto
522 A\bx + T$, one obtains the transformation rules:
523 
524 \[
525 \begin{align*}
526 \frac{\partial f \circ (A,T)}{\partial \bx^\top}
527 &=
528 \left(\frac{\partial f}{\partial \bar\bx^\top} \circ (A,T)\right)A,
529 \\
530 \nabla (f \circ (A,T))
531 &= A^\top (\nabla f) \circ (A,T),
532 \\
533 H_{f \circ(A,T)}
534 &= A^\top (H_f \circ (A,T)) A,
535 \\
536 \Delta (f \circ(A,T))
537 &= \det(A)^2\, (\Delta f) \circ (A,T).
538 \end{align*}
539 \]
540 
541 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
542 @section covdet-smoothing Integral operations: smoothing
543 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
544 
545 In practice, given an image $\ell$ expressed in digital format, good
546 derivative approximations can be computed only if the bandwidth of the
547 image is limited and, in particular, compatible with the sampling
548 density. Since it is unreasonable to expect real images to be
549 band-limited, the bandwidth is artificially constrained by suitably
550 smoothing the image prior to computing its derivatives. This is also
551 interpreted as a form of regularization or as a way of focusing on the
552 image content at a particular scale.
553 
554 Formally, we will focus on Gaussian smoothing kernels. For the 2D case
555 $\bx\in\real^2$, the Gaussian kernel of covariance $\Sigma$ is given
556 by
557 
558 \[
559 g_{\Sigma}(\bx) = \frac{1}{2\pi \sqrt{\det\Sigma}}
560   \exp\left(
561   - \frac{1}{2} \bx^\top \Sigma^{-1} \bx
562   \right).
563 \]
564 
565 The symbol $g_{\sigma^2}$ will be used to denote a Gaussian kernel
566 with isotropic standard deviation $\sigma$, i.e. $\Sigma = \sigma^2
567 I$. Given an image $\ell$, the symbol $\ell_\Sigma$ will be used to
568 denote the image smoothed by the Gaussian kernel of parameter
569 $\Sigma$:
570 
571 \[
572 \ell_\Sigma(\bx) = (g_\Sigma * \ell)(\bx)
573 =
574 \int_{\real^m}
575 g_\Sigma(\bx - \by) \ell(\by)\,d\by.
576 \]
577 
578 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
579 @subsection covdet-smoothing-transformations Smoothing and image warps
580 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
581 
582 One advantage of Gaussian kernels is that they are (up to
583 renormalization) closed under a linear warp:
584 
585 \[
586  |A|\, g_\Sigma \circ A = g_{A^{-1} \Sigma A^{-\top}}
587 \]
588 
589 This also means that smoothing a warped image is the same as warping
590 the result of smoothing the original image by a suitably adjusted
591 Gaussian kernel:
592 
593 \[
594 g_{\Sigma} * (\ell \circ (A,T))
595 =
596 (g_{A\Sigma A^\top} * \ell) \circ (A,T).
597 \]
598 
599 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
600 @page covdet-corner-types Cornerness measures
601 @tableofcontents
602 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
603 
604 The goal of a cornerness measure (@ref covdet-cornerness) is to
605 associate to an image patch a score proportional to how strongly the
606 patch contain a certain strucuture, for example a corner or a
607 blob. This page reviews the most important cornerness measures as
608 implemented in VLFeat:
609 
610 - @ref covdet-harris
611 - @ref covdet-laplacian
612 - @ref covdet-hessian
613 
614 This page makes use of notation introduced in @ref
615 covdet-differential.
616 
617 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
618 @section covdet-harris Harris corners
619 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
620 
621 This section introduces the fist of the cornerness measure
622 $\mathcal{F}[\ell]$. Recall (@ref covdet-cornerness) that the goal of
623 this functional is to respond strongly to images $\ell$ of corner-like
624 structure.
625 
626 Rather than explicitly encoding the appearance of corners, the idea of
627 the Harris measure is to label as corner *any* image patch whose
628 appearance is sufficiently distinctive to allow accurate
629 localization. In particular, consider an image patch $\ell(\bx),
630 \bx\in\Omega$, where $\Omega$ is a smooth circular window of radius
631 approximately $\sigma_i$; at necessary condition for the patch to
632 allow accurate localization is that even a small translation
633 $\ell(\bx+\delta)$ causes the appearance to vary significantly (if not
634 the origin and location $\delta$ would not be distinguishable from the
635 image alone). This variation is measured by the sum of squared
636 differences
637 
638 \[
639 E(\delta) = \int g_{\sigma_i^2}(\bx)
640 (\ell_{\sigma_d^2}(\bx+\delta) -
641  \ell_{\sigma_d^2}(\bx))^2 \,d\bx
642 \]
643 
644 Note that images are compared at scale $\sigma_d$, known as
645  *differentiation scale* for reasons that will be clear in a moment,
646 and that the squared differences are summed over a window softly
647 defined by $\sigma_i$, also known as *integration scale*. This
648 function can be approximated as $E(\delta)\approx \delta^\top
649 M[\ell;\sigma_i^2,\sigma_d^2] \delta$ where
650 
651 \[
652   M[\ell;\sigma_i^2,\sigma_d^2]
653 = \int  g_{\sigma_i^2}(\bx)
654  (\nabla \ell_{\sigma_d^2}(\bx))
655  (\nabla \ell_{\sigma_d^2}(\bx))^\top \, d\bx.
656 \]
657 
658 is the so called **structure tensor**.
659 
660 A corner is identified when the sum of squared differences $E(\delta)$
661 is large for displacements $\delta$ in all directions. This condition
662 is obtained when both the eignenvalues $\lambda_1,\lambda_2$ of the
663 structure tensor $M$ are large. The **Harris cornerness measure**
664 captures this fact:
665 
666 \[
667  \operatorname{Harris}[\ell;\sigma_i^2,\sigma_d^2] =
668  \det M - \kappa \operatorname{trace}^2 M =
669  \lambda_1\lambda_2 - \kappa (\lambda_1+\lambda_2)^2
670 \]
671 
672 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
673 @subsection covdet-harris-warped Harris in the warped domain
674 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
675 
676 The cornerness measure of a feature a location $u$ (recall that
677 locations $u$ are in general defined as image warps) should be
678 computed after normalizing the image (by applying to it the warp
679 $u^{-1}$). This section shows that, for affine warps, the Harris
680 cornerness measure can be computed directly in the Gaussian affine
681 scale space of the image. In particular, for similarities, it can be
682 computed in the standard Gaussian scale space.
683 
684 To this end, let $u=(A,T)$ be an affine warp identifying a feature
685 location in image $\ell(\bx)$. Let $\bar\ell(\bar\bx) =
686 \ell(A\bar\bx+T)$ be the normalized image and rewrite the structure
687 tensor of the normalized image as follows:
688 
689 \[
690  M[\bar\ell; \bar\Sigma_i, \bar\Sigma_d]
691 =
692  M[\bar\ell; \bar\Sigma_i, \bar\Sigma_d](\mathbf{0})
693 =
694 \left[
695 g_{\bar\Sigma_i} *
696 (\nabla\bar\ell_{\bar\Sigma_d})
697 (\nabla\bar\ell_{\bar\Sigma_d})^\top
698 \right](\mathbf{0})
699 \]
700 
701 This notation emphasizes that the structure tensor is obtained by
702 taking derivatives and convolutions of the image. Using the fact that
703 $\nabla g_{\bar\Sigma_d} * \bar\ell = A^\top (\nabla g_{A\bar\Sigma
704 A^\top} * \ell) \circ (A,T)$ and that $g_{\bar\Sigma} * \bar \ell =
705 (g_{A\bar\Sigma A^\top} * \ell) \circ (A,T)$, we get the equivalent
706 expression:
707 
708 \[
709  M[\bar\ell; \bar\Sigma_i, \bar\Sigma_d](\mathbf{0})
710  =
711 A^\top
712 \left[
713 g_{A\bar\Sigma_i A^\top} *
714 (\nabla\ell_{A\bar\Sigma_dA^\top})(\nabla\ell_{A\bar\Sigma_d A^\top})^\top
715 \right](A\mathbf{0}+T)
716 A.
717 \]
718 
719 In other words, the structure tensor of the normalized image can be
720 computed as:
721 
722 \[
723 M[\bar\ell; \bar\Sigma_i, \bar\Sigma_d](\mathbf{0})
724 =
725 A^\top M[\ell; \Sigma_i, \Sigma_d](T) A,
726 \quad
727 \Sigma_{i} = A\bar\Sigma_{i}A^\top,
728 \quad
729 \Sigma_{d} = A\bar\Sigma_{d}A^\top.
730 \]
731 
732 This equation allows to compute the structure tensor for feature at
733 all locations directly in the original image. In particular, features
734 at all translations $T$ can be evaluated efficiently by computing
735 convolutions and derivatives of the image
736 $\ell_{A\bar\Sigma_dA^\top}$.
737 
738 A case of particular instance is when $\bar\Sigma_i= \bar\sigma_i^2 I$
739 and $\bar\Sigma_d = \bar\sigma_d^2$ are both isotropic covariance
740 matrices and the affine transformation is a similarity $A=sR$.  Using
741 the fact that $\det\left( s^2 R^\top M R \right)= s^4 \det M$ and
742 $\operatorname{tr}\left(s^2 R^\top M R\right) = s^2 \operatorname{tr}
743 M$, one obtains the relation
744 
745 \[
746  \operatorname{Harris}[\bar \ell;\bar\sigma_i^2,\bar\sigma_d^2] =
747  s^4 \operatorname{Harris}[\ell;s^2\bar\sigma_i^2,s^2\bar\sigma_d^2](T).
748 \]
749 
750 This equation indicates that, for similarity transformations, not only
751 the structure tensor, but directly the Harris cornerness measure can
752 be computed on the original image and then be transferred back to the
753 normalized domain. Note, however, that this requires rescaling the
754 measure by the factor $s^4$.
755 
756 Another important consequence of this relation is that the Harris
757 measure is invariant to pure image rotations. It cannot, therefore, be
758 used to associate an orientation to the detected features.
759 
760 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
761 @section covdet-hessian Hessian blobs
762 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
763 
764 The *(determinant of the) Hessian* cornerness measure is given
765 determinant of the Hessian of the image:
766 
767 \[
768  \operatorname{DetHess}[\ell;\sigma_d^2]
769  =
770  \det H_{g_{\sigma_d^2} * \ell}(\mathbf{0})
771 \]
772 
773 This number is large and positive if the image is locally curved
774 (peaked), roughly corresponding to blob-like structures in the image.
775 In particular, a large score requires the product of the eigenvalues
776 of the Hessian to be large, which requires both of them to have the
777 same sign and are large in absolute value.
778 
779 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
780 @subsection covdet-hessian-warped Hessian in the warped domain
781 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
782 
783 Similarly to the Harris measure, it is possible to work with the
784 Hessian measure on the original unnormalized image. As before, let
785 $\bar\ell(\bar\bx) = \ell(A\bar\bx+T)$ be the normalized image and
786 rewrite the Hessian of the normalized image as follows:
787 
788 \[
789 H_{g_{\bar\Sigma_d} * \bar\ell}(\mathbf{0}) = A^\top \left(H_{g_{\Sigma_d} * \ell}(T)\right) A.
790 \]
791 
792 Then
793 
794 \[
795  \operatorname{DetHess}[\bar\ell;\bar\Sigma_d]
796  =
797  (\det A)^2 \operatorname{DetHess}[\ell;A\bar\Sigma_d A^\top](T).
798 \]
799 
800 In particular, for isotropic covariance matrices and similarity
801 transformations $A=sR$:
802 
803 \[
804  \operatorname{DetHess}[\bar\ell;\bar\sigma_d^2]
805  =
806  s^4 \operatorname{DetHess}[\ell;s^2\bar\sigma_d^2](T)
807 \]
808 
809 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
810 @section covdet-laplacian Laplacian and Difference of Gaussians blobs
811 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
812 
813 The **Laplacian of Gaussian (LoG)** or **trace of the Hessian**
814 cornerness measure is given by the trace of the Hessian of the image:
815 
816 \[
817  \operatorname{Lap}[\ell;\sigma_d^2]
818  =
819  \operatorname{tr} H_{g_{\sigma_d}^2 * \ell}
820 \]
821 
822 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
823 @subsection covdet-laplacian-warped Laplacian in the warped domain
824 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
825 
826 Similarly to the Hessian measure, the Laplacian cornenress can often
827 be efficiently computed for features at all locations in the original
828 unnormalized image domain. In particular, if the derivative covariance
829 matrix $\Sigma_d$ is isotropic and one considers as warpings
830 similarity transformations $A=sR$, where $R$ is a rotatin and $s$ a
831 rescaling, one has
832 
833 \[
834  \operatorname{Lap}[\bar\ell;\bar\sigma_d^2]
835  =
836  s^2 \operatorname{Lap}[\ell;s^2\bar\sigma_d^2](T)
837 \]
838 
839 Note that, comparing to the Harris and determinant of Hessian
840 measures, the scaling for the Laplacian is $s^2$ rather than $s^4$.
841 
842 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
843 @subsection covdet-laplacian-matched Laplacian as a matched filter
844 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
845 
846 The Laplacian is given by the trace of the Hessian
847 operator. Differently from the determinant of the Hessian, this is a
848 linear operation. This means that computing the Laplacian cornerness
849 measure can be seen as applying a linear filtering operator to the
850 image. This filter can then be interpreted as a *template* of a corner
851 being matched to the image. Hence, the Laplacian cornerness measure
852 can be interpreted as matching this corner template at all possible
853 image locations.
854 
855 To see this formally, compute the Laplacian score in the input image domain:
856 
857 \[
858  \operatorname{Lap}[\bar\ell;\bar\sigma_d^2]
859  =
860  s^2 \operatorname{Lap}[\ell;s^2\bar\sigma_d^2](T)
861  =
862  s^2 (\Delta g_{s^2\bar\sigma_d^2} * \ell)(T)
863 \]
864 
865 The Laplacian fitler is obtained by moving the Laplacian operator from
866 the image to the Gaussian smoothing kernel:
867 
868 \[
869  s^2 (\Delta g_{s^2\bar\sigma_d^2} * \ell)
870 =
871  (s^2 \Delta g_{s^2\bar\sigma_d^2}) * \ell
872 \]
873 
874 Note that the filter is rescaled by the $s^2$; sometimes, this factor
875 is incorporated in the Laplacian operator, yielding the so-called
876 normalized Laplacian.
877 
878 The Laplacian of Gaussian is also called *top-hat function* and has
879 the expression:
880 
881 \[
882 \Delta g_{\sigma^2}(x,y)
883 =
884 \frac{x^2+y^2 - 2 \sigma^2}{\sigma^4} g_{\sigma^2}(x,y).
885 \]
886 
887 This filter, which acts as corner template, resembles a blob (a dark
888 disk surrounded by a bright ring).
889 
890 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
891 @subsection covdet-laplacian-dog Difference of Gaussians
892 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
893 
894 The **Difference of Gaussian** (DoG) cornerness measure can be
895 interpreted as an approximation of the Laplacian that is easy to
896 obtain once a scalespace of the input image has been computed.
897 
898 As noted above, the Laplacian cornerness of the normalized feature can
899 be computed directly from the input image by convolving the image by
900 the normalized Laplacian of Gaussian filter $s^2 \Delta
901 g_{s^2\bar\sigma_d^2}$.
902 
903 Like the other derivative operators, this filter is simpe to
904 discriteize. However, it is often approximated by computing the the
905 *Difference of Gaussians* (DoG) approximation instead. This
906 approximation is obtained from the easily-proved identity:
907 
908 \[
909   \frac{\partial}{\partial \sigma} g_{\sigma^2} =
910   \sigma \Delta g_{\sigma^2}.
911 \]
912 
913 This indicates that computing the normalized Laplacian of a Gaussian
914 filter is, in the limit, the same as taking the difference between
915 Gaussian filters of slightly increasing standard deviation $\sigma$
916 and $\kappa\sigma$, where $\kappa \approx 1$:
917 
918 \[
919 \sigma^2 \Delta g_{\sigma^2}
920 \approx
921 \sigma \frac{g_{(\kappa\sigma)^2} - g_{\sigma^2}}{\kappa\sigma - \sigma}
922 =
923 \frac{1}{\kappa - 1}
924 (g_{(\kappa\sigma)^2} - g_{\sigma^2}).
925 \]
926 
927 One nice propery of this expression is that the factor $\sigma$
928 cancels out in the right-hand side. Usually, scales $\sigma$ and
929 $\kappa\sigma$ are pre-computed in the image scale-space and
930 successive scales are sampled with uniform geometric spacing, meaning
931 that the factor $\kappa$ is the same for all scales. Then, up to a
932 overall scaling factor, the LoG cornerness measure can be obtained by
933 taking the difference of successive scale space images
934 $\ell_{(\kappa\sigma)^2}$ and $\ell_{\sigma^2}$.
935 
936 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
937 @page covdet-affine-adaptation Affine adaptation
938 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
939 
940 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
941 @page covdet-dominant-orientation Dominant orientation
942 <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  -->
943 **/
944 
945 #include "covdet.h"
946 #include <string.h>
947 
948 /** @brief Reallocate buffer
949  ** @param buffer
950  ** @param bufferSize
951  ** @param targetSize
952  ** @return error code
953  **/
954 
955 static int
_vl_resize_buffer(void ** buffer,vl_size * bufferSize,vl_size targetSize)956 _vl_resize_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) {
957   void * newBuffer ;
958   if (*buffer == NULL) {
959     *buffer = vl_malloc(targetSize) ;
960     if (*buffer) {
961       *bufferSize = targetSize ;
962       return VL_ERR_OK ;
963     } else {
964       *bufferSize = 0 ;
965       return VL_ERR_ALLOC ;
966     }
967   }
968   newBuffer = vl_realloc(*buffer, targetSize) ;
969   if (newBuffer) {
970     *buffer = newBuffer ;
971     *bufferSize = targetSize ;
972     return VL_ERR_OK ;
973   } else {
974     return VL_ERR_ALLOC ;
975   }
976 }
977 
978 /** @brief Enlarge buffer
979  ** @param buffer
980  ** @param bufferSize
981  ** @param targetSize
982  ** @return error code
983  **/
984 
985 static int
_vl_enlarge_buffer(void ** buffer,vl_size * bufferSize,vl_size targetSize)986 _vl_enlarge_buffer (void ** buffer, vl_size * bufferSize, vl_size targetSize) {
987   if (*bufferSize >= targetSize) return VL_ERR_OK ;
988   return _vl_resize_buffer(buffer,bufferSize,targetSize) ;
989 }
990 
991 /* ---------------------------------------------------------------- */
992 /*                                            Finding local extrema */
993 /* ---------------------------------------------------------------- */
994 
995 /* Todo: make this generally available in the library */
996 
997 typedef struct _VlCovDetExtremum2
998 {
999   vl_index xi ;
1000   vl_index yi ;
1001   float x ;
1002   float y ;
1003   float peakScore ;
1004   float edgeScore ;
1005 } VlCovDetExtremum2 ;
1006 
1007 typedef struct _VlCovDetExtremum3
1008 {
1009   vl_index xi ;
1010   vl_index yi ;
1011   vl_index zi ;
1012   float x ;
1013   float y ;
1014   float z ;
1015   float peakScore ;
1016   float edgeScore ;
1017 } VlCovDetExtremum3 ;
1018 
1019 VL_EXPORT vl_size
1020 vl_find_local_extrema_3 (vl_index ** extrema, vl_size * bufferSize,
1021                          float const * map,
1022                          vl_size width, vl_size height, vl_size depth,
1023                          double threshold) ;
1024 
1025 VL_EXPORT vl_size
1026 vl_find_local_extrema_2 (vl_index ** extrema, vl_size * bufferSize,
1027                          float const * map,
1028                          vl_size width, vl_size height,
1029                          double threshold) ;
1030 
1031 VL_EXPORT vl_bool
1032 vl_refine_local_extreum_3 (VlCovDetExtremum3 * refined,
1033                            float const * map,
1034                            vl_size width, vl_size height, vl_size depth,
1035                            vl_index x, vl_index y, vl_index z) ;
1036 
1037 VL_EXPORT vl_bool
1038 vl_refine_local_extreum_2 (VlCovDetExtremum2 * refined,
1039                            float const * map,
1040                            vl_size width, vl_size height,
1041                            vl_index x, vl_index y) ;
1042 
1043 /** @internal
1044  ** @brief Find the extrema of a 3D function
1045  ** @param extrema buffer containing the extrema found (in/out).
1046  ** @param bufferSize size of the @a extrema buffer in bytes (in/out).
1047  ** @param map a 3D array representing the map.
1048  ** @param width of the map.
1049  ** @param height of the map.
1050  ** @param depth of the map.
1051  ** @param threshold minumum extremum value.
1052  ** @return number of extrema found.
1053  ** @see @ref ::vl_refine_local_extreum_2.
1054  **/
1055 
1056 vl_size
vl_find_local_extrema_3(vl_index ** extrema,vl_size * bufferSize,float const * map,vl_size width,vl_size height,vl_size depth,double threshold)1057 vl_find_local_extrema_3 (vl_index ** extrema, vl_size * bufferSize,
1058                          float const * map,
1059                          vl_size width, vl_size height, vl_size depth,
1060                          double threshold)
1061 {
1062   vl_index x, y, z ;
1063   vl_size const xo = 1 ;
1064   vl_size const yo = width ;
1065   vl_size const zo = width * height ;
1066   float const *pt = map + xo + yo + zo ;
1067 
1068   vl_size numExtrema = 0 ;
1069   vl_size requiredSize = 0 ;
1070 
1071 #define CHECK_NEIGHBORS_3(v,CMP,SGN)     (\
1072 v CMP ## = SGN threshold &&               \
1073 v CMP *(pt + xo) &&                       \
1074 v CMP *(pt - xo) &&                       \
1075 v CMP *(pt + zo) &&                       \
1076 v CMP *(pt - zo) &&                       \
1077 v CMP *(pt + yo) &&                       \
1078 v CMP *(pt - yo) &&                       \
1079 \
1080 v CMP *(pt + yo + xo) &&                  \
1081 v CMP *(pt + yo - xo) &&                  \
1082 v CMP *(pt - yo + xo) &&                  \
1083 v CMP *(pt - yo - xo) &&                  \
1084 \
1085 v CMP *(pt + xo      + zo) &&             \
1086 v CMP *(pt - xo      + zo) &&             \
1087 v CMP *(pt + yo      + zo) &&             \
1088 v CMP *(pt - yo      + zo) &&             \
1089 v CMP *(pt + yo + xo + zo) &&             \
1090 v CMP *(pt + yo - xo + zo) &&             \
1091 v CMP *(pt - yo + xo + zo) &&             \
1092 v CMP *(pt - yo - xo + zo) &&             \
1093 \
1094 v CMP *(pt + xo      - zo) &&             \
1095 v CMP *(pt - xo      - zo) &&             \
1096 v CMP *(pt + yo      - zo) &&             \
1097 v CMP *(pt - yo      - zo) &&             \
1098 v CMP *(pt + yo + xo - zo) &&             \
1099 v CMP *(pt + yo - xo - zo) &&             \
1100 v CMP *(pt - yo + xo - zo) &&             \
1101 v CMP *(pt - yo - xo - zo) )
1102 
1103   for (z = 1 ; z < (signed)depth - 1 ; ++z) {
1104     for (y = 1 ; y < (signed)height - 1 ; ++y) {
1105       for (x = 1 ; x < (signed)width - 1 ; ++x) {
1106         float value = *pt ;
1107         if (CHECK_NEIGHBORS_3(value,>,+) || CHECK_NEIGHBORS_3(value,<,-)) {
1108           numExtrema ++ ;
1109           requiredSize += sizeof(vl_index) * 3 ;
1110           if (*bufferSize < requiredSize) {
1111             int err = _vl_resize_buffer((void**)extrema, bufferSize,
1112                                         requiredSize + 2000 * 3 * sizeof(vl_index)) ;
1113             if (err != VL_ERR_OK) abort() ;
1114           }
1115           (*extrema) [3 * (numExtrema - 1) + 0] = x ;
1116           (*extrema) [3 * (numExtrema - 1) + 1] = y ;
1117           (*extrema) [3 * (numExtrema - 1) + 2] = z ;
1118         }
1119         pt += xo ;
1120       }
1121       pt += 2*xo ;
1122     }
1123     pt += 2*yo ;
1124   }
1125   return numExtrema ;
1126 }
1127 
1128 /** @internal
1129  ** @brief Find extrema in a 2D function
1130  ** @param extrema buffer containing the found extrema (in/out).
1131  ** @param bufferSize size of the @a extrema buffer in bytes (in/out).
1132  ** @param map a 3D array representing the map.
1133  ** @param width of the map.
1134  ** @param height of the map.
1135  ** @param threshold minumum extremum value.
1136  ** @return number of extrema found.
1137  **
1138  ** An extremum contains 2 ::vl_index values; they are arranged
1139  ** sequentially.
1140  **
1141  ** The function can reuse an already allocated buffer if
1142  ** @a extrema and @a bufferSize are initialized on input.
1143  ** It may have to @a realloc the memory if the buffer is too small.
1144  **/
1145 
1146 vl_size
vl_find_local_extrema_2(vl_index ** extrema,vl_size * bufferSize,float const * map,vl_size width,vl_size height,double threshold)1147 vl_find_local_extrema_2 (vl_index ** extrema, vl_size * bufferSize,
1148                          float const* map,
1149                          vl_size width, vl_size height,
1150                          double threshold)
1151 {
1152   vl_index x, y ;
1153   vl_size const xo = 1 ;
1154   vl_size const yo = width ;
1155   float const *pt = map + xo + yo ;
1156 
1157   vl_size numExtrema = 0 ;
1158   vl_size requiredSize = 0 ;
1159 #define CHECK_NEIGHBORS_2(v,CMP,SGN)     (\
1160 v CMP ## = SGN threshold &&               \
1161 v CMP *(pt + xo) &&                       \
1162 v CMP *(pt - xo) &&                       \
1163 v CMP *(pt + yo) &&                       \
1164 v CMP *(pt - yo) &&                       \
1165 \
1166 v CMP *(pt + yo + xo) &&                  \
1167 v CMP *(pt + yo - xo) &&                  \
1168 v CMP *(pt - yo + xo) &&                  \
1169 v CMP *(pt - yo - xo) )
1170 
1171   for (y = 1 ; y < (signed)height - 1 ; ++y) {
1172     for (x = 1 ; x < (signed)width - 1 ; ++x) {
1173       float value = *pt ;
1174       if (CHECK_NEIGHBORS_2(value,>,+) || CHECK_NEIGHBORS_2(value,<,-)) {
1175         numExtrema ++ ;
1176         requiredSize += sizeof(vl_index) * 2 ;
1177         if (*bufferSize < requiredSize) {
1178           int err = _vl_resize_buffer((void**)extrema, bufferSize,
1179                                       requiredSize + 2000 * 2 * sizeof(vl_index)) ;
1180           if (err != VL_ERR_OK) abort() ;
1181         }
1182         (*extrema) [2 * (numExtrema - 1) + 0] = x ;
1183         (*extrema) [2 * (numExtrema - 1) + 1] = y ;
1184       }
1185       pt += xo ;
1186     }
1187     pt += 2*xo ;
1188   }
1189   return numExtrema ;
1190 }
1191 
1192 /** @internal
1193  ** @brief Refine the location of a local extremum of a 3D map
1194  ** @param refined refined extremum (out).
1195  ** @param map a 3D array representing the map.
1196  ** @param width of the map.
1197  ** @param height of the map.
1198  ** @param depth of the map.
1199  ** @param x initial x position.
1200  ** @param y initial y position.
1201  ** @param z initial z position.
1202  ** @return a flat indicating whether the extrema refinement was stable.
1203  **/
1204 
1205 VL_EXPORT vl_bool
vl_refine_local_extreum_3(VlCovDetExtremum3 * refined,float const * map,vl_size width,vl_size height,vl_size depth,vl_index x,vl_index y,vl_index z)1206 vl_refine_local_extreum_3 (VlCovDetExtremum3 * refined,
1207                            float const * map,
1208                            vl_size width, vl_size height, vl_size depth,
1209                            vl_index x, vl_index y, vl_index z)
1210 {
1211   vl_size const xo = 1 ;
1212   vl_size const yo = width ;
1213   vl_size const zo = width * height ;
1214 
1215   double Dx=0,Dy=0,Dz=0,Dxx=0,Dyy=0,Dzz=0,Dxy=0,Dxz=0,Dyz=0 ;
1216   double A [3*3], b [3] ;
1217 
1218 #define at(dx,dy,dz) (*(pt + (dx)*xo + (dy)*yo + (dz)*zo))
1219 #define Aat(i,j) (A[(i)+(j)*3])
1220 
1221   float const * pt ;
1222   vl_index dx = 0 ;
1223   vl_index dy = 0 ;
1224   /*vl_index dz = 0 ;*/
1225   vl_index iter ;
1226   int err ;
1227 
1228   assert (map) ;
1229   assert (1 <= x && x <= (signed)width - 2) ;
1230   assert (1 <= y && y <= (signed)height - 2) ;
1231   assert (1 <= z && z <= (signed)depth - 2) ;
1232 
1233   for (iter = 0 ; iter < 5 ; ++iter) {
1234     x += dx ;
1235     y += dy ;
1236     pt = map + x*xo + y*yo + z*zo ;
1237 
1238     /* compute the gradient */
1239     Dx = 0.5 * (at(+1,0,0) - at(-1,0,0)) ;
1240     Dy = 0.5 * (at(0,+1,0) - at(0,-1,0));
1241     Dz = 0.5 * (at(0,0,+1) - at(0,0,-1)) ;
1242 
1243     /* compute the Hessian */
1244     Dxx = (at(+1,0,0) + at(-1,0,0) - 2.0 * at(0,0,0)) ;
1245     Dyy = (at(0,+1,0) + at(0,-1,0) - 2.0 * at(0,0,0)) ;
1246     Dzz = (at(0,0,+1) + at(0,0,-1) - 2.0 * at(0,0,0)) ;
1247 
1248     Dxy = 0.25 * (at(+1,+1,0) + at(-1,-1,0) - at(-1,+1,0) - at(+1,-1,0)) ;
1249     Dxz = 0.25 * (at(+1,0,+1) + at(-1,0,-1) - at(-1,0,+1) - at(+1,0,-1)) ;
1250     Dyz = 0.25 * (at(0,+1,+1) + at(0,-1,-1) - at(0,-1,+1) - at(0,+1,-1)) ;
1251 
1252     /* solve linear system */
1253     Aat(0,0) = Dxx ;
1254     Aat(1,1) = Dyy ;
1255     Aat(2,2) = Dzz ;
1256     Aat(0,1) = Aat(1,0) = Dxy ;
1257     Aat(0,2) = Aat(2,0) = Dxz ;
1258     Aat(1,2) = Aat(2,1) = Dyz ;
1259 
1260     b[0] = - Dx ;
1261     b[1] = - Dy ;
1262     b[2] = - Dz ;
1263 
1264     err = vl_solve_linear_system_3(b, A, b) ;
1265 
1266     if (err != VL_ERR_OK) {
1267       b[0] = 0 ;
1268       b[1] = 0 ;
1269       b[2] = 0 ;
1270       break ;
1271     }
1272 
1273     /* Keep going if there is sufficient translation */
1274 
1275     dx = (b[0] > 0.6 && x < (signed)width - 2 ?  1 : 0)
1276     + (b[0] < -0.6 && x > 1 ? -1 : 0) ;
1277 
1278     dy = (b[1] > 0.6 && y < (signed)height - 2 ?  1 : 0)
1279     + (b[1] < -0.6 && y > 1 ? -1 : 0) ;
1280 
1281     if (dx == 0 && dy == 0) break ;
1282   }
1283 
1284   /* check threshold and other conditions */
1285   {
1286     double peakScore = at(0,0,0)
1287     + 0.5 * (Dx * b[0] + Dy * b[1] + Dz * b[2]) ;
1288     double alpha = (Dxx+Dyy)*(Dxx+Dyy) / (Dxx*Dyy - Dxy*Dxy) ;
1289     double edgeScore ;
1290 
1291     if (alpha < 0) {
1292       /* not an extremum */
1293       edgeScore = VL_INFINITY_D ;
1294     } else {
1295       edgeScore = (0.5*alpha - 1) + sqrt(VL_MAX(0.25*alpha - 1,0)*alpha) ;
1296     }
1297 
1298     refined->xi = x ;
1299     refined->yi = y ;
1300     refined->zi = z ;
1301     refined->x = x + b[0] ;
1302     refined->y = y + b[1] ;
1303     refined->z = z + b[2] ;
1304     refined->peakScore = peakScore ;
1305     refined->edgeScore = edgeScore ;
1306 
1307     return
1308     err == VL_ERR_OK &&
1309     vl_abs_d(b[0]) < 1.5 &&
1310     vl_abs_d(b[1]) < 1.5 &&
1311     vl_abs_d(b[2]) < 1.5 &&
1312     0 <= refined->x && refined->x <= (signed)width - 1 &&
1313     0 <= refined->y && refined->y <= (signed)height - 1 &&
1314     0 <= refined->z && refined->z <= (signed)depth - 1 ;
1315   }
1316 #undef Aat
1317 #undef at
1318 }
1319 
1320 /** @internal
1321  ** @brief Refine the location of a local extremum of a 2D map
1322  ** @param refined refined extremum (out).
1323  ** @param map a 2D array representing the map.
1324  ** @param width of the map.
1325  ** @param height of the map.
1326  ** @param x initial x position.
1327  ** @param y initial y position.
1328  ** @return a flat indicating whether the extrema refinement was stable.
1329  **/
1330 
1331 VL_EXPORT vl_bool
vl_refine_local_extreum_2(VlCovDetExtremum2 * refined,float const * map,vl_size width,vl_size height,vl_index x,vl_index y)1332 vl_refine_local_extreum_2 (VlCovDetExtremum2 * refined,
1333                            float const * map,
1334                            vl_size width, vl_size height,
1335                            vl_index x, vl_index y)
1336 {
1337   vl_size const xo = 1 ;
1338   vl_size const yo = width ;
1339 
1340   double Dx=0,Dy=0,Dxx=0,Dyy=0,Dxy=0;
1341   double A [2*2], b [2] ;
1342 
1343 #define at(dx,dy) (*(pt + (dx)*xo + (dy)*yo ))
1344 #define Aat(i,j) (A[(i)+(j)*2])
1345 
1346   float const * pt ;
1347   vl_index dx = 0 ;
1348   vl_index dy = 0 ;
1349   vl_index iter ;
1350   int err ;
1351 
1352   assert (map) ;
1353   assert (1 <= x && x <= (signed)width - 2) ;
1354   assert (1 <= y && y <= (signed)height - 2) ;
1355 
1356   for (iter = 0 ; iter < 5 ; ++iter) {
1357     x += dx ;
1358     y += dy ;
1359     pt = map + x*xo + y*yo  ;
1360 
1361     /* compute the gradient */
1362     Dx = 0.5 * (at(+1,0) - at(-1,0)) ;
1363     Dy = 0.5 * (at(0,+1) - at(0,-1));
1364 
1365     /* compute the Hessian */
1366     Dxx = (at(+1,0) + at(-1,0) - 2.0 * at(0,0)) ;
1367     Dyy = (at(0,+1) + at(0,-1) - 2.0 * at(0,0)) ;
1368     Dxy = 0.25 * (at(+1,+1) + at(-1,-1) - at(-1,+1) - at(+1,-1)) ;
1369 
1370     /* solve linear system */
1371     Aat(0,0) = Dxx ;
1372     Aat(1,1) = Dyy ;
1373     Aat(0,1) = Aat(1,0) = Dxy ;
1374 
1375     b[0] = - Dx ;
1376     b[1] = - Dy ;
1377 
1378     err = vl_solve_linear_system_2(b, A, b) ;
1379 
1380     if (err != VL_ERR_OK) {
1381       b[0] = 0 ;
1382       b[1] = 0 ;
1383       break ;
1384     }
1385 
1386     /* Keep going if there is sufficient translation */
1387 
1388     dx = (b[0] > 0.6 && x < (signed)width - 2 ?  1 : 0)
1389     + (b[0] < -0.6 && x > 1 ? -1 : 0) ;
1390 
1391     dy = (b[1] > 0.6 && y < (signed)height - 2 ?  1 : 0)
1392     + (b[1] < -0.6 && y > 1 ? -1 : 0) ;
1393 
1394     if (dx == 0 && dy == 0) break ;
1395   }
1396 
1397   /* check threshold and other conditions */
1398   {
1399     double peakScore = at(0,0) + 0.5 * (Dx * b[0] + Dy * b[1]) ;
1400     double alpha = (Dxx+Dyy)*(Dxx+Dyy) / (Dxx*Dyy - Dxy*Dxy) ;
1401     double edgeScore ;
1402 
1403     if (alpha < 0) {
1404       /* not an extremum */
1405       edgeScore = VL_INFINITY_D ;
1406     } else {
1407       edgeScore = (0.5*alpha - 1) + sqrt(VL_MAX(0.25*alpha - 1,0)*alpha) ;
1408     }
1409 
1410     refined->xi = x ;
1411     refined->yi = y ;
1412     refined->x = x + b[0] ;
1413     refined->y = y + b[1] ;
1414     refined->peakScore = peakScore ;
1415     refined->edgeScore = edgeScore ;
1416 
1417     return
1418     err == VL_ERR_OK &&
1419     vl_abs_d(b[0]) < 1.5 &&
1420     vl_abs_d(b[1]) < 1.5 &&
1421     0 <= refined->x && refined->x <= (signed)width - 1 &&
1422     0 <= refined->y && refined->y <= (signed)height - 1 ;
1423   }
1424 #undef Aat
1425 #undef at
1426 }
1427 
1428 /* ---------------------------------------------------------------- */
1429 /*                                                Covarant detector */
1430 /* ---------------------------------------------------------------- */
1431 
1432 #define VL_COVDET_MAX_NUM_ORIENTATIONS 4
1433 #define VL_COVDET_MAX_NUM_LAPLACIAN_SCALES 4
1434 #define VL_COVDET_AA_PATCH_RESOLUTION 20
1435 #define VL_COVDET_AA_MAX_NUM_ITERATIONS 15
1436 #define VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS 36
1437 #define VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA 3
1438 #define VL_COVDET_AA_RELATIVE_DERIVATIVE_SIGMA 1
1439 #define VL_COVDET_AA_MAX_ANISOTROPY 5
1440 #define VL_COVDET_AA_CONVERGENCE_THRESHOLD 1.001
1441 #define VL_COVDET_AA_ACCURATE_SMOOTHING VL_FALSE
1442 #define VL_COVDET_AA_PATCH_EXTENT (3*VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA)
1443 #define VL_COVDET_OR_ADDITIONAL_PEAKS_RELATIVE_SIZE 0.8
1444 #define VL_COVDET_LAP_NUM_LEVELS 10
1445 #define VL_COVDET_LAP_PATCH_RESOLUTION 16
1446 #define VL_COVDET_LAP_DEF_PEAK_THRESHOLD 0.01
1447 #define VL_COVDET_DOG_DEF_PEAK_THRESHOLD VL_COVDET_LAP_DEF_PEAK_THRESHOLD
1448 #define VL_COVDET_DOG_DEF_EDGE_THRESHOLD 10.0
1449 #define VL_COVDET_HARRIS_DEF_PEAK_THRESHOLD 0.000002
1450 #define VL_COVDET_HARRIS_DEF_EDGE_THRESHOLD 10.0
1451 #define VL_COVDET_HESSIAN_DEF_PEAK_THRESHOLD 0.003
1452 #define VL_COVDET_HESSIAN_DEF_EDGE_THRESHOLD 10.0
1453 
1454 /** @brief Covariant feature detector */
1455 struct _VlCovDet
1456 {
1457   VlScaleSpace *gss ;        /**< Gaussian scale space. */
1458   VlScaleSpace *css ;        /**< Cornerness scale space. */
1459   VlCovDetMethod method ;    /**< feature extraction method. */
1460   double peakThreshold ;     /**< peak threshold. */
1461   double edgeThreshold ;     /**< edge threshold. */
1462   double lapPeakThreshold;   /**< peak threshold for Laplacian scale selection. */
1463   vl_size octaveResolution ; /**< resolution of each octave. */
1464   vl_index firstOctave ;     /**< index of the first octave. */
1465 
1466   double nonExtremaSuppression ;
1467   vl_size numNonExtremaSuppressed ;
1468 
1469   VlCovDetFeature *features ;
1470   vl_size numFeatures ;
1471   vl_size numFeatureBufferSize ;
1472 
1473   float * patch ;
1474   vl_size patchBufferSize ;
1475 
1476   vl_bool transposed ;
1477   VlCovDetFeatureOrientation orientations [VL_COVDET_MAX_NUM_ORIENTATIONS] ;
1478   VlCovDetFeatureLaplacianScale scales [VL_COVDET_MAX_NUM_LAPLACIAN_SCALES] ;
1479 
1480   vl_bool aaAccurateSmoothing ;
1481   float aaPatch [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
1482   float aaPatchX [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
1483   float aaPatchY [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
1484   float aaMask [(2*VL_COVDET_AA_PATCH_RESOLUTION+1)*(2*VL_COVDET_AA_PATCH_RESOLUTION+1)] ;
1485 
1486   float lapPatch [(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)] ;
1487   float laplacians [(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*(2*VL_COVDET_LAP_PATCH_RESOLUTION+1)*VL_COVDET_LAP_NUM_LEVELS] ;
1488   vl_size numFeaturesWithNumScales [VL_COVDET_MAX_NUM_LAPLACIAN_SCALES + 1] ;
1489 }  ;
1490 
1491 VlEnumerator vlCovdetMethods [VL_COVDET_METHOD_NUM] = {
1492   {"DoG" ,              (vl_index)VL_COVDET_METHOD_DOG               },
1493   {"Hessian",           (vl_index)VL_COVDET_METHOD_HESSIAN           },
1494   {"HessianLaplace",    (vl_index)VL_COVDET_METHOD_HESSIAN_LAPLACE   },
1495   {"HarrisLaplace",     (vl_index)VL_COVDET_METHOD_HARRIS_LAPLACE    },
1496   {"MultiscaleHessian", (vl_index)VL_COVDET_METHOD_MULTISCALE_HESSIAN},
1497   {"MultiscaleHarris",  (vl_index)VL_COVDET_METHOD_MULTISCALE_HARRIS },
1498   {0,                   0                                            }
1499 } ;
1500 
1501 /** @brief Create a new object instance
1502  ** @param method method for covariant feature detection.
1503  ** @return new covariant detector.
1504  **/
1505 
1506 VlCovDet *
vl_covdet_new(VlCovDetMethod method)1507 vl_covdet_new (VlCovDetMethod method)
1508 {
1509   VlCovDet * self = vl_calloc(sizeof(VlCovDet),1) ;
1510   self->method = method ;
1511   self->octaveResolution = 3 ;
1512   self->firstOctave = -1 ;
1513   switch (self->method) {
1514     case VL_COVDET_METHOD_DOG :
1515       self->peakThreshold = VL_COVDET_DOG_DEF_PEAK_THRESHOLD ;
1516       self->edgeThreshold = VL_COVDET_DOG_DEF_EDGE_THRESHOLD ;
1517       self->lapPeakThreshold = 0  ; /* not used */
1518       break ;
1519     case VL_COVDET_METHOD_HARRIS_LAPLACE:
1520     case VL_COVDET_METHOD_MULTISCALE_HARRIS:
1521       self->peakThreshold = VL_COVDET_HARRIS_DEF_PEAK_THRESHOLD ;
1522       self->edgeThreshold = VL_COVDET_HARRIS_DEF_EDGE_THRESHOLD ;
1523       self->lapPeakThreshold = VL_COVDET_LAP_DEF_PEAK_THRESHOLD ;
1524       break ;
1525     case VL_COVDET_METHOD_HESSIAN :
1526     case VL_COVDET_METHOD_HESSIAN_LAPLACE:
1527     case VL_COVDET_METHOD_MULTISCALE_HESSIAN:
1528       self->peakThreshold = VL_COVDET_HESSIAN_DEF_PEAK_THRESHOLD ;
1529       self->edgeThreshold = VL_COVDET_HESSIAN_DEF_EDGE_THRESHOLD ;
1530       self->lapPeakThreshold = VL_COVDET_LAP_DEF_PEAK_THRESHOLD ;
1531       break;
1532     default:
1533       assert(0) ;
1534   }
1535 
1536   self->nonExtremaSuppression = 0.5 ;
1537   self->features = NULL ;
1538   self->numFeatures = 0 ;
1539   self->numFeatureBufferSize = 0 ;
1540   self->patch = NULL ;
1541   self->patchBufferSize = 0 ;
1542   self->transposed = VL_FALSE ;
1543   self->aaAccurateSmoothing = VL_COVDET_AA_ACCURATE_SMOOTHING ;
1544 
1545   {
1546     vl_index const w = VL_COVDET_AA_PATCH_RESOLUTION ;
1547     vl_index i,j ;
1548     double step = (2.0 * VL_COVDET_AA_PATCH_EXTENT) / (2*w+1) ;
1549     double sigma = VL_COVDET_AA_RELATIVE_INTEGRATION_SIGMA ;
1550     for (j = -w ; j <= w ; ++j) {
1551       for (i = -w ; i <= w ; ++i) {
1552         double dx = i*step/sigma ;
1553         double dy = j*step/sigma ;
1554         self->aaMask[(i+w) + (2*w+1)*(j+w)] = exp(-0.5*(dx*dx+dy*dy)) ;
1555       }
1556     }
1557   }
1558 
1559   {
1560     /*
1561      Covers one octave of Laplacian filters, from sigma=1 to sigma=2.
1562      The spatial sampling step is 0.5.
1563      */
1564     vl_index s ;
1565     for (s = 0 ; s < VL_COVDET_LAP_NUM_LEVELS ; ++s) {
1566       double sigmaLap = pow(2.0, -0.5 +
1567                             (double)s / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
1568       double const sigmaImage = 1.0 / sqrt(2.0) ;
1569       double const step = 0.5 * sigmaImage ;
1570       double const sigmaDelta = sqrt(sigmaLap*sigmaLap - sigmaImage*sigmaImage) ;
1571       vl_size const w = VL_COVDET_LAP_PATCH_RESOLUTION ;
1572       vl_size const num = 2 * w + 1  ;
1573       float * pt = self->laplacians + s * (num * num) ;
1574 
1575       memset(pt, 0, num * num * sizeof(float)) ;
1576 
1577 #define at(x,y) pt[(x+w)+(y+w)*(2*w+1)]
1578       at(0,0) = - 4.0 ;
1579       at(-1,0) = 1.0 ;
1580       at(+1,0) = 1.0 ;
1581       at(0,1) = 1.0 ;
1582       at(0,-1) = 1.0 ;
1583 #undef at
1584 
1585       vl_imsmooth_f(pt, num,
1586                     pt, num, num, num,
1587                     sigmaDelta / step, sigmaDelta / step) ;
1588 
1589 #if 0
1590       {
1591         char name [200] ;
1592         snprintf(name, 200, "/tmp/%f-lap.pgm", sigmaDelta) ;
1593         vl_pgm_write_f(name, pt, num, num) ;
1594       }
1595 #endif
1596 
1597     }
1598   }
1599   return self ;
1600 }
1601 
1602 /** @brief Reset object
1603  ** @param self object.
1604  **
1605  ** This function removes any buffered features and frees other
1606  ** internal buffers.
1607  **/
1608 
1609 void
vl_covdet_reset(VlCovDet * self)1610 vl_covdet_reset (VlCovDet * self)
1611 {
1612   if (self->features) {
1613     vl_free(self->features) ;
1614     self->features = NULL ;
1615   }
1616   if (self->css) {
1617     vl_scalespace_delete(self->css) ;
1618     self->css = NULL ;
1619   }
1620   if (self->gss) {
1621     vl_scalespace_delete(self->gss) ;
1622     self->gss = NULL ;
1623   }
1624 }
1625 
1626 /** @brief Delete object instance
1627  ** @param self object.
1628  **/
1629 
1630 void
vl_covdet_delete(VlCovDet * self)1631 vl_covdet_delete (VlCovDet * self)
1632 {
1633   vl_covdet_reset(self) ;
1634   if (self->patch) vl_free (self->patch) ;
1635   vl_free(self) ;
1636 }
1637 
1638 /** @brief Append a feature to the internal buffer.
1639  ** @param self object.
1640  ** @param feature a pointer to the feature to append.
1641  ** @return status.
1642  **
1643  ** The feature is copied. The function may fail with @c status
1644  ** equal to ::VL_ERR_ALLOC if there is insufficient memory.
1645  **/
1646 
1647 int
vl_covdet_append_feature(VlCovDet * self,VlCovDetFeature const * feature)1648 vl_covdet_append_feature (VlCovDet * self, VlCovDetFeature const * feature)
1649 {
1650   vl_size requiredSize ;
1651   assert(self) ;
1652   assert(feature) ;
1653   self->numFeatures ++ ;
1654   requiredSize = self->numFeatures * sizeof(VlCovDetFeature) ;
1655   if (requiredSize > self->numFeatureBufferSize) {
1656     int err = _vl_resize_buffer((void**)&self->features, &self->numFeatureBufferSize,
1657                                 (self->numFeatures + 1000) * sizeof(VlCovDetFeature)) ;
1658     if (err) {
1659       self->numFeatures -- ;
1660       return err ;
1661     }
1662   }
1663   self->features[self->numFeatures - 1] = *feature ;
1664   return VL_ERR_OK ;
1665 }
1666 
1667 /* ---------------------------------------------------------------- */
1668 /*                                              Process a new image */
1669 /* ---------------------------------------------------------------- */
1670 
1671 /** @brief Detect features in an image
1672  ** @param self object.
1673  ** @param image image to process.
1674  ** @param width image width.
1675  ** @param height image height.
1676  ** @return status.
1677  **
1678  ** @a width and @a height must be at least one pixel. The function
1679  ** fails by returing ::VL_ERR_ALLOC if the memory is insufficient.
1680  **/
1681 
1682 int
vl_covdet_put_image(VlCovDet * self,float const * image,vl_size width,vl_size height)1683 vl_covdet_put_image (VlCovDet * self,
1684                      float const * image,
1685                      vl_size width, vl_size height)
1686 {
1687   vl_size const minOctaveSize = 16 ;
1688   vl_index lastOctave ;
1689   vl_index octaveFirstSubdivision ;
1690   vl_index octaveLastSubdivision ;
1691   VlScaleSpaceGeometry geom = vl_scalespace_get_default_geometry(width,height) ;
1692 
1693   assert (self) ;
1694   assert (image) ;
1695   assert (width >= 1) ;
1696   assert (height >= 1) ;
1697 
1698   /* (minOctaveSize - 1) 2^lastOctave <= min(width,height) - 1 */
1699   lastOctave = vl_floor_d(vl_log2_d(VL_MIN((double)width-1,(double)height-1) / (minOctaveSize - 1))) ;
1700 
1701   if (self->method == VL_COVDET_METHOD_DOG) {
1702     octaveFirstSubdivision = -1 ;
1703     octaveLastSubdivision = self->octaveResolution + 1 ;
1704   } else if (self->method == VL_COVDET_METHOD_HESSIAN) {
1705     octaveFirstSubdivision = -1 ;
1706     octaveLastSubdivision = self->octaveResolution ;
1707   } else {
1708     octaveFirstSubdivision = 0 ;
1709     octaveLastSubdivision = self->octaveResolution - 1 ;
1710   }
1711 
1712   geom.width = width ;
1713   geom.height = height ;
1714   geom.firstOctave = self->firstOctave ;
1715   geom.lastOctave = lastOctave ;
1716   geom.octaveResolution = self->octaveResolution ;
1717   geom.octaveFirstSubdivision = octaveFirstSubdivision ;
1718   geom.octaveLastSubdivision = octaveLastSubdivision ;
1719 
1720   if (self->gss == NULL ||
1721       ! vl_scalespacegeometry_is_equal (geom,
1722                                         vl_scalespace_get_geometry(self->gss)))
1723   {
1724     if (self->gss) vl_scalespace_delete(self->gss) ;
1725     self->gss = vl_scalespace_new_with_geometry(geom) ;
1726     if (self->gss == NULL) return VL_ERR_ALLOC ;
1727   }
1728   vl_scalespace_put_image(self->gss, image) ;
1729   return VL_ERR_OK ;
1730 }
1731 
1732 /* ---------------------------------------------------------------- */
1733 /*                                              Cornerness measures */
1734 /* ---------------------------------------------------------------- */
1735 
1736 /** @brief Scaled derminant of the Hessian filter
1737  ** @param hessian output image.
1738  ** @param image input image.
1739  ** @param width image width.
1740  ** @param height image height.
1741  ** @param step image sampling step (pixel size).
1742  ** @param sigma Gaussian smoothing of the input image.
1743  **/
1744 
1745 static void
_vl_det_hessian_response(float * hessian,float const * image,vl_size width,vl_size height,double step,double sigma)1746 _vl_det_hessian_response (float * hessian,
1747                           float const * image,
1748                           vl_size width, vl_size height,
1749                           double step, double sigma)
1750 {
1751   float factor = (float) pow(sigma/step, 4.0) ;
1752   vl_index const xo = 1 ; /* x-stride */
1753   vl_index const yo = width;  /* y-stride */
1754   vl_size r, c;
1755 
1756   float p11, p12, p13, p21, p22, p23, p31, p32, p33;
1757 
1758   /* setup input pointer to be centered at 0,1 */
1759   float const *in = image + yo ;
1760 
1761   /* setup output pointer to be centered at 1,1 */
1762   float *out = hessian + xo + yo;
1763 
1764   /* move 3x3 window and convolve */
1765   for (r = 1; r < height - 1; ++r)
1766   {
1767     /* fill in shift registers at the beginning of the row */
1768     p11 = in[-yo]; p12 = in[xo - yo];
1769     p21 = in[  0]; p22 = in[xo     ];
1770     p31 = in[+yo]; p32 = in[xo + yo];
1771     /* setup input pointer to (2,1) of the 3x3 square */
1772     in += 2;
1773     for (c = 1; c < width - 1; ++c)
1774     {
1775       float Lxx, Lyy, Lxy;
1776       /* fetch remaining values (last column) */
1777       p13 = in[-yo]; p23 = *in; p33 = in[+yo];
1778 
1779       /* Compute 3x3 Hessian values from pixel differences. */
1780       Lxx = (-p21 + 2*p22 - p23);
1781       Lyy = (-p12 + 2*p22 - p32);
1782       Lxy = ((p11 - p31 - p13 + p33)/4.0f);
1783 
1784       /* normalize and write out */
1785       *out = (Lxx * Lyy - Lxy * Lxy) * factor ;
1786 
1787       /* move window */
1788       p11=p12; p12=p13;
1789       p21=p22; p22=p23;
1790       p31=p32; p32=p33;
1791 
1792       /* move input/output pointers */
1793       in++; out++;
1794     }
1795     out += 2;
1796   }
1797 
1798   /* Copy the computed values to borders */
1799   in = hessian + yo + xo ;
1800   out = hessian + xo ;
1801 
1802   /* Top row without corners */
1803   memcpy(out, in, (width - 2)*sizeof(float));
1804   out--;
1805   in -= yo;
1806 
1807   /* Left border columns without last row */
1808   for (r = 0; r < height - 1; r++){
1809     *out = *in;
1810     *(out + yo - 1) = *(in + yo - 3);
1811     in += yo;
1812     out += yo;
1813   }
1814 
1815   /* Bottom corners */
1816   in -= yo;
1817   *out = *in;
1818   *(out + yo - 1) = *(in + yo - 3);
1819 
1820   /* Bottom row without corners */
1821   out++;
1822   memcpy(out, in, (width - 2)*sizeof(float));
1823 }
1824 
1825 /** @brief Scale-normalised Harris response
1826  ** @param harris output image.
1827  ** @param image input image.
1828  ** @param width image width.
1829  ** @param height image height.
1830  ** @param step image sampling step (pixel size).
1831  ** @param sigma Gaussian smoothing of the input image.
1832  ** @param sigmaI integration scale.
1833  ** @param alpha factor in the definition of the Harris score.
1834  **/
1835 
1836 static void
_vl_harris_response(float * harris,float const * image,vl_size width,vl_size height,double step,double sigma,double sigmaI,double alpha)1837 _vl_harris_response (float * harris,
1838                      float const * image,
1839                      vl_size width, vl_size height,
1840                      double step, double sigma,
1841                      double sigmaI, double alpha)
1842 {
1843   float factor = (float) pow(sigma/step, 4.0) ;
1844   vl_index k ;
1845 
1846   float * LxLx ;
1847   float * LyLy ;
1848   float * LxLy ;
1849 
1850   LxLx = vl_malloc(sizeof(float) * width * height) ;
1851   LyLy = vl_malloc(sizeof(float) * width * height) ;
1852   LxLy = vl_malloc(sizeof(float) * width * height) ;
1853 
1854   vl_imgradient_f (LxLx, LyLy, 1, width, image, width, height, width) ;
1855 
1856   for (k = 0 ; k < (signed)(width * height) ; ++k) {
1857     float dx = LxLx[k] ;
1858     float dy = LyLy[k] ;
1859     LxLx[k] = dx*dx ;
1860     LyLy[k] = dy*dy ;
1861     LxLy[k] = dx*dy ;
1862   }
1863 
1864   vl_imsmooth_f(LxLx, width, LxLx, width, height, width,
1865                 sigmaI / step, sigmaI / step) ;
1866 
1867   vl_imsmooth_f(LyLy, width, LyLy, width, height, width,
1868                 sigmaI / step, sigmaI / step) ;
1869 
1870   vl_imsmooth_f(LxLy, width, LxLy, width, height, width,
1871                 sigmaI / step, sigmaI / step) ;
1872 
1873   for (k = 0 ; k < (signed)(width * height) ; ++k) {
1874     float a = LxLx[k] ;
1875     float b = LyLy[k] ;
1876     float c = LxLy[k] ;
1877 
1878     float determinant = a * b - c * c ;
1879     float trace = a + b ;
1880 
1881     harris[k] = factor * (determinant - alpha * (trace * trace)) ;
1882   }
1883 
1884   vl_free(LxLy) ;
1885   vl_free(LyLy) ;
1886   vl_free(LxLx) ;
1887 }
1888 
1889 /** @brief Difference of Gaussian
1890  ** @param dog output image.
1891  ** @param level1 input image at the smaller Gaussian scale.
1892  ** @param level2 input image at the larger Gaussian scale.
1893  ** @param width image width.
1894  ** @param height image height.
1895  **/
1896 
1897 static void
_vl_dog_response(float * dog,float const * level1,float const * level2,vl_size width,vl_size height)1898 _vl_dog_response (float * dog,
1899                   float const * level1,
1900                   float const * level2,
1901                   vl_size width, vl_size height)
1902 {
1903   vl_index k ;
1904   for (k = 0 ; k < (signed)(width*height) ; ++k) {
1905     dog[k] = level2[k] - level1[k] ;
1906   }
1907 }
1908 
1909 /* ---------------------------------------------------------------- */
1910 /*                                                  Detect features */
1911 /* ---------------------------------------------------------------- */
1912 
1913 /** @brief Detect scale-space features
1914  ** @param self object.
1915  **
1916  ** This function runs the configured feature detector on the image
1917  ** that was passed by using ::vl_covdet_put_image.
1918  **/
1919 
1920 void
vl_covdet_detect(VlCovDet * self,vl_size max_num_features)1921 vl_covdet_detect (VlCovDet * self, vl_size max_num_features)
1922 {
1923   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
1924   VlScaleSpaceGeometry cgeom ;
1925   float * levelxx = NULL ;
1926   float * levelyy = NULL ;
1927   float * levelxy = NULL ;
1928   vl_index o, s ;
1929 
1930   assert (self) ;
1931   assert (self->gss) ;
1932 
1933   /* clear previous detections if any */
1934   self->numFeatures = 0 ;
1935 
1936   /* prepare buffers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
1937   cgeom = geom ;
1938   if (self->method == VL_COVDET_METHOD_DOG) {
1939     cgeom.octaveLastSubdivision -= 1 ;
1940   }
1941   if (!self->css ||
1942       !vl_scalespacegeometry_is_equal(cgeom,
1943                                       vl_scalespace_get_geometry(self->css)))
1944   {
1945     if (self->css) vl_scalespace_delete(self->css) ;
1946     self->css = vl_scalespace_new_with_geometry(cgeom) ;
1947   }
1948   if (self->method == VL_COVDET_METHOD_HARRIS_LAPLACE ||
1949       self->method == VL_COVDET_METHOD_MULTISCALE_HARRIS) {
1950     VlScaleSpaceOctaveGeometry oct = vl_scalespace_get_octave_geometry(self->gss, geom.firstOctave) ;
1951     levelxx = vl_malloc(oct.width * oct.height * sizeof(float)) ;
1952     levelyy = vl_malloc(oct.width * oct.height * sizeof(float)) ;
1953     levelxy = vl_malloc(oct.width * oct.height * sizeof(float)) ;
1954   }
1955 
1956   /* compute cornerness ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
1957   for (o = cgeom.firstOctave ; o <= cgeom.lastOctave ; ++o) {
1958     VlScaleSpaceOctaveGeometry oct = vl_scalespace_get_octave_geometry(self->css, o) ;
1959 
1960     for (s = cgeom.octaveFirstSubdivision ; s <= cgeom.octaveLastSubdivision ; ++s) {
1961       float * level = vl_scalespace_get_level(self->gss, o, s) ;
1962       float * clevel = vl_scalespace_get_level(self->css, o, s) ;
1963       double sigma = vl_scalespace_get_level_sigma(self->css, o, s) ;
1964       switch (self->method) {
1965         case VL_COVDET_METHOD_DOG:
1966           _vl_dog_response(clevel,
1967                            vl_scalespace_get_level(self->gss, o, s + 1),
1968                            level,
1969                            oct.width, oct.height) ;
1970           break ;
1971 
1972         case VL_COVDET_METHOD_HARRIS_LAPLACE:
1973         case VL_COVDET_METHOD_MULTISCALE_HARRIS:
1974           _vl_harris_response(clevel,
1975                               level, oct.width, oct.height, oct.step,
1976                               sigma, 1.4 * sigma, 0.05) ;
1977           break ;
1978 
1979         case VL_COVDET_METHOD_HESSIAN:
1980         case VL_COVDET_METHOD_HESSIAN_LAPLACE:
1981         case VL_COVDET_METHOD_MULTISCALE_HESSIAN:
1982           _vl_det_hessian_response(clevel, level, oct.width, oct.height, oct.step, sigma) ;
1983           break ;
1984 
1985         default:
1986           assert(0) ;
1987       }
1988     }
1989   }
1990 
1991   /* find and refine local maxima ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
1992   {
1993     vl_index * extrema = NULL ;
1994     vl_size extremaBufferSize = 0 ;
1995     vl_size numExtrema ;
1996     vl_size index ;
1997     for (o = cgeom.lastOctave; o >= cgeom.firstOctave; --o) {
1998       VlScaleSpaceOctaveGeometry octgeom = vl_scalespace_get_octave_geometry(self->css, o) ;
1999       double step = octgeom.step ;
2000       vl_size width = octgeom.width ;
2001       vl_size height = octgeom.height ;
2002       vl_size depth = cgeom.octaveLastSubdivision - cgeom.octaveFirstSubdivision + 1 ;
2003 
2004       switch (self->method) {
2005         case VL_COVDET_METHOD_DOG:
2006         case VL_COVDET_METHOD_HESSIAN:
2007         {
2008           /* scale-space extrema */
2009           float const * octave =
2010           vl_scalespace_get_level(self->css, o, cgeom.octaveFirstSubdivision) ;
2011           numExtrema = vl_find_local_extrema_3(&extrema, &extremaBufferSize,
2012                                                octave, width, height, depth,
2013                                                0.8 * self->peakThreshold);
2014           for (index = 0 ; index < numExtrema ; ++index) {
2015             VlCovDetExtremum3 refined ;
2016             VlCovDetFeature feature ;
2017             vl_bool ok ;
2018             memset(&feature, 0, sizeof(feature)) ;
2019             ok = vl_refine_local_extreum_3(&refined,
2020                                            octave, width, height, depth,
2021                                            extrema[3*index+0],
2022                                            extrema[3*index+1],
2023                                            extrema[3*index+2]) ;
2024             ok &= fabs(refined.peakScore) > self->peakThreshold ;
2025             ok &= refined.edgeScore < self->edgeThreshold ;
2026             if (ok) {
2027               double sigma = cgeom.baseScale *
2028               pow(2.0, o + (refined.z + cgeom.octaveFirstSubdivision)
2029                   / cgeom.octaveResolution) ;
2030               feature.frame.x = refined.x * step ;
2031               feature.frame.y = refined.y * step ;
2032               feature.frame.a11 = sigma ;
2033               feature.frame.a12 = 0.0 ;
2034               feature.frame.a21 = 0.0 ;
2035               feature.frame.a22 = sigma ;
2036               feature.o = o ;
2037               feature.s = round(refined.z) ;
2038               feature.peakScore = refined.peakScore ;
2039               feature.edgeScore = refined.edgeScore ;
2040               vl_covdet_append_feature(self, &feature) ;
2041             }
2042           }
2043           break ;
2044         }
2045 
2046         default:
2047         {
2048           for (s = cgeom.octaveFirstSubdivision ; s < cgeom.octaveLastSubdivision ; ++s) {
2049             /* space extrema */
2050             float const * level = vl_scalespace_get_level(self->css,o,s) ;
2051             numExtrema = vl_find_local_extrema_2(&extrema, &extremaBufferSize,
2052                                                  level,
2053                                                  width, height,
2054                                                  0.8 * self->peakThreshold);
2055             for (index = 0 ; index < numExtrema ; ++index) {
2056               VlCovDetExtremum2 refined ;
2057               VlCovDetFeature feature ;
2058               vl_bool ok ;
2059               memset(&feature, 0, sizeof(feature)) ;
2060               ok = vl_refine_local_extreum_2(&refined,
2061                                              level, width, height,
2062                                              extrema[2*index+0],
2063                                              extrema[2*index+1]);
2064               ok &= fabs(refined.peakScore) > self->peakThreshold ;
2065               ok &= refined.edgeScore < self->edgeThreshold ;
2066               if (ok) {
2067                 double sigma = cgeom.baseScale *
2068                 pow(2.0, o + (double)s / cgeom.octaveResolution) ;
2069                 feature.frame.x = refined.x * step ;
2070                 feature.frame.y = refined.y * step ;
2071                 feature.frame.a11 = sigma ;
2072                 feature.frame.a12 = 0.0 ;
2073                 feature.frame.a21 = 0.0 ;
2074                 feature.frame.a22 = sigma ;
2075                 feature.o = o ;
2076                 feature.s = s ;
2077                 feature.peakScore = refined.peakScore ;
2078                 feature.edgeScore = refined.edgeScore ;
2079                 vl_covdet_append_feature(self, &feature) ;
2080               }
2081             }
2082           }
2083           break ;
2084         }
2085       }
2086       if (self->numFeatures >= max_num_features) {
2087         break;
2088       }
2089     } /* next octave */
2090 
2091     if (extrema) { vl_free(extrema) ; extrema = 0 ; }
2092   }
2093 
2094   /* Laplacian scale selection for certain methods */
2095   switch (self->method) {
2096     case VL_COVDET_METHOD_HARRIS_LAPLACE :
2097     case VL_COVDET_METHOD_HESSIAN_LAPLACE :
2098       vl_covdet_extract_laplacian_scales (self) ;
2099       break ;
2100     default:
2101       break ;
2102   }
2103 
2104   if (self->nonExtremaSuppression) {
2105     vl_index i, j ;
2106     double tol = self->nonExtremaSuppression ;
2107     self->numNonExtremaSuppressed = 0 ;
2108     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
2109       double x = self->features[i].frame.x ;
2110       double y = self->features[i].frame.y ;
2111       double sigma = self->features[i].frame.a11 ;
2112       double score = self->features[i].peakScore ;
2113       if (score == 0) continue ;
2114 
2115       for (j = 0 ; j < (signed)self->numFeatures ; ++j) {
2116         double dx_ = self->features[j].frame.x - x ;
2117         double dy_ = self->features[j].frame.y - y ;
2118         double sigma_ = self->features[j].frame.a11 ;
2119         double score_ = self->features[j].peakScore ;
2120         if (score_ == 0) continue ;
2121         if (sigma < (1+tol) * sigma_ &&
2122             sigma_ < (1+tol) * sigma &&
2123             vl_abs_d(dx_) < tol * sigma &&
2124             vl_abs_d(dy_) < tol * sigma &&
2125             vl_abs_d(score) > vl_abs_d(score_)) {
2126           self->features[j].peakScore = 0 ;
2127           self->numNonExtremaSuppressed ++ ;
2128         }
2129       }
2130     }
2131     j = 0 ;
2132     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
2133       VlCovDetFeature feature = self->features[i] ;
2134       if (self->features[i].peakScore != 0) {
2135         self->features[j++] = feature ;
2136       }
2137     }
2138     self->numFeatures = j ;
2139   }
2140 
2141   if (levelxx) vl_free(levelxx) ;
2142   if (levelyy) vl_free(levelyy) ;
2143   if (levelxy) vl_free(levelxy) ;
2144 }
2145 
2146 /* ---------------------------------------------------------------- */
2147 /*                                                  Extract patches */
2148 /* ---------------------------------------------------------------- */
2149 
2150 /** @internal
2151  ** @brief Helper for extracting patches
2152  ** @param self object.
2153  ** @param[out] sigma1 actual patch smoothing along the first axis.
2154  ** @param[out] sigma2 actual patch smoothing along the second axis.
2155  ** @param patch buffer.
2156  ** @param resolution patch resolution.
2157  ** @param extent patch extent.
2158  ** @param sigma desired smoothing in the patch frame.
2159  ** @param A_ linear transfomration from patch to image.
2160  ** @param T_ translation from patch to image.
2161  ** @param d1 first singular value @a A.
2162  ** @param d2 second singular value of @a A.
2163  **/
2164 
2165 vl_bool
vl_covdet_extract_patch_helper(VlCovDet * self,double * sigma1,double * sigma2,float * patch,vl_size resolution,double extent,double sigma,double A_[4],double T_[2],double d1,double d2)2166 vl_covdet_extract_patch_helper (VlCovDet * self,
2167                                 double * sigma1,
2168                                 double * sigma2,
2169                                 float * patch,
2170                                 vl_size resolution,
2171                                 double extent,
2172                                 double sigma,
2173                                 double A_ [4],
2174                                 double T_ [2],
2175                                 double d1, double d2)
2176 {
2177   vl_index o, s ;
2178   double factor ;
2179   double sigma_ ;
2180   float const * level ;
2181   vl_size width, height ;
2182   double step ;
2183 
2184   double A [4] = {A_[0], A_[1], A_[2], A_[3]} ;
2185   double T [2] = {T_[0], T_[1]} ;
2186 
2187   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
2188   VlScaleSpaceOctaveGeometry oct ;
2189 
2190   /* Starting from a pre-smoothed image at scale sigma_
2191      because of the mapping A the resulting smoothing in
2192      the warped patch is S, where
2193 
2194         sigma_^2 I = A S A',
2195 
2196         S = sigma_^2 inv(A) inv(A)' = sigma_^2 V D^-2 V',
2197 
2198         A = U D V'.
2199 
2200      Thus we rotate A by V to obtain an axis-aligned smoothing:
2201 
2202         A = U*D,
2203 
2204         S = sigma_^2 D^-2.
2205 
2206      Then we search the scale-space for the best sigma_ such
2207      that the target smoothing is approximated from below:
2208 
2209         max sigma_(o,s) :    simga_(o,s) factor <= sigma,
2210         factor = max{abs(D11), abs(D22)}.
2211    */
2212 
2213 
2214   /*
2215    Determine the best level (o,s) such that sigma_(o,s) factor <= sigma.
2216    This can be obtained by scanning octaves from smallest to largest
2217    and stopping when no level in the octave satisfies the relation.
2218 
2219    Given the range of octave availables, do the best you can.
2220    */
2221 
2222   factor = 1.0 / VL_MIN(d1, d2) ;
2223 
2224   for (o = geom.firstOctave + 1 ; o <= geom.lastOctave ; ++o) {
2225     s = vl_floor_d(vl_log2_d(sigma / (factor * geom.baseScale)) - o) ;
2226     s = VL_MAX(s, geom.octaveFirstSubdivision) ;
2227     s = VL_MIN(s, geom.octaveLastSubdivision) ;
2228     sigma_ = geom.baseScale * pow(2.0, o + (double)s / geom.octaveResolution) ;
2229     /*VL_PRINTF(".. %d D=%g %g; sigma_=%g factor*sigma_=%g\n", o, d1, d2, sigma_, factor* sigma_) ;*/
2230     if (factor * sigma_ > sigma) {
2231       o -- ;
2232       break ;
2233     }
2234   }
2235   o = VL_MIN(o, geom.lastOctave) ;
2236   s = vl_floor_d(vl_log2_d(sigma / (factor * geom.baseScale)) - o) ;
2237   s = VL_MAX(s, geom.octaveFirstSubdivision) ;
2238   s = VL_MIN(s, geom.octaveLastSubdivision) ;
2239   sigma_ = geom.baseScale * pow(2.0, o + (double)s / geom.octaveResolution) ;
2240   if (sigma1) *sigma1 = sigma_ / d1 ;
2241   if (sigma2) *sigma2 = sigma_ / d2 ;
2242 
2243   /*VL_PRINTF("%d %d %g %g %g %g\n", o, s, factor, sigma_, factor * sigma_, sigma) ;*/
2244 
2245   /*
2246    Now the scale space level to be used for this warping has been
2247    determined.
2248 
2249    If the patch is partially or completely out of the image boundary,
2250    create a padded copy of the required region first.
2251    */
2252 
2253   level = vl_scalespace_get_level(self->gss, o, s) ;
2254   oct = vl_scalespace_get_octave_geometry(self->gss, o) ;
2255   width = oct.width ;
2256   height = oct.height ;
2257   step = oct.step ;
2258 
2259   A[0] /= step ;
2260   A[1] /= step ;
2261   A[2] /= step ;
2262   A[3] /= step ;
2263   T[0] /= step ;
2264   T[1] /= step ;
2265 
2266   {
2267     /*
2268      Warp the patch domain [x0hat,y0hat,x1hat,y1hat] to the image domain/
2269      Obtain a box [x0,y0,x1,y1] enclosing that wrapped box, and then
2270      an integer vertexes version [x0i, y0i, x1i, y1i], making room
2271      for one pixel at the boundary to simplify bilinear interpolation
2272      later on.
2273      */
2274     vl_index x0i, y0i, x1i, y1i ;
2275     double x0 = +VL_INFINITY_D ;
2276     double x1 = -VL_INFINITY_D ;
2277     double y0 = +VL_INFINITY_D ;
2278     double y1 = -VL_INFINITY_D ;
2279     double boxx [4] = {extent, extent, -extent, -extent} ;
2280     double boxy [4] = {-extent, extent, extent, -extent} ;
2281     int i ;
2282     for (i = 0 ; i < 4 ; ++i) {
2283       double x = A[0] * boxx[i] + A[2] * boxy[i] + T[0] ;
2284       double y = A[1] * boxx[i] + A[3] * boxy[i] + T[1] ;
2285       x0 = VL_MIN(x0, x) ;
2286       x1 = VL_MAX(x1, x) ;
2287       y0 = VL_MIN(y0, y) ;
2288       y1 = VL_MAX(y1, y) ;
2289     }
2290 
2291     /* Leave one pixel border for bilinear interpolation. */
2292     x0i = floor(x0) - 1 ;
2293     y0i = floor(y0) - 1 ;
2294     x1i = ceil(x1) + 1 ;
2295     y1i = ceil(y1) + 1 ;
2296 
2297     /*
2298      If the box [x0i,y0i,x1i,y1i] is not fully contained in the
2299      image domain, then create a copy of this region by padding
2300      the image. The image is extended by continuity.
2301      */
2302 
2303     if (x0i < 0 || x1i > (signed)width-1 ||
2304         y0i < 0 || y1i > (signed)height-1) {
2305       vl_index xi, yi ;
2306 
2307       /* compute the amount of l,r,t,b padding needed to complete the patch */
2308       vl_index padx0 = VL_MAX(0, - x0i) ;
2309       vl_index pady0 = VL_MAX(0, - y0i) ;
2310       vl_index padx1 = VL_MAX(0, x1i - ((signed)width - 1)) ;
2311       vl_index pady1 = VL_MAX(0, y1i - ((signed)height - 1)) ;
2312 
2313       /* make enough room for the patch */
2314       vl_index patchWidth = x1i - x0i + 1 ;
2315       vl_index patchHeight = y1i - y0i + 1 ;
2316       vl_size patchBufferSize = patchWidth * patchHeight * sizeof(float) ;
2317       if (patchBufferSize > self->patchBufferSize) {
2318         int err = _vl_resize_buffer((void**)&self->patch, &self->patchBufferSize, patchBufferSize) ;
2319         if (err) return vl_set_last_error(VL_ERR_ALLOC, NULL) ;
2320       }
2321 
2322       if (pady0 < patchHeight - pady1) {
2323         /* start by filling the central horizontal band */
2324         for (yi = y0i + pady0 ; yi < y0i + patchHeight - pady1 ; ++ yi) {
2325           float *dst = self->patch + (yi - y0i) * patchWidth ;
2326           float const *src = level + yi * width + VL_MIN(VL_MAX(0, x0i),(signed)width-1) ;
2327           for (xi = x0i ; xi < x0i + padx0 ; ++xi) *dst++ = *src ;
2328           for ( ; xi < x0i + patchWidth - padx1 - 2 ; ++xi) *dst++ = *src++ ;
2329           for ( ; xi < x0i + patchWidth ; ++xi) *dst++ = *src ;
2330         }
2331         /* now extend the central band up and down */
2332         for (yi = 0 ; yi < pady0 ; ++yi) {
2333           memcpy(self->patch + yi * patchWidth,
2334                  self->patch + pady0 * patchWidth,
2335                  patchWidth * sizeof(float)) ;
2336         }
2337         for (yi = patchHeight - pady1 ; yi < patchHeight ; ++yi) {
2338           memcpy(self->patch + yi * patchWidth,
2339                  self->patch + (patchHeight - pady1 - 1) * patchWidth,
2340                  patchWidth * sizeof(float)) ;
2341         }
2342       } else {
2343         /* should be handled better! */
2344         memset(self->patch, 0, self->patchBufferSize) ;
2345       }
2346 #if 0
2347       {
2348         char name [200] ;
2349         snprintf(name, 200, "/tmp/%20.0f-ext.pgm", 1e10*vl_get_cpu_time()) ;
2350         vl_pgm_write_f(name, patch, patchWidth, patchWidth) ;
2351       }
2352 #endif
2353 
2354       level = self->patch ;
2355       width = patchWidth ;
2356       height = patchHeight ;
2357       T[0] -= x0i ;
2358       T[1] -= y0i ;
2359     }
2360   }
2361 
2362   /*
2363    Resample by using bilinear interpolation.
2364    */
2365   {
2366     float * pt = patch ;
2367     double yhat = -extent ;
2368     vl_index xxi ;
2369     vl_index yyi ;
2370     double stephat = extent / resolution ;
2371 
2372     for (yyi = 0 ; yyi < 2 * (signed)resolution + 1 ; ++yyi) {
2373       double xhat = -extent ;
2374       double rx = A[2] * yhat + T[0] ;
2375       double ry = A[3] * yhat + T[1] ;
2376       for (xxi = 0 ; xxi < 2 * (signed)resolution + 1 ; ++xxi) {
2377         double x = A[0] * xhat + rx ;
2378         double y = A[1] * xhat + ry ;
2379         vl_index xi = vl_floor_d(x) ;
2380         vl_index yi = vl_floor_d(y) ;
2381         double i00 = level[yi * width + xi] ;
2382         double i10 = level[yi * width + xi + 1] ;
2383         double i01 = level[(yi + 1) * width + xi] ;
2384         double i11 = level[(yi + 1) * width + xi + 1] ;
2385         double wx = x - xi ;
2386         double wy = y - yi ;
2387 
2388         assert(xi >= 0 && xi <= (signed)width - 1) ;
2389         assert(yi >= 0 && yi <= (signed)height - 1) ;
2390 
2391         *pt++ =
2392         (1.0 - wy) * ((1.0 - wx) * i00 + wx * i10) +
2393         wy * ((1.0 - wx) * i01 + wx * i11) ;
2394 
2395         xhat += stephat ;
2396       }
2397       yhat += stephat ;
2398     }
2399   }
2400 #if 0
2401     {
2402       char name [200] ;
2403       snprintf(name, 200, "/tmp/%20.0f.pgm", 1e10*vl_get_cpu_time()) ;
2404       vl_pgm_write_f(name, patch, 2*resolution+1, 2*resolution+1) ;
2405     }
2406 #endif
2407   return VL_ERR_OK ;
2408 }
2409 
2410 /** @brief Helper for extracting patches
2411  ** @param self object.
2412  ** @param patch buffer.
2413  ** @param resolution patch resolution.
2414  ** @param extent patch extent.
2415  ** @param sigma desired smoothing in the patch frame.
2416  ** @param frame feature frame.
2417  **
2418  ** The function considers a patch of extent <code>[-extent,extent]</code>
2419  ** on each side, with a side counting <code>2*resolution+1</code> pixels.
2420  ** In attempts to extract from the scale space a patch
2421  ** based on the affine warping specified by @a frame in such a way
2422  ** that the resulting smoothing of the image is @a sigma (in the
2423  ** patch frame).
2424  **
2425  ** The transformation is specified by the matrices @c A and @c T
2426  ** embedded in the feature @a frame. Note that this transformation maps
2427  ** pixels from the patch frame to the image frame.
2428  **/
2429 
2430 vl_bool
vl_covdet_extract_patch_for_frame(VlCovDet * self,float * patch,vl_size resolution,double extent,double sigma,VlFrameOrientedEllipse frame)2431 vl_covdet_extract_patch_for_frame (VlCovDet * self,
2432                                    float * patch,
2433                                    vl_size resolution,
2434                                    double extent,
2435                                    double sigma,
2436                                    VlFrameOrientedEllipse frame)
2437 {
2438   double A[2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
2439   double T[2] = {frame.x, frame.y} ;
2440   double D[4], U[4], V[4] ;
2441 
2442   vl_svd2(D, U, V, A) ;
2443 
2444   return vl_covdet_extract_patch_helper
2445   (self, NULL, NULL, patch, resolution, extent, sigma, A, T, D[0], D[3]) ;
2446 }
2447 
2448 /* ---------------------------------------------------------------- */
2449 /*                                                     Affine shape */
2450 /* ---------------------------------------------------------------- */
2451 
2452 /** @brief Extract the affine shape for a feature frame
2453  ** @param self object.
2454  ** @param adapted the shape-adapted frame.
2455  ** @param frame the input frame.
2456  ** @return ::VL_ERR_OK if affine adaptation is successful.
2457  **
2458  ** This function may fail if adaptation is unsuccessful or if
2459  ** memory is insufficient.
2460  **/
2461 
2462 int
vl_covdet_extract_affine_shape_for_frame(VlCovDet * self,VlFrameOrientedEllipse * adapted,VlFrameOrientedEllipse frame)2463 vl_covdet_extract_affine_shape_for_frame (VlCovDet * self,
2464                                           VlFrameOrientedEllipse * adapted,
2465                                           VlFrameOrientedEllipse frame)
2466 {
2467   vl_index iter = 0 ;
2468 
2469   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
2470   double T [2] = {frame.x, frame.y} ;
2471   double U [2*2] ;
2472   double V [2*2] ;
2473   double D [2*2] ;
2474   double M [2*2] ;
2475   double P [2*2] ;
2476   double P_ [2*2] ;
2477   double Q [2*2] ;
2478   double sigma1, sigma2 ;
2479   double sigmaD = VL_COVDET_AA_RELATIVE_DERIVATIVE_SIGMA ;
2480   double factor ;
2481   double anisotropy ;
2482   double referenceScale ;
2483   vl_size const resolution = VL_COVDET_AA_PATCH_RESOLUTION ;
2484   vl_size const side = 2*VL_COVDET_AA_PATCH_RESOLUTION + 1 ;
2485   double const extent = VL_COVDET_AA_PATCH_EXTENT ;
2486 
2487 
2488   *adapted = frame ;
2489 
2490   while (1) {
2491     double lxx = 0, lxy = 0, lyy = 0 ;
2492     vl_index k ;
2493     int err ;
2494 
2495     /* A = U D V' */
2496     vl_svd2(D, U, V, A) ;
2497     anisotropy = VL_MAX(D[0]/D[3], D[3]/D[0]) ;
2498 
2499     /* VL_PRINTF("anisot: %g\n", anisotropy); */
2500 
2501     if (anisotropy > VL_COVDET_AA_MAX_ANISOTROPY) {
2502       /* diverged, give up with current solution */
2503       break ;
2504     }
2505 
2506     /* make sure that the smallest singluar value stays fixed
2507        after the first iteration */
2508     if (iter == 0) {
2509       referenceScale = VL_MIN(D[0], D[3]) ;
2510       factor = 1.0 ;
2511     } else {
2512       factor = referenceScale / VL_MIN(D[0],D[3]) ;
2513     }
2514 
2515     D[0] *= factor ;
2516     D[3] *= factor ;
2517 
2518     A[0] = U[0] * D[0] ;
2519     A[1] = U[1] * D[0] ;
2520     A[2] = U[2] * D[3] ;
2521     A[3] = U[3] * D[3] ;
2522 
2523     adapted->a11 = A[0] ;
2524     adapted->a21 = A[1] ;
2525     adapted->a12 = A[2] ;
2526     adapted->a22 = A[3] ;
2527 
2528     if (++iter >= VL_COVDET_AA_MAX_NUM_ITERATIONS) break ;
2529 
2530     err = vl_covdet_extract_patch_helper(self,
2531                                          &sigma1, &sigma2,
2532                                          self->aaPatch,
2533                                          resolution,
2534                                          extent,
2535                                          sigmaD,
2536                                          A, T, D[0], D[3]) ;
2537     if (err) return err ;
2538 
2539     if (self->aaAccurateSmoothing ) {
2540       double deltaSigma1 = sqrt(VL_MAX(sigmaD*sigmaD - sigma1*sigma1,0)) ;
2541       double deltaSigma2 = sqrt(VL_MAX(sigmaD*sigmaD - sigma2*sigma2,0)) ;
2542       double stephat = extent / resolution ;
2543       vl_imsmooth_f(self->aaPatch, side,
2544                     self->aaPatch, side, side, side,
2545                     deltaSigma1 / stephat, deltaSigma2 / stephat) ;
2546     }
2547 
2548     /* compute second moment matrix */
2549     vl_imgradient_f (self->aaPatchX, self->aaPatchY, 1, side,
2550                      self->aaPatch, side, side, side) ;
2551 
2552     for (k = 0 ; k < (signed)(side*side) ; ++k) {
2553       double lx = self->aaPatchX[k] ;
2554       double ly = self->aaPatchY[k] ;
2555       lxx += lx * lx * self->aaMask[k] ;
2556       lyy += ly * ly * self->aaMask[k] ;
2557       lxy += lx * ly * self->aaMask[k] ;
2558     }
2559     M[0] = lxx ;
2560     M[1] = lxy ;
2561     M[2] = lxy ;
2562     M[3] = lyy ;
2563 
2564     if (lxx == 0 || lyy == 0) {
2565       *adapted = frame ;
2566       break ;
2567     }
2568 
2569     /* decompose M = P * Q * P' */
2570     vl_svd2 (Q, P, P_, M) ;
2571 
2572     /*
2573      Setting A <- A * dA results in M to change approximatively as
2574 
2575      M --> dA'  M dA = dA' P Q P dA
2576 
2577      To make this proportional to the identity, we set
2578 
2579      dA ~= P Q^1/2
2580 
2581      we also make it so the smallest singular value of A is unchanged.
2582      */
2583 
2584     if (Q[3]/Q[0] < VL_COVDET_AA_CONVERGENCE_THRESHOLD &&
2585         Q[0]/Q[3] < VL_COVDET_AA_CONVERGENCE_THRESHOLD) {
2586       break ;
2587     }
2588 
2589     {
2590       double Ap [4] ;
2591       double q0 = sqrt(Q[0]) ;
2592       double q1 = sqrt(Q[3]) ;
2593       Ap[0] = (A[0] * P[0] + A[2] * P[1]) / q0 ;
2594       Ap[1] = (A[1] * P[0] + A[3] * P[1]) / q0 ;
2595       Ap[2] = (A[0] * P[2] + A[2] * P[3]) / q1 ;
2596       Ap[3] = (A[1] * P[2] + A[3] * P[3]) / q1 ;
2597       memcpy(A,Ap,4*sizeof(double)) ;
2598     }
2599 
2600   } /* next iteration */
2601 
2602   /*
2603    Make upright.
2604 
2605    Shape adaptation does not estimate rotation. This is fixed by default
2606    so that a selected axis is not rotated at all (usually this is the
2607    vertical axis for upright features). To do so, the frame is rotated
2608    as follows.
2609    */
2610   {
2611     double A [2*2] = {adapted->a11, adapted->a21, adapted->a12, adapted->a22} ;
2612     double ref [2] ;
2613     double ref_ [2] ;
2614     double angle ;
2615     double angle_ ;
2616     double dangle ;
2617     double r1, r2 ;
2618 
2619     if (self->transposed) {
2620       /* up is the x axis */
2621       ref[0] = 1 ;
2622       ref[1] = 0 ;
2623     } else {
2624       /* up is the y axis */
2625       ref[0] = 0 ;
2626       ref[1] = 1 ;
2627     }
2628 
2629     vl_solve_linear_system_2 (ref_, A, ref) ;
2630     angle = atan2(ref[1], ref[0]) ;
2631     angle_ = atan2(ref_[1], ref_[0]) ;
2632     dangle = angle_ - angle ;
2633     r1 = cos(dangle) ;
2634     r2 = sin(dangle) ;
2635     adapted->a11 = + A[0] * r1 + A[2] * r2 ;
2636     adapted->a21 = + A[1] * r1 + A[3] * r2 ;
2637     adapted->a12 = - A[0] * r2 + A[2] * r1 ;
2638     adapted->a22 = - A[1] * r2 + A[3] * r1 ;
2639   }
2640 
2641   return VL_ERR_OK ;
2642 }
2643 
2644 /** @brief Extract the affine shape for the stored features
2645  ** @param self object.
2646  **
2647  ** This function may discard features for which no affine
2648  ** shape can reliably be detected.
2649  **/
2650 
2651 void
vl_covdet_extract_affine_shape(VlCovDet * self)2652 vl_covdet_extract_affine_shape (VlCovDet * self)
2653 {
2654   vl_index i, j = 0 ;
2655   vl_size numFeatures = vl_covdet_get_num_features(self) ;
2656   VlCovDetFeature * feature = vl_covdet_get_features(self);
2657   for (i = 0 ; i < (signed)numFeatures ; ++i) {
2658     int status ;
2659     VlFrameOrientedEllipse adapted ;
2660     status = vl_covdet_extract_affine_shape_for_frame(self, &adapted, feature[i].frame) ;
2661     if (status == VL_ERR_OK) {
2662       feature[j] = feature[i] ;
2663       feature[j].frame = adapted ;
2664       ++ j ;
2665     }
2666   }
2667   self->numFeatures = j ;
2668 }
2669 
2670 /* ---------------------------------------------------------------- */
2671 /*                                                      Orientation */
2672 /* ---------------------------------------------------------------- */
2673 
2674 static int
_vl_covdet_compare_orientations_descending(void const * a_,void const * b_)2675 _vl_covdet_compare_orientations_descending (void const * a_,
2676                                             void const * b_)
2677 {
2678   VlCovDetFeatureOrientation const * a = a_ ;
2679   VlCovDetFeatureOrientation const * b = b_ ;
2680   if (a->score > b->score) return -1 ;
2681   if (a->score < b->score) return +1 ;
2682   return 0 ;
2683 }
2684 
2685 /** @brief Extract the orientation(s) for a feature
2686  ** @param self object.
2687  ** @param numOrientations the number of detected orientations.
2688  ** @param frame pose of the feature.
2689  ** @return an array of detected orientations with their scores.
2690  **
2691  ** The returned array is a matrix of size @f$ 2 \times n @f$
2692  ** where <em>n</em> is the number of detected orientations.
2693  **
2694  ** The function returns @c NULL if memory is insufficient.
2695  **/
2696 
2697 VlCovDetFeatureOrientation *
vl_covdet_extract_orientations_for_frame(VlCovDet * self,vl_size * numOrientations,VlFrameOrientedEllipse frame)2698 vl_covdet_extract_orientations_for_frame (VlCovDet * self,
2699                                           vl_size * numOrientations,
2700                                           VlFrameOrientedEllipse frame)
2701 {
2702   int err ;
2703   vl_index k, i ;
2704   vl_index iter ;
2705 
2706   double extent = VL_COVDET_AA_PATCH_EXTENT ;
2707   vl_size resolution = VL_COVDET_AA_PATCH_RESOLUTION ;
2708   vl_size side = 2 * resolution + 1  ;
2709 
2710   vl_size const numBins = VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS ;
2711   double hist [VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS] ;
2712   double const binExtent = 2 * VL_PI / VL_COVDET_OR_NUM_ORIENTATION_HISTOGAM_BINS ;
2713   double const peakRelativeSize = VL_COVDET_OR_ADDITIONAL_PEAKS_RELATIVE_SIZE ;
2714   double maxPeakValue ;
2715 
2716   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
2717   double T [2] = {frame.x, frame.y} ;
2718   double U [2*2] ;
2719   double V [2*2] ;
2720   double D [2*2] ;
2721   double sigma1, sigma2 ;
2722   double sigmaD = 1.0 ;
2723   double theta0 ;
2724 
2725   assert(self);
2726   assert(numOrientations) ;
2727 
2728   /*
2729    The goal is to estimate a rotation R(theta) such that the patch given
2730    by the transformation A R(theta) has the strongest average
2731    gradient pointing right (or down for transposed conventions).
2732 
2733    To compensate for tha anisotropic smoothing due to warping,
2734    A is decomposed as A = U D V' and the patch is warped by
2735    U D only, meaning that the matrix R_(theta) will be estimated instead,
2736    where:
2737 
2738       A R(theta) = U D V' R(theta) = U D R_(theta)
2739 
2740    such that R(theta) = V R(theta). That is an extra rotation of
2741    theta0 = atan2(V(2,1),V(1,1)).
2742    */
2743 
2744   /* axis aligned anisotropic smoothing for easier compensation */
2745   vl_svd2(D, U, V, A) ;
2746 
2747   A[0] = U[0] * D[0] ;
2748   A[1] = U[1] * D[0] ;
2749   A[2] = U[2] * D[3] ;
2750   A[3] = U[3] * D[3] ;
2751 
2752   theta0 = atan2(V[1],V[0]) ;
2753 
2754   err = vl_covdet_extract_patch_helper(self,
2755                                        &sigma1, &sigma2,
2756                                        self->aaPatch,
2757                                        resolution,
2758                                        extent,
2759                                        sigmaD,
2760                                        A, T, D[0], D[3]) ;
2761 
2762   if (err) {
2763     *numOrientations = 0 ;
2764     return NULL ;
2765   }
2766 
2767   if (1) {
2768     double deltaSigma1 = sqrt(VL_MAX(sigmaD*sigmaD - sigma1*sigma1,0)) ;
2769     double deltaSigma2 = sqrt(VL_MAX(sigmaD*sigmaD - sigma2*sigma2,0)) ;
2770     double stephat = extent / resolution ;
2771     vl_imsmooth_f(self->aaPatch, side,
2772                   self->aaPatch, side, side, side,
2773                   deltaSigma1 / stephat, deltaSigma2 / stephat) ;
2774   }
2775 
2776   /* histogram of oriented gradients */
2777   vl_imgradient_polar_f (self->aaPatchX, self->aaPatchY, 1, side,
2778                          self->aaPatch, side, side, side) ;
2779 
2780   memset (hist, 0, sizeof(double) * numBins) ;
2781 
2782   for (k = 0 ; k < (signed)(side*side) ; ++k) {
2783     double modulus = self->aaPatchX[k] ;
2784     double angle = self->aaPatchY[k] ;
2785     double weight = self->aaMask[k] ;
2786 
2787     double x = angle / binExtent ;
2788     vl_index bin = vl_floor_d(x) ;
2789     double w2 = x - bin ;
2790     double w1 = 1.0 - w2 ;
2791 
2792     hist[(bin + numBins) % numBins] += w1 * (modulus * weight) ;
2793     hist[(bin + numBins + 1) % numBins] += w2 * (modulus * weight) ;
2794   }
2795 
2796   /* smooth histogram */
2797   for (iter = 0; iter < 6; iter ++) {
2798     double prev = hist [numBins - 1] ;
2799     double first = hist [0] ;
2800     vl_index i ;
2801     for (i = 0; i < (signed)numBins - 1; ++i) {
2802       double curr = (prev + hist[i] + hist[(i + 1) % numBins]) / 3.0 ;
2803       prev = hist[i] ;
2804       hist[i] = curr ;
2805     }
2806     hist[i] = (prev + hist[i] + first) / 3.0 ;
2807   }
2808 
2809   /* find the histogram maximum */
2810   maxPeakValue = 0 ;
2811   for (i = 0 ; i < (signed)numBins ; ++i) {
2812     maxPeakValue = VL_MAX (maxPeakValue, hist[i]) ;
2813   }
2814 
2815   /* find peaks within 80% from max */
2816   *numOrientations = 0 ;
2817   for(i = 0 ; i < (signed)numBins ; ++i) {
2818     double h0 = hist [i] ;
2819     double hm = hist [(i - 1 + numBins) % numBins] ;
2820     double hp = hist [(i + 1 + numBins) % numBins] ;
2821 
2822     /* is this a peak? */
2823     if (h0 > peakRelativeSize * maxPeakValue && h0 > hm && h0 > hp) {
2824       /* quadratic interpolation */
2825       double di = - 0.5 * (hp - hm) / (hp + hm - 2 * h0) ;
2826       double th = binExtent * (i + di) + theta0 ;
2827       if (self->transposed) {
2828         /* the axis to the right is y, measure orientations from this */
2829         th = th - VL_PI/2 ;
2830       }
2831       self->orientations[*numOrientations].angle = th ;
2832       self->orientations[*numOrientations].score = h0 ;
2833       *numOrientations += 1 ;
2834       //VL_PRINTF("%d %g\n", *numOrientations, th) ;
2835 
2836       if (*numOrientations >= VL_COVDET_MAX_NUM_ORIENTATIONS) break ;
2837     }
2838   }
2839 
2840   /* sort the orientations by decreasing scores */
2841   qsort(self->orientations,
2842         *numOrientations,
2843         sizeof(VlCovDetFeatureOrientation),
2844         _vl_covdet_compare_orientations_descending) ;
2845 
2846   return self->orientations ;
2847 }
2848 
2849 /** @brief Extract the orientation(s) for the stored features.
2850  ** @param self object.
2851  **
2852  ** Note that, since more than one orientation can be detected
2853  ** for each feature, this function may create copies of them,
2854  ** one for each orientation.
2855  **/
2856 
2857 void
vl_covdet_extract_orientations(VlCovDet * self)2858 vl_covdet_extract_orientations (VlCovDet * self)
2859 {
2860   vl_index i, j  ;
2861   vl_size numFeatures = vl_covdet_get_num_features(self) ;
2862   for (i = 0 ; i < (signed)numFeatures ; ++i) {
2863     vl_size numOrientations ;
2864     VlCovDetFeature feature = self->features[i] ;
2865     VlCovDetFeatureOrientation* orientations =
2866     vl_covdet_extract_orientations_for_frame(self, &numOrientations, feature.frame) ;
2867 
2868     for (j = 0 ; j < (signed)numOrientations ; ++j) {
2869       double A [2*2] = {
2870         feature.frame.a11,
2871         feature.frame.a21,
2872         feature.frame.a12,
2873         feature.frame.a22} ;
2874       double r1 = cos(orientations[j].angle) ;
2875       double r2 = sin(orientations[j].angle) ;
2876       VlCovDetFeature * oriented ;
2877 
2878       if (j == 0) {
2879         oriented = & self->features[i] ;
2880       } else {
2881         vl_covdet_append_feature(self, &feature) ;
2882         oriented = & self->features[self->numFeatures -1] ;
2883       }
2884 
2885       oriented->orientationScore = orientations[j].score ;
2886       oriented->frame.a11 = + A[0] * r1 + A[2] * r2 ;
2887       oriented->frame.a21 = + A[1] * r1 + A[3] * r2 ;
2888       oriented->frame.a12 = - A[0] * r2 + A[2] * r1 ;
2889       oriented->frame.a22 = - A[1] * r2 + A[3] * r1 ;
2890     }
2891   }
2892 }
2893 
2894 /* ---------------------------------------------------------------- */
2895 /*                                                 Laplacian scales */
2896 /* ---------------------------------------------------------------- */
2897 
2898 /** @brief Extract the Laplacian scale(s) for a feature frame.
2899  ** @param self object.
2900  ** @param numScales the number of detected scales.
2901  ** @param frame pose of the feature.
2902  ** @return an array of detected scales.
2903  **
2904  ** The function returns @c NULL if memory is insufficient.
2905  **/
2906 
2907 VlCovDetFeatureLaplacianScale *
vl_covdet_extract_laplacian_scales_for_frame(VlCovDet * self,vl_size * numScales,VlFrameOrientedEllipse frame)2908 vl_covdet_extract_laplacian_scales_for_frame (VlCovDet * self,
2909                                               vl_size * numScales,
2910                                               VlFrameOrientedEllipse frame)
2911 {
2912   /*
2913    We try to explore one octave, with the nominal detection scale 1.0
2914    (in the patch reference frame) in the middle. Thus the goal is to sample
2915    the response of the tr-Laplacian operator at logarithmically
2916    spaced scales in 1/sqrt(2), sqrt(2).
2917 
2918    To this end, the patch is warped with a smoothing of at most
2919    sigmaImage = 1 / sqrt(2) (beginning of the scale), sampled at
2920    roughly twice the Nyquist frequency (so step = 1 / (2*sqrt(2))).
2921    This maes it possible to approximate the Laplacian operator at
2922    that scale by simple finite differences.
2923 
2924    */
2925   int err ;
2926   double const sigmaImage = 1.0 / sqrt(2.0) ;
2927   double const step = 0.5 * sigmaImage ;
2928   double actualSigmaImage ;
2929   vl_size const resolution = VL_COVDET_LAP_PATCH_RESOLUTION ;
2930   vl_size const num = 2 * resolution + 1 ;
2931   double extent = step * resolution ;
2932   double scores [VL_COVDET_LAP_NUM_LEVELS] ;
2933   double factor = 1.0 ;
2934   float const * pt ;
2935   vl_index k ;
2936 
2937   double A[2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
2938   double T[2] = {frame.x, frame.y} ;
2939   double D[4], U[4], V[4] ;
2940   double sigma1, sigma2 ;
2941 
2942   assert(self) ;
2943   assert(numScales) ;
2944 
2945   *numScales = 0 ;
2946 
2947   vl_svd2(D, U, V, A) ;
2948 
2949   err = vl_covdet_extract_patch_helper
2950   (self, &sigma1, &sigma2, self->lapPatch, resolution, extent, sigmaImage, A, T, D[0], D[3]) ;
2951   if (err) return NULL ;
2952 
2953   /* the actual smoothing after warping is never the target one */
2954   if (sigma1 == sigma2) {
2955     actualSigmaImage = sigma1 ;
2956   } else {
2957     /* here we could compensate */
2958     actualSigmaImage = sqrt(sigma1*sigma2) ;
2959   }
2960 
2961   /* now multiply by the bank of Laplacians */
2962   pt = self->laplacians ;
2963   for (k = 0 ; k < VL_COVDET_LAP_NUM_LEVELS ; ++k) {
2964     vl_index q ;
2965     double score = 0 ;
2966     double sigmaLap = pow(2.0, -0.5 + (double)k / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
2967     /* note that the sqrt argument cannot be negative since by construction
2968      sigmaLap >= sigmaImage */
2969     sigmaLap = sqrt(sigmaLap*sigmaLap
2970                     - sigmaImage*sigmaImage
2971                     + actualSigmaImage*actualSigmaImage) ;
2972 
2973     for (q = 0 ; q < (signed)(num * num) ; ++q) {
2974       score += (*pt++) * self->lapPatch[q] ;
2975     }
2976     scores[k] = score * sigmaLap * sigmaLap ;
2977   }
2978 
2979   /* find and interpolate maxima */
2980   for (k = 1 ; k < VL_COVDET_LAP_NUM_LEVELS - 1 ; ++k) {
2981     double a = scores[k-1] ;
2982     double b = scores[k] ;
2983     double c = scores[k+1] ;
2984     double t = self->lapPeakThreshold ;
2985 
2986     if ((((b > a) && (b > c)) || ((b < a) && (b < c))) && (vl_abs_d(b) >= t)) {
2987       double dk = - 0.5 * (c - a) / (c + a - 2 * b) ;
2988       double s = k + dk ;
2989       double sigmaLap = pow(2.0, -0.5 + s / (VL_COVDET_LAP_NUM_LEVELS - 1)) ;
2990       double scale ;
2991       sigmaLap = sqrt(sigmaLap*sigmaLap
2992                       - sigmaImage*sigmaImage
2993                       + actualSigmaImage*actualSigmaImage) ;
2994       scale = sigmaLap / 1.0 ;
2995       /*
2996        VL_PRINTF("** k:%d, s:%f, sigmaLapFilter:%f, sigmaLap%f, scale:%f (%f %f %f)\n",
2997        k,s,sigmaLapFilter,sigmaLap,scale,a,b,c) ;
2998        */
2999       if (*numScales < VL_COVDET_MAX_NUM_LAPLACIAN_SCALES) {
3000         self->scales[*numScales].scale = scale * factor ;
3001         self->scales[*numScales].score = b + 0.5 * (c - a) * dk ;
3002         *numScales += 1 ;
3003       }
3004     }
3005   }
3006   return self->scales ;
3007 }
3008 
3009 /** @brief Extract the Laplacian scales for the stored features
3010  ** @param self object.
3011  **
3012  ** Note that, since more than one orientation can be detected
3013  ** for each feature, this function may create copies of them,
3014  ** one for each orientation.
3015  **/
3016 void
vl_covdet_extract_laplacian_scales(VlCovDet * self)3017 vl_covdet_extract_laplacian_scales (VlCovDet * self)
3018 {
3019   vl_index i, j  ;
3020   vl_bool dropFeaturesWithoutScale = VL_TRUE ;
3021   vl_size numFeatures = vl_covdet_get_num_features(self) ;
3022   memset(self->numFeaturesWithNumScales, 0,
3023          sizeof(self->numFeaturesWithNumScales)) ;
3024 
3025   for (i = 0 ; i < (signed)numFeatures ; ++i) {
3026     vl_size numScales ;
3027     VlCovDetFeature feature = self->features[i] ;
3028     VlCovDetFeatureLaplacianScale const * scales =
3029     vl_covdet_extract_laplacian_scales_for_frame(self, &numScales, feature.frame) ;
3030 
3031     self->numFeaturesWithNumScales[numScales] ++ ;
3032 
3033     if (numScales == 0 && dropFeaturesWithoutScale) {
3034       self->features[i].peakScore = 0 ;
3035     }
3036 
3037     for (j = 0 ; j < (signed)numScales ; ++j) {
3038       VlCovDetFeature * scaled ;
3039 
3040       if (j == 0) {
3041         scaled = & self->features[i] ;
3042       } else {
3043         vl_covdet_append_feature(self, &feature) ;
3044         scaled = & self->features[self->numFeatures -1] ;
3045       }
3046 
3047       scaled->laplacianScaleScore = scales[j].score ;
3048       scaled->frame.a11 *= scales[j].scale ;
3049       scaled->frame.a21 *= scales[j].scale ;
3050       scaled->frame.a12 *= scales[j].scale ;
3051       scaled->frame.a22 *= scales[j].scale ;
3052     }
3053   }
3054   if (dropFeaturesWithoutScale) {
3055     j = 0 ;
3056     for (i = 0 ; i < (signed)self->numFeatures ; ++i) {
3057       VlCovDetFeature feature = self->features[i] ;
3058       if (feature.peakScore) {
3059         self->features[j++] = feature ;
3060       }
3061     }
3062     self->numFeatures = j ;
3063   }
3064 
3065 }
3066 
3067 /* ---------------------------------------------------------------- */
3068 /*                       Checking that features are inside an image */
3069 /* ---------------------------------------------------------------- */
3070 
3071 vl_bool
_vl_covdet_check_frame_inside(VlCovDet * self,VlFrameOrientedEllipse frame,double margin)3072 _vl_covdet_check_frame_inside (VlCovDet * self, VlFrameOrientedEllipse frame, double margin)
3073 {
3074   double extent = margin ;
3075   double A [2*2] = {frame.a11, frame.a21, frame.a12, frame.a22} ;
3076   double T[2] = {frame.x, frame.y} ;
3077   double x0 = +VL_INFINITY_D ;
3078   double x1 = -VL_INFINITY_D ;
3079   double y0 = +VL_INFINITY_D ;
3080   double y1 = -VL_INFINITY_D ;
3081   double boxx [4] = {extent, extent, -extent, -extent} ;
3082   double boxy [4] = {-extent, extent, extent, -extent} ;
3083   VlScaleSpaceGeometry geom = vl_scalespace_get_geometry(self->gss) ;
3084   int i ;
3085   for (i = 0 ; i < 4 ; ++i) {
3086     double x = A[0] * boxx[i] + A[2] * boxy[i] + T[0] ;
3087     double y = A[1] * boxx[i] + A[3] * boxy[i] + T[1] ;
3088     x0 = VL_MIN(x0, x) ;
3089     x1 = VL_MAX(x1, x) ;
3090     y0 = VL_MIN(y0, y) ;
3091     y1 = VL_MAX(y1, y) ;
3092   }
3093 
3094   return
3095   0 <= x0 && x1 <= geom.width-1 &&
3096   0 <= y0 && y1 <= geom.height-1 ;
3097 }
3098 
3099 /** @brief Drop features (partially) outside the image
3100  ** @param self object.
3101  ** @param margin geometric marging.
3102  **
3103  ** The feature extent is defined by @c maring. A bounding box
3104  ** in the normalised feature frame containin a circle of radius
3105  ** @a maring is created and mapped to the image by
3106  ** the feature frame transformation. Then the feature
3107  ** is dropped if the bounding box is not contained in the image.
3108  **
3109  ** For example, setting @c margin to zero drops a feature only
3110  ** if its center is not contained.
3111  **
3112  ** Typically a valua of @c margin equal to 1 or 2 is used.
3113  **/
3114 
3115 void
vl_covdet_drop_features_outside(VlCovDet * self,double margin)3116 vl_covdet_drop_features_outside (VlCovDet * self, double margin)
3117 {
3118   vl_index i, j = 0 ;
3119   vl_size numFeatures = vl_covdet_get_num_features(self) ;
3120   for (i = 0 ; i < (signed)numFeatures ; ++i) {
3121     vl_bool inside =
3122     _vl_covdet_check_frame_inside (self, self->features[i].frame, margin) ;
3123     if (inside) {
3124       self->features[j] = self->features[i] ;
3125       ++j ;
3126     }
3127   }
3128   self->numFeatures = j ;
3129 }
3130 
3131 /* ---------------------------------------------------------------- */
3132 /*                                              Setters and getters */
3133 /* ---------------------------------------------------------------- */
3134 
3135 /* ---------------------------------------------------------------- */
3136 /** @brief Get wether images are passed in transposed
3137  ** @param self object.
3138  ** @return whether images are transposed.
3139  **/
3140 vl_bool
vl_covdet_get_transposed(VlCovDet const * self)3141 vl_covdet_get_transposed (VlCovDet const  * self)
3142 {
3143   return self->transposed ;
3144 }
3145 
3146 /** @brief Set the index of the first octave
3147  ** @param self object.
3148  ** @param t whether images are transposed.
3149  **/
3150 void
vl_covdet_set_transposed(VlCovDet * self,vl_bool t)3151 vl_covdet_set_transposed (VlCovDet * self, vl_bool t)
3152 {
3153   self->transposed = t ;
3154 }
3155 
3156 /* ---------------------------------------------------------------- */
3157 /** @brief Get the edge threshold
3158  ** @param self object.
3159  ** @return the edge threshold.
3160  **/
3161 double
vl_covdet_get_edge_threshold(VlCovDet const * self)3162 vl_covdet_get_edge_threshold (VlCovDet const * self)
3163 {
3164   return self->edgeThreshold ;
3165 }
3166 
3167 /** @brief Set the edge threshold
3168  ** @param self object.
3169  ** @param edgeThreshold the edge threshold.
3170  **
3171  ** The edge threshold must be non-negative.
3172  **/
3173 void
vl_covdet_set_edge_threshold(VlCovDet * self,double edgeThreshold)3174 vl_covdet_set_edge_threshold (VlCovDet * self, double edgeThreshold)
3175 {
3176   assert(edgeThreshold >= 0) ;
3177   self->edgeThreshold = edgeThreshold ;
3178 }
3179 
3180 /* ---------------------------------------------------------------- */
3181 /** @brief Get the peak threshold
3182  ** @param self object.
3183  ** @return the peak threshold.
3184  **/
3185 double
vl_covdet_get_peak_threshold(VlCovDet const * self)3186 vl_covdet_get_peak_threshold (VlCovDet const * self)
3187 {
3188   return self->peakThreshold ;
3189 }
3190 
3191 /** @brief Set the peak threshold
3192  ** @param self object.
3193  ** @param peakThreshold the peak threshold.
3194  **
3195  ** The peak threshold must be non-negative.
3196  **/
3197 void
vl_covdet_set_peak_threshold(VlCovDet * self,double peakThreshold)3198 vl_covdet_set_peak_threshold (VlCovDet * self, double peakThreshold)
3199 {
3200   assert(peakThreshold >= 0) ;
3201   self->peakThreshold = peakThreshold ;
3202 }
3203 
3204 /* ---------------------------------------------------------------- */
3205 /** @brief Get the Laplacian peak threshold
3206  ** @param self object.
3207  ** @return the Laplacian peak threshold.
3208  **
3209  ** This parameter affects only the detecors using the Laplacian
3210  ** scale selectino method such as Harris-Laplace.
3211  **/
3212 double
vl_covdet_get_laplacian_peak_threshold(VlCovDet const * self)3213 vl_covdet_get_laplacian_peak_threshold (VlCovDet const * self)
3214 {
3215   return self->lapPeakThreshold ;
3216 }
3217 
3218 /** @brief Set the Laplacian peak threshold
3219  ** @param self object.
3220  ** @param peakThreshold the Laplacian peak threshold.
3221  **
3222  ** The peak threshold must be non-negative.
3223  **/
3224 void
vl_covdet_set_laplacian_peak_threshold(VlCovDet * self,double peakThreshold)3225 vl_covdet_set_laplacian_peak_threshold (VlCovDet * self, double peakThreshold)
3226 {
3227   assert(peakThreshold >= 0) ;
3228   self->lapPeakThreshold = peakThreshold ;
3229 }
3230 
3231 /* ---------------------------------------------------------------- */
3232 /** @brief Get the index of the first octave
3233  ** @param self object.
3234  ** @return index of the first octave.
3235  **/
3236 vl_index
vl_covdet_get_first_octave(VlCovDet const * self)3237 vl_covdet_get_first_octave (VlCovDet const * self)
3238 {
3239   return self->firstOctave ;
3240 }
3241 
3242 /** @brief Set the index of the first octave
3243  ** @param self object.
3244  ** @param o index of the first octave.
3245  **
3246  ** Calling this function resets the detector.
3247  **/
3248 void
vl_covdet_set_first_octave(VlCovDet * self,vl_index o)3249 vl_covdet_set_first_octave (VlCovDet * self, vl_index o)
3250 {
3251   self->firstOctave = o ;
3252   vl_covdet_reset(self) ;
3253 }
3254 
3255 /* ---------------------------------------------------------------- */
3256 /** @brief Get the octave resolution.
3257  ** @param self object.
3258  ** @return octave resolution.
3259  **/
3260 
3261 vl_size
vl_covdet_get_octave_resolution(VlCovDet const * self)3262 vl_covdet_get_octave_resolution (VlCovDet const * self)
3263 {
3264   return self->octaveResolution ;
3265 }
3266 
3267 /** @brief Set the octave resolutuon.
3268  ** @param self object.
3269  ** @param r octave resoltuion.
3270  **
3271  ** Calling this function resets the detector.
3272  **/
3273 
3274 void
vl_covdet_set_octave_resolution(VlCovDet * self,vl_size r)3275 vl_covdet_set_octave_resolution (VlCovDet * self, vl_size r)
3276 {
3277   self->octaveResolution = r ;
3278   vl_covdet_reset(self) ;
3279 }
3280 
3281 /* ---------------------------------------------------------------- */
3282 /** @brief Get whether affine adaptation uses accurate smoothing.
3283  ** @param self object.
3284  ** @return @c true if accurate smoothing is used.
3285  **/
3286 
3287 vl_bool
vl_covdet_get_aa_accurate_smoothing(VlCovDet const * self)3288 vl_covdet_get_aa_accurate_smoothing (VlCovDet const * self)
3289 {
3290   return self->aaAccurateSmoothing ;
3291 }
3292 
3293 /** @brief Set whether affine adaptation uses accurate smoothing.
3294  ** @param self object.
3295  ** @param x whether accurate smoothing should be usd.
3296  **/
3297 
3298 void
vl_covdet_set_aa_accurate_smoothing(VlCovDet * self,vl_bool x)3299 vl_covdet_set_aa_accurate_smoothing (VlCovDet * self, vl_bool x)
3300 {
3301   self->aaAccurateSmoothing = x ;
3302 }
3303 
3304 /* ---------------------------------------------------------------- */
3305 /** @brief Get the non-extrema suppression threshold
3306  ** @param self object.
3307  ** @return threshold.
3308  **/
3309 
3310 double
vl_covdet_get_non_extrema_suppression_threshold(VlCovDet const * self)3311 vl_covdet_get_non_extrema_suppression_threshold (VlCovDet const * self)
3312 {
3313   return self->nonExtremaSuppression ;
3314 }
3315 
3316 /** @brief Set the non-extrema suppression threshod
3317  ** @param self object.
3318  ** @param x threshold.
3319  **/
3320 
3321 void
vl_covdet_set_non_extrema_suppression_threshold(VlCovDet * self,double x)3322 vl_covdet_set_non_extrema_suppression_threshold (VlCovDet * self, double x)
3323 {
3324   self->nonExtremaSuppression = x ;
3325 }
3326 
3327 /** @brief Get the number of non-extrema suppressed
3328  ** @param self object.
3329  ** @return number.
3330  **/
3331 
3332 vl_size
vl_covdet_get_num_non_extrema_suppressed(VlCovDet const * self)3333 vl_covdet_get_num_non_extrema_suppressed (VlCovDet const * self)
3334 {
3335   return self->numNonExtremaSuppressed ;
3336 }
3337 
3338 
3339 /* ---------------------------------------------------------------- */
3340 /** @brief Get number of stored frames
3341  ** @return number of frames stored in the detector.
3342  **/
3343 vl_size
vl_covdet_get_num_features(VlCovDet const * self)3344 vl_covdet_get_num_features (VlCovDet const * self)
3345 {
3346   return self->numFeatures ;
3347 }
3348 
3349 /** @brief Get the stored frames
3350  ** @return frames stored in the detector.
3351  **/
3352 VlCovDetFeature *
vl_covdet_get_features(VlCovDet * self)3353 vl_covdet_get_features (VlCovDet * self)
3354 {
3355   return self->features ;
3356 }
3357 
3358 /** @brief Get the Gaussian scale space
3359  ** @return Gaussian scale space.
3360  **
3361  ** A Gaussian scale space exists only after calling ::vl_covdet_put_image.
3362  ** Otherwise the function returns @c NULL.
3363  **/
3364 
3365 VlScaleSpace *
vl_covdet_get_gss(VlCovDet const * self)3366 vl_covdet_get_gss (VlCovDet const * self)
3367 {
3368   return self->gss ;
3369 }
3370 
3371 /** @brief Get the cornerness measure scale space
3372  ** @return cornerness measure scale space.
3373  **
3374  ** A cornerness measure scale space exists only after calling
3375  ** ::vl_covdet_detect. Otherwise the function returns @c NULL.
3376  **/
3377 
3378 VlScaleSpace *
vl_covdet_get_css(VlCovDet const * self)3379 vl_covdet_get_css (VlCovDet const * self)
3380 {
3381   return self->css ;
3382 }
3383 
3384 /** @brief Get the number of features found with a certain number of scales
3385  ** @param self object.
3386  ** @param numScales length of the histogram (out).
3387  ** @return histogram.
3388  **
3389  ** Calling this function makes sense only after running a detector
3390  ** that uses the Laplacian as a secondary measure for scale
3391  ** detection
3392  **/
3393 
3394 vl_size const *
vl_covdet_get_laplacian_scales_statistics(VlCovDet const * self,vl_size * numScales)3395 vl_covdet_get_laplacian_scales_statistics (VlCovDet const * self,
3396                                            vl_size * numScales)
3397 {
3398   *numScales = VL_COVDET_MAX_NUM_LAPLACIAN_SCALES ;
3399   return self->numFeaturesWithNumScales ;
3400 }
3401